Skip to content

Commit 7a07e39

Browse files
committed
Add Test Validating Allocated Size Matches Reported Size
Adds additional record-keeping to track the live allocation size of a context using a custom memory allocator. This could be extended to more scenarios (e.g., streaming) if desired.
1 parent 92a17e9 commit 7a07e39

File tree

2 files changed

+153
-10
lines changed

2 files changed

+153
-10
lines changed

doc/lz4frame_manual.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,5 +504,11 @@ <h1>1.10.0 Manual</h1>
504504

505505
</p></pre><BR>
506506

507+
<pre><b>LZ4FLIB_STATIC_API size_t LZ4F_cctx_size(const LZ4F_cctx* cctx);
508+
LZ4FLIB_STATIC_API size_t LZ4F_dctx_size(const LZ4F_dctx* dctx);
509+
</b><p> These functions return the total memory footprint of the provided context.
510+
511+
</p></pre><BR>
512+
507513
</html>
508514
</body>

tests/frametest.c

Lines changed: 147 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -105,19 +105,95 @@ static U32 use_pause = 0;
105105
#define MAX(a,b) ( (a) > (b) ? (a) : (b) )
106106

107107
typedef struct {
108-
int nbAllocs;
108+
uintptr_t ptr;
109+
size_t size;
110+
} Test_alloc_alloc;
111+
112+
typedef struct {
113+
Test_alloc_alloc* allocs;
114+
size_t allocs_capacity;
115+
116+
size_t live_alloc_count;
117+
size_t live_alloc_total_space;
109118
} Test_alloc_state;
110-
static Test_alloc_state g_testAllocState = { 0 };
119+
120+
static Test_alloc_state g_testAllocState;
121+
122+
static void alloc_state_init(Test_alloc_state* state) {
123+
state->allocs = NULL;
124+
state->allocs_capacity = 0;
125+
126+
state->live_alloc_count = 0;
127+
state->live_alloc_total_space = 0;
128+
}
129+
130+
static void alloc_state_destroy(Test_alloc_state* state) {
131+
free(state->allocs);
132+
alloc_state_init(state);
133+
}
134+
135+
static void alloc_state_record_alloc(Test_alloc_state* state, uintptr_t ptr, size_t size) {
136+
size_t i;
137+
Test_alloc_alloc* alloc = NULL;
138+
if (ptr == (uintptr_t)NULL) {
139+
assert(size == 0);
140+
return;
141+
}
142+
for (i = 0; i < state->allocs_capacity; i++) {
143+
if (state->allocs[i].ptr == (uintptr_t)NULL) {
144+
alloc = &state->allocs[i];
145+
break;
146+
}
147+
}
148+
if (alloc == NULL) {
149+
const size_t old_capacity = state->allocs_capacity;
150+
const size_t new_capacity = (old_capacity + !old_capacity) * 2;
151+
Test_alloc_alloc* new_allocs = realloc(state->allocs, new_capacity * sizeof(state->allocs[0]));
152+
assert(state->live_alloc_count == old_capacity);
153+
assert(new_allocs != NULL);
154+
memset(&new_allocs[old_capacity], 0, (new_capacity - old_capacity) * sizeof(state->allocs[0]));
155+
state->allocs = new_allocs;
156+
state->allocs_capacity = new_capacity;
157+
alloc = &state->allocs[old_capacity];
158+
}
159+
assert(alloc != NULL);
160+
assert(alloc->ptr == (uintptr_t)NULL);
161+
alloc->ptr = ptr;
162+
alloc->size = size;
163+
state->live_alloc_count++;
164+
state->live_alloc_total_space += size;
165+
}
166+
167+
static void alloc_state_record_free(Test_alloc_state* state, uintptr_t ptr) {
168+
size_t i;
169+
if (ptr == (uintptr_t)NULL) {
170+
return;
171+
}
172+
for (i = 0; i < state->allocs_capacity; i++) {
173+
Test_alloc_alloc* alloc = &state->allocs[i];
174+
if (alloc->ptr == ptr) {
175+
const size_t size = alloc->size;
176+
assert(state->live_alloc_count >= 1);
177+
assert(state->live_alloc_total_space >= size);
178+
state->live_alloc_count--;
179+
state->live_alloc_total_space -= size;
180+
alloc->ptr = (uintptr_t)NULL;
181+
alloc->size = 0;
182+
return;
183+
}
184+
}
185+
assert(0); /* didn't find matching entry */
186+
}
111187

