cx_attr_unused CxArrayReallocator *alloc
) {
size_t n;
+ // LCOV_EXCL_START
if (cx_szmul(new_capacity, elem_size, &n)) {
errno = EOVERFLOW;
return NULL;
- }
+ } // LCOV_EXCL_STOP
return cxReallocDefault(array, n);
}
) {
// check for overflow
size_t n;
+ // LCOV_EXCL_START
if (cx_szmul(new_capacity, elem_size, &n)) {
errno = EOVERFLOW;
return NULL;
- }
+ } // LCOV_EXCL_STOP
// retrieve the pointer to the actual allocator
const CxAllocator *al = alloc->allocator;
*
* @param current_capacity the current capacity of the array
* @param needed_capacity the required capacity of the array
- * @param maximum_capacity the maximum capacity (given by the data type)
* @return the new capacity
*/
static size_t cx_array_grow_capacity(
size_t current_capacity,
- size_t needed_capacity,
- size_t maximum_capacity
+ size_t needed_capacity
) {
if (current_capacity >= needed_capacity) {
return current_capacity;
else if (cap < 1024) alignment = 64;
else if (cap < 8192) alignment = 512;
else alignment = 1024;
-
- if (cap - 1 > maximum_capacity - alignment) {
- return maximum_capacity;
- } else {
- return cap - (cap % alignment) + alignment;
- }
+ return cap - (cap % alignment) + alignment;
}
int cx_array_reserve(
const size_t newsize = oldsize < minsize ? minsize : oldsize;
// reallocate if necessary
- const size_t newcap = cx_array_grow_capacity(oldcap, newsize, max_size);
+ const size_t newcap = cx_array_grow_capacity(oldcap, newsize);
if (newcap > oldcap) {
// check if we need to repair the src pointer
uintptr_t targetaddr = (uintptr_t) *target;
if (elem_count == 0) return 0;
// overflow check
+ // LCOV_EXCL_START
if (elem_count > SIZE_MAX - *size) {
errno = EOVERFLOW;
return 1;
}
+ // LCOV_EXCL_STOP
// store some counts
const size_t old_size = *size;
const size_t old_capacity = *capacity;
// the necessary capacity is the worst case assumption, including duplicates
- const size_t needed_capacity = cx_array_grow_capacity(old_capacity,
- old_size + elem_count, SIZE_MAX);
+ const size_t needed_capacity = cx_array_grow_capacity(old_capacity, old_size + elem_count);
// if we need more than we have, try a reallocation
if (needed_capacity > old_capacity) {
// while there are both source and buffered elements left,
// copy them interleaving
while (si < elem_count && bi < new_size) {
- // determine how many source elements can be inserted
+ // determine how many source elements can be inserted.
+ // the first element that shall not be inserted is the smallest element
+ // that is strictly larger than the first buffered element
+ // (located at the index of the infimum plus one).
+ // the infimum is guaranteed to exist:
+ // - if all src elements are larger,
+ // there is no buffer, and this loop is skipped
+ // - if any src element is smaller or equal, the infimum exists
+ // - when all src elements that are smaller are copied, the second part
+ // of this loop body will copy the remaining buffer (emptying it)
+ // Therefore, the buffer can never contain an element that is smaller
+ // than any element in the source and the infimum exists.
size_t copy_len, bytes_copied;
- copy_len = cx_array_binary_search_sup(
- src,
- elem_count - si,
- elem_size,
- bptr,
- cmp_func
+ copy_len = cx_array_binary_search_inf(
+ src, elem_count - si, elem_size, bptr, cmp_func
);
- // binary search gives us the smallest index;
- // we also want to include equal elements here
- while (si + copy_len < elem_count
- && cmp_func(bptr, src+copy_len*elem_size) == 0) {
- copy_len++;
- }
+ copy_len++;
// copy the source elements
if (copy_len > 0) {
// duplicates allowed or nothing inserted yet: simply copy everything
memcpy(dest, src, elem_size * (elem_count - si));
} else {
- if (dest != *target) {
- // skip all source elements that equal the last element
- char *last = dest - elem_size;
- while (si < elem_count) {
- if (last != NULL && cmp_func(last, src) == 0) {
- src += elem_size;
- si++;
- (*size)--;
- } else {
- break;
- }
- }
- }
- // we must check the elements in the chunk one by one
+ // we must check the remaining source elements one by one
+ // to skip the duplicates.
+ // Note that no source element can equal the last element in the
+ // destination, because that would have created an insertion point
+ // and a buffer, s.t. the above loop already handled the duplicates
while (si < elem_count) {
// find a chain of elements that can be copied
size_t copy_len = 1, skip_len = 0;
{
const char *left_src = src;
- while (si + copy_len < elem_count) {
+ while (si + copy_len + skip_len < elem_count) {
const char *right_src = left_src + elem_size;
int d = cmp_func(left_src, right_src);
if (d < 0) {
cmp_func, sorted_data, elem_size, elem_count, reallocator, false);
}
-size_t cx_array_binary_search_inf(
+// implementation that finds ANY index
+static size_t cx_array_binary_search_inf_impl(
const void *arr,
size_t size,
size_t elem_size,
result = cmp_func(elem, arr_elem);
if (result == 0) {
// found it!
- // check previous elements;
- // when they are equal, report the smallest index
- arr_elem -= elem_size;
- while (pivot_index > 0 && cmp_func(elem, arr_elem) == 0) {
- pivot_index--;
- arr_elem -= elem_size;
- }
return pivot_index;
} else if (result < 0) {
// element is smaller than pivot, continue search left
return result < 0 ? (pivot_index - 1) : pivot_index;
}
+size_t cx_array_binary_search_inf(
+ const void *arr,
+ size_t size,
+ size_t elem_size,
+ const void *elem,
+ cx_compare_func cmp_func
+) {
+ size_t index = cx_array_binary_search_inf_impl(
+ arr, size, elem_size, elem, cmp_func);
+ // in case of equality, report the largest index
+ const char *e = ((const char *) arr) + (index + 1) * elem_size;
+ while (index + 1 < size && cmp_func(e, elem) == 0) {
+ e += elem_size;
+ index++;
+ }
+ return index;
+}
+
size_t cx_array_binary_search(
const void *arr,
size_t size,
const void *elem,
cx_compare_func cmp_func
) {
- size_t inf = cx_array_binary_search_inf(
+ size_t index = cx_array_binary_search_inf_impl(
arr, size, elem_size, elem, cmp_func
);
- if (inf == size) {
- // no infimum means, first element is supremum
+ const char *e = ((const char *) arr) + index * elem_size;
+ if (index == size) {
+ // no infimum means the first element is supremum
return 0;
- } else if (cmp_func(((const char *) arr) + inf * elem_size, elem) == 0) {
- return inf;
+ } else if (cmp_func(e, elem) == 0) {
+ // found an equal element, search the smallest index
+ e -= elem_size; // e now contains the element at index-1
+ while (index > 0 && cmp_func(e, elem) == 0) {
+ e -= elem_size;
+ index--;
+ }
+ return index;
} else {
- return inf + 1;
+ // we already have the largest index of the infimum (by design)
+ // the next element is the supremum (or there is no supremum)
+ return index + 1;
}
}
// guarantee enough capacity
if (arl->capacity < list->collection.size + n) {
- const size_t new_capacity = cx_array_grow_capacity(arl->capacity,
- list->collection.size + n, SIZE_MAX);
+ const size_t new_capacity = cx_array_grow_capacity(arl->capacity,list->collection.size + n);
if (cxReallocateArray(
list->collection.allocator,
&arl->data, new_capacity,
list->collection.elem_size)
) {
- return 0;
+ return 0; // LCOV_EXCL_LINE
}
arl->capacity = new_capacity;
}
&arl->reallocator
)) {
// array list implementation is "all or nothing"
- return 0;
+ return 0; // LCOV_EXCL_LINE
} else {
return n;
}
&arl->reallocator
)) {
// array list implementation is "all or nothing"
- return 0;
+ return 0; // LCOV_EXCL_LINE
} else {
return n;
}
if (iter->index < list->collection.size) {
if (cx_arl_insert_element(list,
iter->index + 1 - prepend, elem) == NULL) {
- return 1;
+ return 1; // LCOV_EXCL_LINE
}
iter->elem_count++;
if (prepend != 0) {
return 0;
} else {
if (cx_arl_insert_element(list, list->collection.size, elem) == NULL) {
- return 1;
+ return 1; // LCOV_EXCL_LINE
}
iter->elem_count++;
iter->index = list->collection.size;
#ifdef _WIN32
#include <Windows.h>
#include <sysinfoapi.h>
-static unsigned long system_page_size() {
+static unsigned long system_page_size(void) {
static unsigned long ps = 0;
if (ps == 0) {
SYSTEM_INFO sysinfo;
}
return ps;
}
-#define SYSTEM_PAGE_SIZE system_page_size()
#else
#include <unistd.h>
-#define SYSTEM_PAGE_SIZE sysconf(_SC_PAGESIZE)
+static unsigned long system_page_size(void) {
+ static unsigned long ps = 0;
+ if (ps == 0) {
+ long sc = sysconf(_SC_PAGESIZE);
+ if (sc < 0) {
+ // fallback for systems which do not report a value here
+ ps = 4096; // LCOV_EXCL_LINE
+ } else {
+ ps = (unsigned long) sc;
+ }
+ }
+ return ps;
+}
#endif
static int buffer_copy_on_write(CxBuffer* buffer) {
if (0 == (buffer->flags & CX_BUFFER_COPY_ON_WRITE)) return 0;
void *newspace = cxMalloc(buffer->allocator, buffer->capacity);
- if (NULL == newspace) return -1;
+ if (NULL == newspace) return -1; // LCOV_EXCL_LINE
memcpy(newspace, buffer->space, buffer->size);
buffer->space = newspace;
buffer->flags &= ~CX_BUFFER_COPY_ON_WRITE;
buffer->flags = flags;
if (!space) {
buffer->bytes = cxMalloc(allocator, capacity);
- if (buffer->bytes == NULL) {
- return -1; // LCOV_EXCL_LINE
- }
+ if (buffer->bytes == NULL) return -1; // LCOV_EXCL_LINE
buffer->flags |= CX_BUFFER_FREE_CONTENTS;
} else {
buffer->bytes = space;
allocator = cxDefaultAllocator;
}
CxBuffer *buf = cxMalloc(allocator, sizeof(CxBuffer));
- if (buf == NULL) return NULL;
+ if (buf == NULL) return NULL; // LCOV_EXCL_LINE
if (0 == cxBufferInit(buf, space, capacity, allocator, flags)) {
return buf;
} else {
}
+size_t cxBufferPop(CxBuffer *buffer, size_t size, size_t nitems) {
+ size_t len;
+ if (cx_szmul(size, nitems, &len)) {
+ // LCOV_EXCL_START
+ errno = EOVERFLOW;
+ return 0;
+ // LCOV_EXCL_STOP
+ }
+ if (len == 0) return 0;
+ if (len > buffer->size) {
+ if (size == 1) {
+ // simple case: everything can be discarded
+ len = buffer->size;
+ } else {
+ // complicated case: misaligned bytes must stay
+ size_t misalignment = buffer->size % size;
+ len = buffer->size - misalignment;
+ }
+ }
+ buffer->size -= len;
+
+ // adjust position, if required
+ if (buffer->pos > buffer->size) {
+ buffer->pos = buffer->size;
+ }
+
+ return len / size;
+}
+
void cxBufferClear(CxBuffer *buffer) {
if (0 == (buffer->flags & CX_BUFFER_COPY_ON_WRITE)) {
memset(buffer->bytes, 0, buffer->size);
return buffer->pos >= buffer->size;
}
-int cxBufferMinimumCapacity(
- CxBuffer *buffer,
- size_t newcap
-) {
+int cxBufferReserve(CxBuffer *buffer, size_t newcap) {
if (newcap <= buffer->capacity) {
return 0;
}
-
- unsigned long pagesize = SYSTEM_PAGE_SIZE;
- // if page size is larger than 64 KB - for some reason - truncate to 64 KB
- if (pagesize > 65536) pagesize = 65536;
- if (newcap < pagesize) {
- // when smaller as one page, map to the next power of two
- newcap--;
- newcap |= newcap >> 1;
- newcap |= newcap >> 2;
- newcap |= newcap >> 4;
- // last operation only needed for pages larger 4096 bytes
- // but if/else would be more expensive than just doing this
- newcap |= newcap >> 8;
- newcap++;
- } else {
- // otherwise, map to a multiple of the page size
- newcap -= newcap % pagesize;
- newcap += pagesize;
- // note: if newcap is already page aligned,
- // this gives a full additional page (which is good)
- }
-
-
const int force_copy_flags = CX_BUFFER_COPY_ON_WRITE | CX_BUFFER_COPY_ON_EXTEND;
if (buffer->flags & force_copy_flags) {
void *newspace = cxMalloc(buffer->allocator, newcap);
}
}
+static size_t cx_buffer_calculate_minimum_capacity(size_t mincap) {
+ unsigned long pagesize = system_page_size();
+ // if page size is larger than 64 KB - for some reason - truncate to 64 KB
+ if (pagesize > 65536) pagesize = 65536;
+ if (mincap < pagesize) {
+ // when smaller as one page, map to the next power of two
+ mincap--;
+ mincap |= mincap >> 1;
+ mincap |= mincap >> 2;
+ mincap |= mincap >> 4;
+ // last operation only needed for pages larger 4096 bytes
+ // but if/else would be more expensive than just doing this
+ mincap |= mincap >> 8;
+ mincap++;
+ } else {
+ // otherwise, map to a multiple of the page size
+ mincap -= mincap % pagesize;
+ mincap += pagesize;
+ // note: if newcap is already page aligned,
+ // this gives a full additional page (which is good)
+ }
+ return mincap;
+}
+
+int cxBufferMinimumCapacity(CxBuffer *buffer, size_t newcap) {
+ if (newcap <= buffer->capacity) {
+ return 0;
+ }
+ newcap = cx_buffer_calculate_minimum_capacity(newcap);
+ return cxBufferReserve(buffer, newcap);
+}
+
void cxBufferShrink(
CxBuffer *buffer,
size_t reserve
bool perform_flush = false;
if (required > buffer->capacity) {
if (buffer->flags & CX_BUFFER_AUTO_EXTEND) {
- if (buffer->flush != NULL && required > buffer->flush->threshold) {
- perform_flush = true;
+ if (buffer->flush != NULL) {
+ size_t newcap = cx_buffer_calculate_minimum_capacity(required);
+ if (newcap > buffer->flush->threshold) {
+ newcap = buffer->flush->threshold;
+ }
+ if (cxBufferReserve(buffer, newcap)) {
+ return total_flushed; // LCOV_EXCL_LINE
+ }
+ if (required > newcap) {
+ perform_flush = true;
+ }
} else {
if (cxBufferMinimumCapacity(buffer, required)) {
return total_flushed; // LCOV_EXCL_LINE
* in @p arr that is less or equal to @p elem with respect to @p cmp_func.
* When no such element exists, @p size is returned.
*
+ * When such an element exists more than once, the largest index of all those
+ * elements is returned.
+ *
* If @p elem is contained in the array, this is identical to
* #cx_array_binary_search().
*
/**
* Searches an item in a sorted array.
*
+ * When such an element exists more than once, the largest index of all those
+ * elements is returned.
+ *
* If the array is not sorted with respect to the @p cmp_func, the behavior
* is undefined.
*
* in @p arr that is greater or equal to @p elem with respect to @p cmp_func.
* When no such element exists, @p size is returned.
*
+ * When such an element exists more than once, the smallest index of all those
+ * elements is returned.
+ *
* If @p elem is contained in the array, this is identical to
* #cx_array_binary_search().
*
/**
* The maximum number of blocks to flush in one cycle.
*
- * @attention while it is guaranteed that cxBufferFlush() will not flush
+ * @attention While it is guaranteed that cxBufferFlush() will not flush
* more blocks, this is not necessarily the case for cxBufferWrite().
* After performing a flush cycle, cxBufferWrite() will retry the write
* operation and potentially trigger another flush cycle, until the
cx_attr_nonnull
CX_EXPORT int cxBufferSeek(CxBuffer *buffer, off_t offset, int whence);
+/**
+ * Discards items from the end of the buffer.
+ *
+ * When the current position points to a byte that gets discarded,
+ * the position is set to the buffer size.
+ *
+ * @param buffer the buffer
+ * @param size the size of one item
+ * @param nitems the number of items to discard
+ * @return the actual number of discarded items
+ */
+cx_attr_nonnull
+CX_EXPORT size_t cxBufferPop(CxBuffer *buffer, size_t size, size_t nitems);
+
/**
* Clears the buffer by resetting the position and deleting the data.
*
cx_attr_nonnull cx_attr_nodiscard
CX_EXPORT bool cxBufferEof(const CxBuffer *buffer);
+/**
+ * Ensures that the buffer has the required capacity.
+ *
+ * If the current capacity is not sufficient, the buffer will be extended.
+ *
+ * This function will reserve no more bytes than requested, in contrast to
+ * cxBufferMinimumCapacity(), which may reserve more bytes to improve the
+ * number of future necessary reallocations.
+ *
+ * @param buffer the buffer
+ * @param capacity the required capacity for this buffer
+ * @retval zero the capacity was already sufficient or successfully increased
+ * @retval non-zero on allocation failure
+ * @see cxBufferShrink()
+ * @see cxBufferMinimumCapacity()
+ */
+cx_attr_nonnull
+CX_EXPORT int cxBufferReserve(CxBuffer *buffer, size_t capacity);
/**
* Ensures that the buffer has a minimum capacity.
*
- * If the current capacity is not sufficient, the buffer will be extended.
+ * If the current capacity is not sufficient, the buffer will be generously
+ * extended.
*
* The new capacity will be a power of two until the system's page size is reached.
* Then, the new capacity will be a multiple of the page size.
* @param capacity the minimum required capacity for this buffer
* @retval zero the capacity was already sufficient or successfully increased
* @retval non-zero on allocation failure
+ * @see cxBufferReserve()
* @see cxBufferShrink()
*/
cx_attr_nonnull
*
* @param buffer the buffer
* @param reserve the number of bytes that shall remain reserved
+ * @see cxBufferReserve()
* @see cxBufferMinimumCapacity()
*/
cx_attr_nonnull
*/
#define CX_INLINE __attribute__((always_inline)) static inline
#else
+/**
+ * Declares a function to be inlined.
+ */
#define CX_INLINE static inline
#endif
/**
* @retval non-zero internal allocation error
* @see cxJsonFill()
*/
-cx_attr_nonnull cx_attr_access_r(2, 3)
+cx_attr_nonnull_arg(1) cx_attr_access_r(2, 3)
CX_EXPORT int cxJsonFilln(CxJson *json, const char *buf, size_t len);
/**
* Creates a new JSON string.
*
+ * Internal function - use cxJsonCreateString() instead.
+ *
* @param allocator the allocator to use
* @param str the string data
* @return the new JSON value or @c NULL if allocation fails
- * @see cxJsonCreateString()
* @see cxJsonObjPutString()
- * @see cxJsonArrAddStrings()
+ * @see cxJsonArrAddCxStrings()
*/
-cx_attr_nodiscard cx_attr_nonnull_arg(2) cx_attr_cstr_arg(2)
-CX_EXPORT CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, const char *str);
+cx_attr_nodiscard
+CX_EXPORT CxJsonValue* cx_json_create_string(const CxAllocator* allocator, cxstring str);
/**
* Creates a new JSON string.
*
- * @param allocator the allocator to use
- * @param str the string data
- * @return the new JSON value or @c NULL if allocation fails
- * @see cxJsonCreateCxString()
- * @see cxJsonObjPutCxString()
+ * @param allocator (@c CxAllocator*) the allocator to use
+ * @param str the string
+ * @return (@c CxJsonValue*) the new JSON value or @c NULL if allocation fails
+ * @see cxJsonObjPutString()
* @see cxJsonArrAddCxStrings()
*/
-cx_attr_nodiscard
-CX_EXPORT CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str);
+#define cxJsonCreateString(allocator, str) cx_json_create_string(allocator, cx_strcast(str))
/**
* Creates a new JSON literal.
/**
* Adds or replaces a value within a JSON object.
*
- * The value will be directly added and not copied.
- *
- * @note If a value with the specified @p name already exists,
- * it will be (recursively) freed with its own allocator.
+ * Internal function - use cxJsonObjPut().
*
* @param obj the JSON object
* @param name the name of the value
* @retval non-zero allocation failure
*/
cx_attr_nonnull
-CX_EXPORT int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child);
+CX_EXPORT int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* child);
+
+/**
+ * Adds or replaces a value within a JSON object.
+ *
+ * The value will be directly added and not copied.
+ *
+ * @note If a value with the specified @p name already exists,
+ * it will be (recursively) freed with its own allocator.
+ *
+ * @param obj (@c CxJsonValue*) the JSON object
+ * @param name (any string) the name of the value
+ * @param child (@c CxJsonValue*) the value
+ * @retval zero success
+ * @retval non-zero allocation failure
+ */
+#define cxJsonObjPut(obj, name, child) cx_json_obj_put(obj, cx_strcast(name), child)
/**
* Creates a new JSON object and adds it to an existing object.
*
+ * Internal function - use cxJsonObjPutObj().
+ *
* @param obj the target JSON object
* @param name the name of the new value
* @return the new value or @c NULL if allocation fails
* @see cxJsonCreateObj()
*/
cx_attr_nonnull
-CX_EXPORT CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name);
+CX_EXPORT CxJsonValue* cx_json_obj_put_obj(CxJsonValue* obj, cxstring name);
+
+/**
+ * Creates a new JSON object and adds it to an existing object.
+ *
+ * @param obj (@c CxJsonValue*) the target JSON object
+ * @param name (any string) the name of the new value
+ * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails
+ * @see cxJsonObjPut()
+ * @see cxJsonCreateObj()
+ */
+#define cxJsonObjPutObj(obj, name) cx_json_obj_put_obj(obj, cx_strcast(name))
/**
* Creates a new JSON array and adds it to an object.
*
+ * Internal function - use cxJsonObjPutArr().
+ *
* @param obj the target JSON object
* @param name the name of the new value
* @return the new value or @c NULL if allocation fails
* @see cxJsonCreateArr()
*/
cx_attr_nonnull
-CX_EXPORT CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, cxstring name);
+CX_EXPORT CxJsonValue* cx_json_obj_put_arr(CxJsonValue* obj, cxstring name);
+
+/**
+ * Creates a new JSON array and adds it to an object.
+ *
+ * @param obj (@c CxJsonValue*) the target JSON object
+ * @param name (any string) the name of the new value
+ * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails
+ * @see cxJsonObjPut()
+ * @see cxJsonCreateArr()
+ */
+#define cxJsonObjPutArr(obj, name) cx_json_obj_put_arr(obj, cx_strcast(name))
/**
* Creates a new JSON number and adds it to an object.
*
+ * Internal function - use cxJsonObjPutNumber().
+ *
* @param obj the target JSON object
* @param name the name of the new value
* @param num the numeric value
* @see cxJsonCreateNumber()
*/
cx_attr_nonnull
-CX_EXPORT CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, cxstring name, double num);
+CX_EXPORT CxJsonValue* cx_json_obj_put_number(CxJsonValue* obj, cxstring name, double num);
+
+/**
+ * Creates a new JSON number and adds it to an object.
+ *
+ * @param obj (@c CxJsonValue*) the target JSON object
+ * @param name (any string) the name of the new value
+ * @param num (@c double) the numeric value
+ * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails
+ * @see cxJsonObjPut()
+ * @see cxJsonCreateNumber()
+ */
+#define cxJsonObjPutNumber(obj, name, num) cx_json_obj_put_number(obj, cx_strcast(name), num)
/**
* Creates a new JSON number, based on an integer, and adds it to an object.
*
+ * Internal function - use cxJsonObjPutInteger().
+ *
* @param obj the target JSON object
* @param name the name of the new value
* @param num the numeric value
* @see cxJsonCreateInteger()
*/
cx_attr_nonnull
-CX_EXPORT CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, cxstring name, int64_t num);
+CX_EXPORT CxJsonValue* cx_json_obj_put_integer(CxJsonValue* obj, cxstring name, int64_t num);
+
+/**
+ * Creates a new JSON number, based on an integer, and adds it to an object.
+ *
+ * @param obj (@c CxJsonValue*) the target JSON object
+ * @param name (any string) the name of the new value
+ * @param num (@c int64_t) the numeric value
+ * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails
+ * @see cxJsonObjPut()
+ * @see cxJsonCreateInteger()
+ */
+#define cxJsonObjPutInteger(obj, name, num) cx_json_obj_put_integer(obj, cx_strcast(name), num)
/**
* Creates a new JSON string and adds it to an object.
*
- * The string data is copied.
+ * Internal function - use cxJsonObjPutString()
*
* @param obj the target JSON object
* @param name the name of the new value
* @see cxJsonObjPut()
* @see cxJsonCreateString()
*/
-cx_attr_nonnull cx_attr_cstr_arg(3)
-CX_EXPORT CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, cxstring name, const char* str);
+cx_attr_nonnull
+CX_EXPORT CxJsonValue* cx_json_obj_put_string(CxJsonValue* obj, cxstring name, cxstring str);
/**
* Creates a new JSON string and adds it to an object.
*
* The string data is copied.
*
- * @param obj the target JSON object
- * @param name the name of the new value
- * @param str the string data
- * @return the new value or @c NULL if allocation fails
+ * @param obj (@c CxJsonValue*) the target JSON object
+ * @param name (any string) the name of the new value
+ * @param str (any string) the string data
+ * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails
* @see cxJsonObjPut()
- * @see cxJsonCreateCxString()
+ * @see cxJsonCreateString()
*/
-cx_attr_nonnull
-CX_EXPORT CxJsonValue* cxJsonObjPutCxString(CxJsonValue* obj, cxstring name, cxstring str);
+#define cxJsonObjPutString(obj, name, str) cx_json_obj_put_string(obj, cx_strcast(name), cx_strcast(str))
/**
* Creates a new JSON literal and adds it to an object.
*
+ * Internal function - use cxJsonObjPutLiteral().
+ *
* @param obj the target JSON object
* @param name the name of the new value
* @param lit the type of literal
* @see cxJsonCreateLiteral()
*/
cx_attr_nonnull
-CX_EXPORT CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, cxstring name, CxJsonLiteral lit);
+CX_EXPORT CxJsonValue* cx_json_obj_put_literal(CxJsonValue* obj, cxstring name, CxJsonLiteral lit);
+
+/**
+ * Creates a new JSON literal and adds it to an object.
+ *
+ * @param obj (@c CxJsonValue*) the target JSON object
+ * @param name (any string) the name of the new value
+ * @param lit (@c CxJsonLiteral) the type of literal
+ * @return (@c CxJsonValue*) the new value or @c NULL if allocation fails
+ * @see cxJsonObjPut()
+ * @see cxJsonCreateLiteral()
+ */
+#define cxJsonObjPutLiteral(obj, name, lit) cx_json_obj_put_literal(obj, cx_strcast(name), lit)
/**
* Recursively deallocates the memory of a JSON value.
cx_attr_nonnull cx_attr_nodiscard
CX_EXPORT CxIterator cxJsonArrIter(const CxJsonValue *value);
+/**
+ * Returns the size of a JSON object.
+ *
+ * If the @p value is not a JSON object, the behavior is undefined.
+ *
+ * @param value the JSON value
+ * @return the size of the object, i.e., the number of key/value pairs
+ * @see cxJsonIsObject()
+ */
+cx_attr_nonnull
+CX_INLINE size_t cxJsonObjSize(const CxJsonValue *value) {
+ return value->value.object.values_size;
+}
+
/**
* Returns an iterator over the JSON object members.
*
* Location of the payload (mandatory).
*/
off_t loc_data;
+ /**
+ * Location of extra data (optional).
+ * Negative when no extra data is requested.
+ * @see cx_linked_list_extra_data()
+ */
+ off_t loc_extra;
/**
* Additional bytes to allocate @em behind the payload (e.g. for metadata).
+ * @see cx_linked_list_extra_data()
*/
size_t extra_data_len;
/**
#define cxLinkedListCreateSimple(elem_size) \
cxLinkedListCreate(NULL, NULL, elem_size)
+/**
+ * Instructs the linked list to reserve extra data in each node.
+ *
+ * The extra data will be aligned and placed behind the element data.
+ * The exact location in the node is stored in the @c loc_extra field
+ * of the linked list.
+ *
+ * You should usually not use this function except when you are creating an
+ * own linked-list implementation that is based on the UCX linked list and
+ * needs to store extra data in each node.
+ *
+ * @param list the list (must be a linked list)
+ * @param len the length of the extra data
+ */
+cx_attr_nonnull
+CX_EXPORT void cx_linked_list_extra_data(cx_linked_list *list, size_t len);
+
/**
* Finds the node at a certain index.
*
ptrdiff_t loc_next;
/**
* The total number of distinct nodes that have been passed so far.
+ * This includes the current node.
*/
size_t counter;
/**
ptrdiff_t loc_next;
/**
* The total number of distinct nodes that have been passed so far.
+ * This includes the currently visited node.
*/
size_t counter;
/**
* @see cxTreeIterate()
*/
cx_attr_nonnull cx_attr_nodiscard
-CxTreeVisitor cxTreeVisit(CxTree *tree);
+CX_EXPORT CxTreeVisitor cxTreeVisit(CxTree *tree);
/**
* Sets the (new) parent of the specified child.
allocator,
sizeof(struct cx_hash_map_element_s) + map->collection.elem_size
);
- if (e == NULL) return NULL;
+ if (e == NULL) return NULL; // LCOV_EXCL_LINE
// write the value
if (value == NULL) {
// copy the key
void *kd = cxMalloc(allocator, key.len);
- if (kd == NULL) {
+ if (kd == NULL) { // LCOV_EXCL_START
cxFree(allocator, e);
return NULL;
- }
+ } // LCOV_EXCL_STOP
memcpy(kd, key.data, key.len);
e->key.data = kd;
e->key.len = key.len;
size_t new_bucket_count = (map->collection.size * 5) >> 1;
if (new_bucket_count < hash_map->bucket_count) {
+ // LCOV_EXCL_START
errno = EOVERFLOW;
return 1;
- }
+ } // LCOV_EXCL_STOP
struct cx_hash_map_element_s **new_buckets = cxCalloc(
map->collection.allocator,
new_bucket_count, sizeof(struct cx_hash_map_element_s *)
);
- if (new_buckets == NULL) return 1;
+ if (new_buckets == NULL) return 1; // LCOV_EXCL_LINE
// iterate through the elements and assign them to their new slots
for (size_t slot = 0; slot < hash_map->bucket_count; slot++) {
size_t newcap = obj->values_capacity;
if (newcap > oldcap) {
if (cxReallocateArray(al, &obj->indices, newcap, sizeof(size_t))) {
- return 1;
+ return 1; // LCOV_EXCL_LINE
}
}
} else if (c == 'u') {
char utf8buf[4];
unsigned utf8len = unescape_unicode_string(
- cx_strn(str.ptr + i - 1, str.length + 1 - i),
+ cx_strn(str.ptr + i - 1, str.length - i),
utf8buf
);
if(utf8len > 0) {
} else {
// TODO: discuss the behavior for unrecognized escape sequences
// most parsers throw an error here - we just ignore it
- result.ptr[result.length++] = '\\';
+ result.ptr[result.length++] = '\\'; // LCOV_EXCL_LINE
}
result.ptr[result.length++] = c;
if (cxBufferEof(&json->buffer)) {
// reinitialize the buffer
cxBufferDestroy(&json->buffer);
+ if (buf == NULL) buf = ""; // buffer must not be initialized with NULL
cxBufferInit(&json->buffer, (char*) buf, size,
NULL, CX_BUFFER_AUTO_EXTEND | CX_BUFFER_COPY_ON_WRITE);
json->buffer.size = size;
}
} else {
if (cx_strtod(token.content, &vbuf->value.number)) {
- return_rec(CX_JSON_FORMAT_ERROR_NUMBER);
+ // TODO: at the moment this is unreachable, because the tokenizer is already stricter than cx_strtod()
+ return_rec(CX_JSON_FORMAT_ERROR_NUMBER); // LCOV_EXCL_LINE
}
}
return_rec(CX_JSON_NO_ERROR);
} else {
// should be unreachable
assert(false);
- return_rec(-1);
+ return_rec(-1); // LCOV_EXCL_LINE
}
}
CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value) {
- // check if buffer has been filled
+ // initialize output value
+ *value = &cx_json_value_nothing;
+
+ // check if the buffer has been filled
if (json->buffer.space == NULL) {
return CX_JSON_NULL_DATA;
}
- // initialize output value
- *value = &cx_json_value_nothing;
-
// parse data
CxJsonStatus result;
do {
return v;
}
-CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, const char* str) {
- return cxJsonCreateCxString(allocator, cx_str(str));
-}
-
-CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str) {
+CxJsonValue* cx_json_create_string(const CxAllocator* allocator, cxstring str) {
if (allocator == NULL) allocator = cxDefaultAllocator;
CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue));
if (v == NULL) return NULL;
CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*));
if (values == NULL) return -1;
for (size_t i = 0; i < count; i++) {
- values[i] = cxJsonCreateCxString(arr->allocator, str[i]);
+ values[i] = cxJsonCreateString(arr->allocator, str[i]);
if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; }
}
int ret = cxJsonArrAddValues(arr, values, count);
);
}
-int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child) {
+int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* child) {
cxmutstr k = cx_strdup_a(obj->allocator, name);
if (k.ptr == NULL) return -1;
CxJsonObjValue kv = {k, child};
if (json_add_objvalue(obj, kv)) {
+ // LCOV_EXCL_START
cx_strfree_a(obj->allocator, &k);
return 1;
+ // LCOV_EXCL_STOP
} else {
return 0;
}
}
-CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name) {
+CxJsonValue* cx_json_obj_put_obj(CxJsonValue* obj, cxstring name) {
CxJsonValue* v = cxJsonCreateObj(obj->allocator);
if (v == NULL) return NULL;
if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
return v;
}
-CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, cxstring name) {
+CxJsonValue* cx_json_obj_put_arr(CxJsonValue* obj, cxstring name) {
CxJsonValue* v = cxJsonCreateArr(obj->allocator);
if (v == NULL) return NULL;
if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
return v;
}
-CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, cxstring name, double num) {
+CxJsonValue* cx_json_obj_put_number(CxJsonValue* obj, cxstring name, double num) {
CxJsonValue* v = cxJsonCreateNumber(obj->allocator, num);
if (v == NULL) return NULL;
if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
return v;
}
-CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, cxstring name, int64_t num) {
+CxJsonValue* cx_json_obj_put_integer(CxJsonValue* obj, cxstring name, int64_t num) {
CxJsonValue* v = cxJsonCreateInteger(obj->allocator, num);
if (v == NULL) return NULL;
if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
return v;
}
-CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, cxstring name, const char* str) {
+CxJsonValue* cx_json_obj_put_string(CxJsonValue* obj, cxstring name, cxstring str) {
CxJsonValue* v = cxJsonCreateString(obj->allocator, str);
if (v == NULL) return NULL;
if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
return v;
}
-CxJsonValue* cxJsonObjPutCxString(CxJsonValue* obj, cxstring name, cxstring str) {
- CxJsonValue* v = cxJsonCreateCxString(obj->allocator, str);
- if (v == NULL) return NULL;
- if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
- return v;
-}
-
-CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, cxstring name, CxJsonLiteral lit) {
+CxJsonValue* cx_json_obj_put_literal(CxJsonValue* obj, cxstring name, CxJsonLiteral lit) {
CxJsonValue* v = cxJsonCreateLiteral(obj->allocator, lit);
if (v == NULL) return NULL;
if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL;}
? look_idx
: value->value.object.indices[look_idx];
CxJsonObjValue *member = &value->value.object.values[elem_idx];
- if (settings->sort_members) {
- depth++;depth--;
- }
// possible indentation
if (settings->pretty) {
if (cx_json_write_rec(
target, element,
wfunc, settings, depth)
- ) return 1;
+ ) {
+ return 1; // LCOV_EXCL_LINE
+ }
if (iter.index < iter.elem_count - 1) {
const char *arr_value_sep = ", ";
}
static CxHashKey *cx_kv_list_loc_key(cx_kv_list *list, void *node_data) {
- return (CxHashKey*)((char*)node_data + list->list.base.collection.elem_size);
+ return (CxHashKey*)((char*)node_data - list->list.loc_data + list->list.loc_extra);
}
static void cx_kvl_deallocate(struct cx_list_s *list) {
CxList *list = cxLinkedListCreate(allocator, comparator, elem_size);
if (list == NULL) return NULL; // LCOV_EXCL_LINE
cx_linked_list *ll = (cx_linked_list*)list;
- ll->extra_data_len = sizeof(CxHashKey);
+ cx_linked_list_extra_data(ll, sizeof(CxHashKey));
CxMap *map = cxHashMapCreate(allocator, CX_STORE_POINTERS, 0);
if (map == NULL) { // LCOV_EXCL_START
cxListFree(list);
#include <string.h>
#include <assert.h>
+#if __STDC_VERSION__ < 202311L
+// we cannot simply include stdalign.h
+// because Solaris is not entirely C11 complaint
+#ifndef __alignof_is_defined
+#define alignof _Alignof
+#define __alignof_is_defined 1
+#endif
+#endif
+
// LOW LEVEL LINKED LIST FUNCTIONS
#define CX_LL_PTR(cur, off) (*(void**)(((char*)(cur))+(off)))
void *sbo[CX_LINKED_LIST_SORT_SBO_SIZE];
void **sorted = length >= CX_LINKED_LIST_SORT_SBO_SIZE ?
cxMallocDefault(sizeof(void *) * length) : sbo;
- if (sorted == NULL) abort();
+ if (sorted == NULL) abort(); // LCOV_EXCL_LINE
void *rc, *lc;
lc = ls;
}
static void *cx_ll_malloc_node(const cx_linked_list *list) {
- return cxZalloc(list->base.collection.allocator,
- list->loc_data + list->base.collection.elem_size + list->extra_data_len);
+ size_t n;
+ if (list->extra_data_len == 0) {
+ n = list->loc_data + list->base.collection.elem_size;
+ } else {
+ n = list->loc_extra + list->extra_data_len;
+ }
+ return cxZalloc(list->base.collection.allocator, n);
}
static int cx_ll_insert_at(
return result;
} else {
if (cx_ll_insert_element(list, list->collection.size, elem) == NULL) {
- return 1;
+ return 1; // LCOV_EXCL_LINE
}
iter->elem_count++;
iter->index = list->collection.size;
cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list));
if (list == NULL) return NULL;
- list->extra_data_len = 0;
list->loc_prev = 0;
list->loc_next = sizeof(void*);
list->loc_data = sizeof(void*)*2;
+ list->loc_extra = -1;
+ list->extra_data_len = 0;
cx_list_init((CxList*)list, &cx_linked_list_class,
allocator, comparator, elem_size);
return (CxList *) list;
}
+
+void cx_linked_list_extra_data(cx_linked_list *list, size_t len) {
+ list->extra_data_len = len;
+
+ off_t loc_extra = list->loc_data + list->base.collection.elem_size;
+ size_t alignment = alignof(void*);
+ size_t padding = alignment - (loc_extra % alignment);
+ list->loc_extra = loc_extra + padding;
+}
CxIterator src_iter = cxListIterator(src);
CxIterator other_iter = cxListIterator(other);
while (cxIteratorValid(src_iter) || cxIteratorValid(other_iter)) {
- void *src_elem, *other_elem;
+ void *src_elem = NULL, *other_elem = NULL;
int d;
if (!cxIteratorValid(src_iter)) {
other_elem = cxIteratorCurrent(other_iter);
va_copy(ap2, ap);
int ret = vsnprintf(buf, CX_PRINTF_SBO_SIZE, fmt, ap);
if (ret < 0) {
+ // LCOV_EXCL_START
va_end(ap2);
return ret;
+ // LCOV_EXCL_STOP
} else if (ret < CX_PRINTF_SBO_SIZE) {
va_end(ap2);
return (int) wfc(buf, 1, ret, stream);
if (s.ptr) {
ret = vsnprintf(s.ptr, len, fmt, ap2);
if (ret < 0) {
+ // LCOV_EXCL_START
cxFree(a, s.ptr);
s.ptr = NULL;
+ // LCOV_EXCL_STOP
} else {
s.length = (size_t) ret;
}
if (ptr) {
int newret = vsnprintf(ptr, newlen, fmt, ap2);
if (newret < 0) {
- cxFree(alloc, ptr);
+ cxFree(alloc, ptr); // LCOV_EXCL_LINE
} else {
*len = newlen;
*str = ptr;
if (ptr) {
int newret = vsnprintf(ptr, newlen, fmt, ap2);
if (newret < 0) {
- cxFree(alloc, ptr);
+ cxFree(alloc, ptr); // LCOV_EXCL_LINE
} else {
*len = newlen;
*str = ptr;
// still not enough data, copy input buffer to internal buffer
if (cxBufferAppend(input.ptr, 1,
input.length, &prop->buffer) < input.length) {
- return CX_PROPERTIES_BUFFER_ALLOC_FAILED;
+ return CX_PROPERTIES_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE
}
// reset the input buffer (make way for a re-fill)
cxBufferReset(&prop->input);
// initialize reader
if (source.read_init_func != NULL) {
if (source.read_init_func(prop, &source)) {
- return CX_PROPERTIES_READ_INIT_FAILED;
+ return CX_PROPERTIES_READ_INIT_FAILED; // LCOV_EXCL_LINE
}
}
while (true) {
// read input
cxstring input;
- if (source.read_func(prop, &source, &input)) {
+ if (source.read_func(prop, &source, &input)) { // LCOV_EXCL_START
status = CX_PROPERTIES_READ_FAILED;
break;
- }
+ } // LCOV_EXCL_STOP
// no more data - break
if (input.length == 0) {
if (kv_status == CX_PROPERTIES_NO_ERROR) {
found = true;
if (sink.sink_func(prop, &sink, key, value)) {
- kv_status = CX_PROPERTIES_SINK_FAILED;
+ kv_status = CX_PROPERTIES_SINK_FAILED; // LCOV_EXCL_LINE
}
}
} while (kv_status == CX_PROPERTIES_NO_ERROR);
cxstring src
) {
if (cxReallocate(alloc, &dest->ptr, src.length + 1)) {
- return 1;
+ return 1; // LCOV_EXCL_LINE
}
memcpy(dest->ptr, src.ptr, src.length);
size_t slen = str.length;
for (size_t i = 0; i < count; i++) {
cxstring s = va_arg(ap, cxstring);
- if (slen > SIZE_MAX - str.length) overflow = true;
+ if (slen > SIZE_MAX - s.length) overflow = true;
slen += s.length;
}
va_end(ap);
} else {
newstr = cxRealloc(alloc, str.ptr, slen + 1);
}
- if (newstr == NULL) {
+ if (newstr == NULL) { // LCOV_EXCL_START
va_end(ap2);
return (cxmutstr) {NULL, 0};
- }
+ } // LCOV_EXCL_STOP
str.ptr = newstr;
// concatenate strings
cxMalloc(allocator, string.length + 1),
string.length
};
+ // LCOV_EXCL_START
if (result.ptr == NULL) {
result.length = 0;
return result;
}
+ // LCOV_EXCL_STOP
memcpy(result.ptr, string.ptr, string.length);
result.ptr[string.length] = '\0';
return result;
ptrdiff_t loc_next
) {
*cnode = cfunc(src, cdata);
- if (*cnode == NULL) return 1;
+ if (*cnode == NULL) return 1; // LCOV_EXCL_LINE
cx_tree_zero_pointers(*cnode, cx_tree_ptr_locations);
void *match = NULL;
// create the new node
void *new_node = cfunc(elem, cdata);
- if (new_node == NULL) return processed;
+ if (new_node == NULL) return processed; // LCOV_EXCL_LINE
cx_tree_zero_pointers(new_node, cx_tree_ptr_locations);
// start searching from current node
void *node;
if (tree->root == NULL) {
node = tree->node_create(data, tree);
- if (node == NULL) return 1;
+ if (node == NULL) return 1; // LCOV_EXCL_LINE
cx_tree_zero_pointers(node, cx_tree_node_layout(tree));
tree->root = node;
tree->size = 1;
// use the first element from the iter to create the root node
void **eptr = iter->current(iter);
void *node = tree->node_create(*eptr, tree);
- if (node == NULL) return 0;
+ if (node == NULL) return 0; // LCOV_EXCL_LINE
cx_tree_zero_pointers(node, cx_tree_node_layout(tree));
tree->root = node;
ins = 1;
const void *data,
size_t depth
) {
- if (tree->root == NULL) return NULL;
+ if (tree->root == NULL) return NULL; // LCOV_EXCL_LINE
void *found;
if (0 == cx_tree_search_data(
assert(search_data_func != NULL);
CxTree *tree = cxMalloc(allocator, sizeof(CxTree));
- if (tree == NULL) return NULL;
+ if (tree == NULL) return NULL; // LCOV_EXCL_LINE
tree->cl = &cx_tree_default_class;
tree->allocator = allocator;
assert(root != NULL);
CxTree *tree = cxMalloc(allocator, sizeof(CxTree));
- if (tree == NULL) return NULL;
+ if (tree == NULL) return NULL; // LCOV_EXCL_LINE
tree->cl = &cx_tree_default_class;
// set the allocator anyway, just in case...
int cxTreeAddChild(CxTree *tree, void *parent, const void *data) {
void *node = tree->node_create(data, tree);
- if (node == NULL) return 1;
+ if (node == NULL) return 1; // LCOV_EXCL_LINE
cx_tree_zero_pointers(node, cx_tree_node_layout(tree));
cx_tree_link(parent, node, cx_tree_node_layout(tree));
tree->size++;
cxFreeDefault(q);
q = next;
}
+ visitor->queue_next = visitor->queue_last = NULL;
}
CxTreeIterator cxTreeIterateSubtree(CxTree *tree, void *node, bool visit_on_exit) {