forked from bartvdbraak/blender
BLI_memiter: Small API for many small allocations
- Each allocation can be a different size (but should be smaller than the chunk size). - Result can be looped over in order of allocation. - Allocations are aligned to pointer size to avoid unaligned reads.
This commit is contained in:
parent
277dc47eea
commit
913d8ec608
72
source/blender/blenlib/BLI_memiter.h
Normal file
72
source/blender/blenlib/BLI_memiter.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#ifndef __BLI_MEMITER_H__
|
||||
#define __BLI_MEMITER_H__
|
||||
|
||||
/** \file BLI_memiter.h
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_compiler_compat.h"
|
||||
|
||||
/* 512kb, good default for small elems. */
|
||||
#define BLI_MEMITER_DEFAULT_SIZE (2 << 18)
|
||||
|
||||
struct BLI_memiter;
|
||||
struct BLI_memiter_chunk;
|
||||
|
||||
typedef struct BLI_memiter BLI_memiter;
|
||||
|
||||
/* warning, ATTR_MALLOC flag on BLI_memiter_alloc causes crash, see: D2756 */
|
||||
BLI_memiter *BLI_memiter_create(unsigned int chunk_size) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
|
||||
void *BLI_memiter_alloc(BLI_memiter *mi, unsigned int size) ATTR_RETURNS_NONNULL ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
|
||||
void BLI_memiter_alloc_from(BLI_memiter *mi, uint elem_size, const void *data_from) ATTR_NONNULL(1, 3);
|
||||
void *BLI_memiter_calloc(BLI_memiter *mi, unsigned int size) ATTR_RETURNS_NONNULL ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
|
||||
void BLI_memiter_destroy(BLI_memiter *mi) ATTR_NONNULL(1);
|
||||
void BLI_memiter_clear(BLI_memiter *mi) ATTR_NONNULL(1);
|
||||
unsigned int BLI_memiter_count(const BLI_memiter *mi) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1);
|
||||
|
||||
/* utils */
|
||||
void *BLI_memiter_elem_first(BLI_memiter *mi);
|
||||
void *BLI_memiter_elem_first_size(BLI_memiter *mi, unsigned int *r_size);
|
||||
|
||||
/* private structure */
|
||||
typedef struct BLI_memiter_handle {
|
||||
struct BLI_memiter_elem *elem;
|
||||
uint elem_left;
|
||||
} BLI_memiter_handle;
|
||||
|
||||
void BLI_memiter_iter_init(BLI_memiter *mi, BLI_memiter_handle *iter) ATTR_NONNULL();
|
||||
bool BLI_memiter_iter_done(BLI_memiter_handle *iter) ATTR_NONNULL();
|
||||
void *BLI_memiter_iter_step(BLI_memiter_handle *iter) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
void *BLI_memiter_iter_step_size(BLI_memiter_handle *iter, uint *r_size) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __BLI_MEMITER_H__ */
|
@ -50,6 +50,7 @@ set(SRC
|
||||
intern/BLI_kdtree.c
|
||||
intern/BLI_linklist.c
|
||||
intern/BLI_memarena.c
|
||||
intern/BLI_memiter.c
|
||||
intern/BLI_mempool.c
|
||||
intern/DLRB_tree.c
|
||||
intern/array_store.c
|
||||
@ -179,6 +180,7 @@ set(SRC
|
||||
BLI_math_statistics.h
|
||||
BLI_math_vector.h
|
||||
BLI_memarena.h
|
||||
BLI_memiter.h
|
||||
BLI_memory_utils.h
|
||||
BLI_mempool.h
|
||||
BLI_noise.h
|
||||
|
354
source/blender/blenlib/intern/BLI_memiter.c
Normal file
354
source/blender/blenlib/intern/BLI_memiter.c
Normal file
@ -0,0 +1,354 @@
|
||||
/*
|
||||
* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/blenlib/intern/BLI_memiter.c
|
||||
* \ingroup bli
|
||||
*
|
||||
* Simple, fast memory allocator for allocating many small elements of different sizes
|
||||
* in fixed size memory chunks,
|
||||
* although allocations bigger than the chunk size are supported.
|
||||
* They will reduce the efficiency of this data-structure.
|
||||
* Elements are pointer aligned.
|
||||
*
|
||||
* Supports:
|
||||
*
|
||||
* - Allocation of mixed sizes.
|
||||
* - Iterating over allocations in-order.
|
||||
* - Clearing for re-use.
|
||||
*
|
||||
* Unsupported:
|
||||
*
|
||||
* - Freeing individual elements.
|
||||
*
|
||||
* \note We could inline iteration stepping,
|
||||
* but tests show this doesn't give noticeable speedup.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BLI_memiter.h" /* own include */
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_strict_flags.h" /* keep last */
|
||||
|
||||
typedef uintptr_t data_t;
|
||||
typedef intptr_t offset_t;
|
||||
|
||||
/* Write the chunk terminator on adding each element.
|
||||
* typically we rely on the 'count' to avoid iterating past the end. */
|
||||
// #define USE_TERMINATE_PARANOID
|
||||
|
||||
/* Currently totalloc isnt used. */
|
||||
// #define USE_TOTALLOC
|
||||
|
||||
/* pad must be power of two */
|
||||
#define PADUP(num, pad) (((num) + ((pad) - 1)) & ~((pad) - 1))
|
||||
|
||||
typedef struct BLI_memiter_elem {
|
||||
offset_t size;
|
||||
data_t data[0];
|
||||
} BLI_memiter_elem;
|
||||
|
||||
typedef struct BLI_memiter_chunk {
|
||||
struct BLI_memiter_chunk *next;
|
||||
/**
|
||||
* internal format is:
|
||||
* ``[next_pointer, size:data, size:data, ..., negative_offset]``
|
||||
*
|
||||
* Where negative offset rewinds to the start.
|
||||
*/
|
||||
data_t data[0];
|
||||
} BLI_memiter_chunk;
|
||||
|
||||
typedef struct BLI_memiter {
|
||||
/* A pointer to 'head' is needed*/
|
||||
struct BLI_memiter_chunk *head, *tail;
|
||||
data_t *data_curr;
|
||||
data_t *data_last;
|
||||
/* Used unless a large element is requested.
|
||||
* (which should be very rare!). */
|
||||
uint chunk_size_in_bytes_min;
|
||||
uint count;
|
||||
#ifdef USE_TOTALLOC
|
||||
uint totalloc;
|
||||
#endif
|
||||
} BLI_memiter;
|
||||
|
||||
|
||||
BLI_INLINE uint data_offset_from_size(uint size)
|
||||
{
|
||||
return (PADUP(size, (uint)sizeof(data_t))) / (uint)sizeof(data_t);
|
||||
}
|
||||
|
||||
static void memiter_set_rewind_offset(BLI_memiter *mi)
|
||||
{
|
||||
BLI_memiter_elem *elem = (BLI_memiter_elem *)mi->data_curr;
|
||||
elem->size = (offset_t)(((data_t *)mi->tail) - mi->data_curr);
|
||||
BLI_assert(elem->size < 0);
|
||||
}
|
||||
|
||||
static void memiter_init(BLI_memiter *mi)
|
||||
{
|
||||
mi->head = NULL;
|
||||
mi->tail = NULL;
|
||||
mi->data_curr = NULL;
|
||||
mi->data_last = NULL;
|
||||
mi->count = 0;
|
||||
#ifdef USE_TOTALLOC
|
||||
mi->totalloc = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
/** \name Public API's
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* \param chunk_size_min: Should be a power of two and
|
||||
* significantly larger than the average element size used.
|
||||
*
|
||||
* While allocations of any size are supported, they won't be efficient
|
||||
* (effectively becoming a single-linked list).
|
||||
*
|
||||
* Its intended that many elements can be stored per chunk.
|
||||
*/
|
||||
BLI_memiter *BLI_memiter_create(uint chunk_size_min)
|
||||
{
|
||||
BLI_memiter *mi = MEM_mallocN(sizeof(BLI_memiter), STRINGIFY(BLI_memiter));
|
||||
memiter_init(mi);
|
||||
|
||||
/* Small values are used for tests to check for correctness,
|
||||
* but otherwise not that useful. */
|
||||
const uint slop_space = (sizeof(BLI_memiter_chunk) + MEM_SIZE_OVERHEAD);
|
||||
if (chunk_size_min >= 1024) {
|
||||
/* As long as the input is a power of 2, this will give efficient sizes. */
|
||||
chunk_size_min -= slop_space;
|
||||
}
|
||||
|
||||
mi->chunk_size_in_bytes_min = (offset_t)chunk_size_min;
|
||||
return mi;
|
||||
}
|
||||
|
||||
void *BLI_memiter_alloc(BLI_memiter *mi, uint elem_size)
|
||||
{
|
||||
const uint data_offset = data_offset_from_size(elem_size);
|
||||
data_t *data_curr_next = mi->data_curr + (1 + data_offset);
|
||||
|
||||
if (UNLIKELY(mi->data_curr == NULL) || (data_curr_next > mi->data_last)) {
|
||||
|
||||
#ifndef USE_TERMINATE_PARANOID
|
||||
if (mi->data_curr != NULL) {
|
||||
memiter_set_rewind_offset(mi);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint chunk_size_in_bytes = mi->chunk_size_in_bytes_min;
|
||||
if (UNLIKELY(chunk_size_in_bytes < elem_size + (uint)sizeof(data_t[2]))) {
|
||||
chunk_size_in_bytes = elem_size + (uint)sizeof(data_t[2]);
|
||||
}
|
||||
uint chunk_size = data_offset_from_size(chunk_size_in_bytes);
|
||||
BLI_memiter_chunk *chunk = MEM_mallocN(
|
||||
sizeof(BLI_memiter_chunk) +
|
||||
(chunk_size * sizeof(data_t)),
|
||||
STRINGIFY(BLI_memiter_chunk));
|
||||
|
||||
if (mi->head == NULL) {
|
||||
BLI_assert(mi->tail == NULL);
|
||||
mi->head = chunk;
|
||||
}
|
||||
else {
|
||||
mi->tail->next = chunk;
|
||||
}
|
||||
mi->tail = chunk;
|
||||
chunk->next = NULL;
|
||||
|
||||
mi->data_curr = chunk->data;
|
||||
mi->data_last = chunk->data + (chunk_size - 1);
|
||||
data_curr_next = mi->data_curr + (1 + data_offset);
|
||||
}
|
||||
|
||||
BLI_assert(data_curr_next <= mi->data_last);
|
||||
|
||||
BLI_memiter_elem *elem = (BLI_memiter_elem *)mi->data_curr;
|
||||
elem->size = elem_size;
|
||||
mi->data_curr = data_curr_next;
|
||||
|
||||
#ifdef USE_TERMINATE_PARANOID
|
||||
memiter_set_rewind_offset(mi);
|
||||
#endif
|
||||
|
||||
mi->count += 1;
|
||||
|
||||
#ifdef USE_TOTALLOC
|
||||
mi->totalloc += elem_size;
|
||||
#endif
|
||||
|
||||
return elem->data;
|
||||
}
|
||||
|
||||
void *BLI_memiter_calloc(BLI_memiter *mi, uint elem_size)
|
||||
{
|
||||
void *data = BLI_memiter_alloc(mi, elem_size);
|
||||
memset(data, 0, elem_size);
|
||||
return data;
|
||||
}
|
||||
|
||||
void BLI_memiter_alloc_from(BLI_memiter *mi, uint elem_size, const void *data_from)
|
||||
{
|
||||
void *data = BLI_memiter_alloc(mi, elem_size);
|
||||
memcpy(data, data_from, elem_size);
|
||||
}
|
||||
|
||||
static void memiter_free_data(BLI_memiter *mi)
|
||||
{
|
||||
BLI_memiter_chunk *chunk = mi->head;
|
||||
while (chunk) {
|
||||
BLI_memiter_chunk *chunk_next = chunk->next;
|
||||
MEM_freeN(chunk);
|
||||
chunk = chunk_next;
|
||||
}
|
||||
}
|
||||
|
||||
void BLI_memiter_destroy(BLI_memiter *mi)
|
||||
{
|
||||
memiter_free_data(mi);
|
||||
MEM_freeN(mi);
|
||||
}
|
||||
|
||||
void BLI_memiter_clear(BLI_memiter *mi)
|
||||
{
|
||||
memiter_free_data(mi);
|
||||
memiter_init(mi);
|
||||
}
|
||||
|
||||
uint BLI_memiter_count(const BLI_memiter *mi)
|
||||
{
|
||||
return mi->count;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
/** \name Helper API's
|
||||
* \{ */
|
||||
|
||||
/* Support direct lookup for first. */
|
||||
void *BLI_memiter_elem_first(BLI_memiter *mi)
|
||||
{
|
||||
if (mi->head != NULL) {
|
||||
BLI_memiter_chunk *chunk = mi->head;
|
||||
BLI_memiter_elem *elem = (BLI_memiter_elem *)chunk->data;
|
||||
return elem->data;
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void *BLI_memiter_elem_first_size(BLI_memiter *mi, unsigned int *r_size)
|
||||
{
|
||||
if (mi->head != NULL) {
|
||||
BLI_memiter_chunk *chunk = mi->head;
|
||||
BLI_memiter_elem *elem = (BLI_memiter_elem *)chunk->data;
|
||||
*r_size = (uint)elem->size;
|
||||
return elem->data;
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
/** \name Iterator API's
|
||||
*
|
||||
* \note We could loop over elements until a NULL chunk is found,
|
||||
* however this means every allocation needs to preemptively run
|
||||
* #memiter_set_rewind_offset (see #USE_TERMINATE_PARANOID).
|
||||
* Unless we have a call to finalize allocation (which complicates usage).
|
||||
* So use a counter instead.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
void BLI_memiter_iter_init(BLI_memiter *mi, BLI_memiter_handle *iter)
|
||||
{
|
||||
iter->elem = mi->head ? (BLI_memiter_elem *)mi->head->data : NULL;
|
||||
iter->elem_left = mi->count;
|
||||
}
|
||||
|
||||
bool BLI_memiter_iter_done(BLI_memiter_handle *iter)
|
||||
{
|
||||
return iter->elem_left != 0;
|
||||
}
|
||||
|
||||
BLI_INLINE void memiter_iter_step(BLI_memiter_handle *iter)
|
||||
{
|
||||
if (UNLIKELY(iter->elem->size < 0)) {
|
||||
BLI_memiter_chunk *chunk = (BLI_memiter_chunk *)(((data_t *)iter->elem) + iter->elem->size);
|
||||
chunk = chunk->next;
|
||||
iter->elem = chunk ? (BLI_memiter_elem *)chunk->data : NULL;
|
||||
BLI_assert(iter->elem == NULL || iter->elem->size >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
void *BLI_memiter_iter_step_size(BLI_memiter_handle *iter, uint *r_size)
|
||||
{
|
||||
if (iter->elem_left != 0) {
|
||||
iter->elem_left -= 1;
|
||||
memiter_iter_step(iter);
|
||||
BLI_assert(iter->elem->size >= 0);
|
||||
uint size = (uint)iter->elem->size;
|
||||
*r_size = size; /* <-- only difference */
|
||||
data_t *data = iter->elem->data;
|
||||
iter->elem = (BLI_memiter_elem *)&data[data_offset_from_size(size)];
|
||||
return (void *)data;
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void *BLI_memiter_iter_step(BLI_memiter_handle *iter)
|
||||
{
|
||||
if (iter->elem_left != 0) {
|
||||
iter->elem_left -= 1;
|
||||
memiter_iter_step(iter);
|
||||
BLI_assert(iter->elem->size >= 0);
|
||||
uint size = (uint)iter->elem->size;
|
||||
data_t *data = iter->elem->data;
|
||||
iter->elem = (BLI_memiter_elem *)&data[data_offset_from_size(size)];
|
||||
return (void *)data;
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
206
tests/gtests/blenlib/BLI_memiter_test.cc
Normal file
206
tests/gtests/blenlib/BLI_memiter_test.cc
Normal file
@ -0,0 +1,206 @@
|
||||
/* Apache License, Version 2.0 */
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
extern "C" {
|
||||
#include "BLI_array_utils.h"
|
||||
#include "BLI_memiter.h"
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_ressource_strings.h"
|
||||
}
|
||||
|
||||
TEST(memiter, Nop)
|
||||
{
|
||||
BLI_memiter *mi = BLI_memiter_create(64);
|
||||
BLI_memiter_destroy(mi);
|
||||
}
|
||||
|
||||
void memiter_empty_test(int num_elems, const int chunk_size)
|
||||
{
|
||||
BLI_memiter *mi = BLI_memiter_create(chunk_size);
|
||||
void *data;
|
||||
for (int index = 0; index < num_elems; index++) {
|
||||
data = BLI_memiter_alloc(mi, 0);
|
||||
}
|
||||
int index = 0, total_size = 0;
|
||||
BLI_memiter_handle it;
|
||||
BLI_memiter_iter_init(mi, &it);
|
||||
uint elem_size;
|
||||
while ((data = BLI_memiter_iter_step_size(&it, &elem_size))) {
|
||||
index += 1;
|
||||
total_size += elem_size;
|
||||
}
|
||||
EXPECT_EQ(0, total_size);
|
||||
EXPECT_EQ(num_elems, index);
|
||||
|
||||
BLI_memiter_destroy(mi);
|
||||
}
|
||||
|
||||
#define MEMITER_NUMBER_TEST_FN(fn, number_type) \
|
||||
void fn(int num_elems, const int chunk_size) \
|
||||
{ \
|
||||
BLI_memiter *mi = BLI_memiter_create(chunk_size); \
|
||||
number_type *data; \
|
||||
for (int index = 0; index < num_elems; index++) { \
|
||||
data = (number_type *)BLI_memiter_alloc(mi, sizeof(number_type)); \
|
||||
*data = index; \
|
||||
} \
|
||||
BLI_memiter_handle it; \
|
||||
BLI_memiter_iter_init(mi, &it); \
|
||||
uint elem_size; \
|
||||
int index = 0; \
|
||||
while ((data = (number_type *)BLI_memiter_iter_step_size(&it, &elem_size))) { \
|
||||
EXPECT_EQ(sizeof(number_type), elem_size); \
|
||||
EXPECT_EQ(index, *data); \
|
||||
index += 1; \
|
||||
} \
|
||||
BLI_memiter_destroy(mi); \
|
||||
}
|
||||
|
||||
/* generate number functions */
|
||||
MEMITER_NUMBER_TEST_FN(memiter_char_test, char)
|
||||
MEMITER_NUMBER_TEST_FN(memiter_short_test, short)
|
||||
MEMITER_NUMBER_TEST_FN(memiter_int_test, int)
|
||||
MEMITER_NUMBER_TEST_FN(memiter_long_test, int64_t)
|
||||
|
||||
void memiter_string_test(const char *strings[], const int chunk_size)
|
||||
{
|
||||
BLI_memiter *mi = BLI_memiter_create(chunk_size);
|
||||
char *data;
|
||||
int index = 0;
|
||||
int total_size_expect = 0;
|
||||
while (strings[index]) {
|
||||
const int size = strlen(strings[index]) + 1;
|
||||
BLI_memiter_alloc_from(mi, size, strings[index]);
|
||||
total_size_expect += size;
|
||||
index += 1;
|
||||
}
|
||||
const int strings_len = index;
|
||||
int total_size = 0;
|
||||
BLI_memiter_handle it;
|
||||
BLI_memiter_iter_init(mi, &it);
|
||||
uint elem_size;
|
||||
index = 0;
|
||||
while ((data = (char *)BLI_memiter_iter_step_size(&it, &elem_size))) {
|
||||
EXPECT_EQ(strlen(strings[index]) + 1, elem_size);
|
||||
EXPECT_STREQ(strings[index], data);
|
||||
total_size += elem_size;
|
||||
index += 1;
|
||||
}
|
||||
EXPECT_EQ(total_size_expect, total_size);
|
||||
EXPECT_EQ(strings_len, index);
|
||||
|
||||
BLI_memiter_destroy(mi);
|
||||
}
|
||||
|
||||
void memiter_words10k_test(const char split_char, const int chunk_size)
|
||||
{
|
||||
const int words_len = sizeof(words10k) - 1;
|
||||
char *words = BLI_strdupn(words10k, words_len);
|
||||
BLI_str_replace_char(words, split_char, '\0');
|
||||
|
||||
BLI_memiter *mi = BLI_memiter_create(chunk_size);
|
||||
|
||||
char *data;
|
||||
int index;
|
||||
char *c_end, *c;
|
||||
c_end = words + words_len;
|
||||
c = words;
|
||||
index = 0;
|
||||
while (c < c_end) {
|
||||
int elem_size = strlen(c) + 1;
|
||||
data = (char *)BLI_memiter_alloc(mi, elem_size);
|
||||
memcpy(data, c, elem_size);
|
||||
c += elem_size;
|
||||
index += 1;
|
||||
}
|
||||
const int len_expect = index;
|
||||
c = words;
|
||||
uint size;
|
||||
BLI_memiter_handle it;
|
||||
BLI_memiter_iter_init(mi, &it);
|
||||
index = 0;
|
||||
while ((data = (char *)BLI_memiter_iter_step_size(&it, &size))) {
|
||||
int size_expect = strlen(c) + 1;
|
||||
EXPECT_EQ(size_expect, size);
|
||||
EXPECT_STREQ(c, data);
|
||||
c += size;
|
||||
index += 1;
|
||||
}
|
||||
EXPECT_EQ(len_expect, index);
|
||||
BLI_memiter_destroy(mi);
|
||||
MEM_freeN(words);
|
||||
}
|
||||
|
||||
|
||||
#define TEST_EMPTY_AT_CHUNK_SIZE(chunk_size) \
|
||||
TEST(memiter, Empty0_##chunk_size) { memiter_empty_test(0, chunk_size); } \
|
||||
TEST(memiter, Empty1_##chunk_size) { memiter_empty_test(1, chunk_size); } \
|
||||
TEST(memiter, Empty2_##chunk_size) { memiter_empty_test(2, chunk_size); } \
|
||||
TEST(memiter, Empty3_##chunk_size) { memiter_empty_test(3, chunk_size); } \
|
||||
TEST(memiter, Empty13_##chunk_size) { memiter_empty_test(13, chunk_size); } \
|
||||
TEST(memiter, Empty256_##chunk_size) { memiter_empty_test(256, chunk_size); } \
|
||||
|
||||
TEST_EMPTY_AT_CHUNK_SIZE(1)
|
||||
TEST_EMPTY_AT_CHUNK_SIZE(2)
|
||||
TEST_EMPTY_AT_CHUNK_SIZE(3)
|
||||
TEST_EMPTY_AT_CHUNK_SIZE(13)
|
||||
TEST_EMPTY_AT_CHUNK_SIZE(256)
|
||||
|
||||
#define TEST_NUMBER_AT_CHUNK_SIZE(chunk_size) \
|
||||
TEST(memiter, Char1_##chunk_size) { memiter_char_test(1, chunk_size); } \
|
||||
TEST(memiter, Short1_##chunk_size) { memiter_short_test(1, chunk_size); } \
|
||||
TEST(memiter, Int1_##chunk_size) { memiter_int_test(1, chunk_size); } \
|
||||
TEST(memiter, Long1_##chunk_size) { memiter_long_test(1, chunk_size); } \
|
||||
\
|
||||
TEST(memiter, Char2_##chunk_size) { memiter_char_test(2, chunk_size); } \
|
||||
TEST(memiter, Short2_##chunk_size) { memiter_short_test(2, chunk_size); } \
|
||||
TEST(memiter, Int2_##chunk_size) { memiter_int_test(2, chunk_size); } \
|
||||
TEST(memiter, Long2_##chunk_size) { memiter_long_test(2, chunk_size); } \
|
||||
\
|
||||
TEST(memiter, Char3_##chunk_size) { memiter_char_test(3, chunk_size); } \
|
||||
TEST(memiter, Short3_##chunk_size) { memiter_short_test(3, chunk_size); } \
|
||||
TEST(memiter, Int3_##chunk_size) { memiter_int_test(3, chunk_size); } \
|
||||
TEST(memiter, Long3_##chunk_size) { memiter_long_test(3, chunk_size); } \
|
||||
\
|
||||
TEST(memiter, Char256_##chunk_size) { memiter_char_test(256, chunk_size); } \
|
||||
TEST(memiter, Short256_##chunk_size) { memiter_short_test(256, chunk_size); } \
|
||||
TEST(memiter, Int256_##chunk_size) { memiter_int_test(256, chunk_size); } \
|
||||
TEST(memiter, Long256_##chunk_size) { memiter_long_test(256, chunk_size); } \
|
||||
|
||||
TEST_NUMBER_AT_CHUNK_SIZE(1)
|
||||
TEST_NUMBER_AT_CHUNK_SIZE(2)
|
||||
TEST_NUMBER_AT_CHUNK_SIZE(3)
|
||||
TEST_NUMBER_AT_CHUNK_SIZE(13)
|
||||
TEST_NUMBER_AT_CHUNK_SIZE(256)
|
||||
|
||||
#define STRINGS_TEST(chunk_size, ...) { \
|
||||
const char *data[] = {__VA_ARGS__, NULL}; \
|
||||
memiter_string_test(data, chunk_size); \
|
||||
}
|
||||
|
||||
#define TEST_STRINGS_AT_CHUNK_SIZE(chunk_size) \
|
||||
TEST(memiter, Strings_##chunk_size) { \
|
||||
STRINGS_TEST(chunk_size, ""); \
|
||||
STRINGS_TEST(chunk_size, "test", "me"); \
|
||||
STRINGS_TEST(chunk_size, "more", "test", "data", "to", "follow"); \
|
||||
}
|
||||
|
||||
TEST_STRINGS_AT_CHUNK_SIZE(1)
|
||||
TEST_STRINGS_AT_CHUNK_SIZE(2)
|
||||
TEST_STRINGS_AT_CHUNK_SIZE(3)
|
||||
TEST_STRINGS_AT_CHUNK_SIZE(13)
|
||||
TEST_STRINGS_AT_CHUNK_SIZE(256)
|
||||
|
||||
|
||||
#define TEST_WORDS10K_AT_CHUNK_SIZE(chunk_size) \
|
||||
TEST(memiter, Words10kSentence_##chunk_size) { memiter_words10k_test('.', chunk_size); } \
|
||||
TEST(memiter, Words10kWords_##chunk_size) { memiter_words10k_test(' ', chunk_size); } \
|
||||
|
||||
TEST_WORDS10K_AT_CHUNK_SIZE(1)
|
||||
TEST_WORDS10K_AT_CHUNK_SIZE(2)
|
||||
TEST_WORDS10K_AT_CHUNK_SIZE(3)
|
||||
TEST_WORDS10K_AT_CHUNK_SIZE(13)
|
||||
TEST_WORDS10K_AT_CHUNK_SIZE(256)
|
@ -42,6 +42,7 @@ BLENDER_TEST(BLI_stack "bf_blenlib")
|
||||
BLENDER_TEST(BLI_math_color "bf_blenlib")
|
||||
BLENDER_TEST(BLI_math_geom "bf_blenlib;bf_intern_eigen")
|
||||
BLENDER_TEST(BLI_math_base "bf_blenlib")
|
||||
BLENDER_TEST(BLI_memiter "bf_blenlib")
|
||||
BLENDER_TEST(BLI_string "bf_blenlib")
|
||||
BLENDER_TEST(BLI_string_utf8 "bf_blenlib")
|
||||
if(WIN32)
|
||||
|
Loading…
Reference in New Issue
Block a user