112188
static void* dummy_malloc(void* state, size_t s)
113189
{
114190
Test_alloc_state* const t = (Test_alloc_state*)state;
115191
void* const p = malloc(s);
116192
if (p==NULL) return NULL;
117193
assert(t != NULL);
118-
t->nbAllocs += 1;
194+
alloc_state_record_alloc(t, (uintptr_t)p, s);
119195
DISPLAYLEVEL(6, "Allocating %u bytes at address %p \n", (unsigned)s, p);
120-
DISPLAYLEVEL(5, "nb allocated memory segments : %i \n", t->nbAllocs);
196+
DISPLAYLEVEL(5, "nb allocated memory segments : %lu \n", t->live_alloc_count);
121197
return p;
122198
}
123199

@@ -127,9 +203,9 @@ static void* dummy_calloc(void* state, size_t s)
127203
void* const p = calloc(1, s);
128204
if (p==NULL) return NULL;
129205
assert(t != NULL);
130-
t->nbAllocs += 1;
206+
alloc_state_record_alloc(t, (uintptr_t)p, s);
131207
DISPLAYLEVEL(6, "Allocating and zeroing %u bytes at address %p \n", (unsigned)s, p);
132-
DISPLAYLEVEL(5, "nb allocated memory segments : %i \n", t->nbAllocs);
208+
DISPLAYLEVEL(5, "nb allocated memory segments : %lu \n", t->live_alloc_count);
133209
return p;
134210
}
135211

@@ -141,11 +217,10 @@ static void dummy_free(void* state, void* p)
141217
return;
142218
}
143219
DISPLAYLEVEL(6, "freeing memory at address %p \n", p);
144-
free(p);
145220
assert(t != NULL);
146-
t->nbAllocs -= 1;
147-
DISPLAYLEVEL(5, "nb of allocated memory segments after this free : %i \n", t->nbAllocs);
148-
assert(t->nbAllocs >= 0);
221+
alloc_state_record_free(t, (uintptr_t)p);
222+
free(p);
223+
DISPLAYLEVEL(5, "nb of allocated memory segments after this free : %lu \n", t->live_alloc_count);
149224
}
150225

151226
static const LZ4F_CustomMem lz4f_cmem_test = {
@@ -293,6 +368,7 @@ static int unitTests(U32 seed, double compressibility)
293368
int basicTests_error = 0;
294369
LZ4F_preferences_t prefs;
295370
memset(&prefs, 0, sizeof(prefs));
371+
alloc_state_init((Test_alloc_state*)lz4f_cmem_test.opaqueState);
296372

297373
if (!CNBuffer || !compressedBuffer || !decodedBuffer) {
298374
DISPLAY("allocation error, not enough memory to start fuzzer tests \n");
@@ -624,6 +700,7 @@ static int unitTests(U32 seed, double compressibility)
624700
/* dictID tests */
625701
{ size_t cErr;
626702
U32 const dictID = 0x99;
703+
627704
/* test advanced variant with custom allocator functions */
628705
cctx = LZ4F_createCompressionContext_advanced(lz4f_cmem_test, LZ4F_VERSION);
629706
if (cctx==NULL) goto _output_error;
@@ -956,13 +1033,73 @@ static int unitTests(U32 seed, double compressibility)
9561033
DISPLAYLEVEL(3, "Skipped %i bytes \n", (int)(ip - (BYTE*)compressedBuffer - 8));
9571034
}
9581035

1036+
DISPLAYLEVEL(3, "Context size test: ");
1037+
{
1038+
size_t c_result;
1039+
size_t d_result;
1040+
LZ4F_cctx* cc;
1041+
LZ4F_dctx* dc;
1042+
Test_alloc_state c_allocs;
1043+
Test_alloc_state d_allocs;
1044+
LZ4F_CustomMem c_mem = lz4f_cmem_test;
1045+
LZ4F_CustomMem d_mem = lz4f_cmem_test;
1046+
1047+
alloc_state_init(&c_allocs);
1048+
alloc_state_init(&d_allocs);
1049+
c_mem.opaqueState = &c_allocs;
1050+
d_mem.opaqueState = &d_allocs;
1051+
1052+
if (c_allocs.live_alloc_total_space != 0) goto _output_error;
1053+
if (d_allocs.live_alloc_total_space != 0) goto _output_error;
1054+
1055+
if (LZ4F_cctx_size(NULL) != 0) goto _output_error;
1056+
if (LZ4F_dctx_size(NULL) != 0) goto _output_error;
1057+
1058+
cc = LZ4F_createCompressionContext_advanced(c_mem, LZ4F_VERSION);
1059+
dc = LZ4F_createDecompressionContext_advanced(d_mem, LZ4F_VERSION);
1060+
1061+
if (cc == NULL) goto _output_error;
1062+
if (dc == NULL) goto _output_error;
1063+
1064+
if (LZ4F_cctx_size(cc) != c_allocs.live_alloc_total_space) goto _output_error;
1065+
if (LZ4F_dctx_size(dc) != d_allocs.live_alloc_total_space) goto _output_error;
1066+
1067+
c_result = LZ4F_compressFrame_usingCDict(
1068+
cc,
1069+
compressedBuffer, LZ4F_compressFrameBound(testSize, NULL),
1070+
CNBuffer, testSize,
1071+
NULL, NULL);
1072+
CHECK(c_result);
1073+
1074+
d_result = testSize + 1;
1075+
CHECK(LZ4F_decompress(dc, decodedBuffer, &d_result, compressedBuffer, &c_result, NULL));
1076+
1077+
if (d_result != testSize) goto _output_error;
1078+
1079+
if (LZ4F_cctx_size(cc) != c_allocs.live_alloc_total_space) {
1080+
DISPLAYLEVEL(3, "%lu allocated in cctx but it says its size is %lu.\n", c_allocs.live_alloc_total_space, LZ4F_cctx_size(cc));
1081+
goto _output_error;
1082+
}
1083+
if (LZ4F_dctx_size(dc) != d_allocs.live_alloc_total_space) {
1084+
DISPLAYLEVEL(3, "%lu allocated in dctx but it says its size is %lu.\n", d_allocs.live_alloc_total_space, LZ4F_dctx_size(dc));
1085+
goto _output_error;
1086+
}
1087+
1088+
LZ4F_freeCompressionContext(cc);
1089+
LZ4F_freeDecompressionContext(dc);
1090+
alloc_state_destroy(&c_allocs);
1091+
alloc_state_destroy(&d_allocs);
1092+
}
1093+
DISPLAYLEVEL(3, "OK \n");
1094+
9591095
DISPLAY("Basic tests completed \n");
9601096
_end:
9611097
free(CNBuffer);
9621098
free(compressedBuffer);
9631099
free(decodedBuffer);
9641100
LZ4F_freeDecompressionContext(dCtx); dCtx = NULL;
9651101
LZ4F_freeCompressionContext(cctx); cctx = NULL;
1102+
alloc_state_destroy((Test_alloc_state*)lz4f_cmem_test.opaqueState);
9661103
return basicTests_error;
9671104

9681105
_output_error:

0 commit comments

Comments
 (0)