Skip to content

Commit a844645

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 a844645

File tree

2 files changed

+156
-12
lines changed

2 files changed

+156
-12
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: 150 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -105,19 +105,96 @@ 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 = (Test_alloc_alloc*)realloc(
152+
state->allocs, new_capacity * sizeof(state->allocs[0]));
153+
assert(state->live_alloc_count == old_capacity);
154+
assert(new_allocs != NULL);
155+
memset(&new_allocs[old_capacity], 0, (new_capacity - old_capacity) * sizeof(state->allocs[0]));
156+
state->allocs = new_allocs;
157+
state->allocs_capacity = new_capacity;
158+
alloc = &state->allocs[old_capacity];
159+
}
160+
assert(alloc != NULL);
161+
assert(alloc->ptr == (uintptr_t)NULL);
162+
alloc->ptr = ptr;
163+
alloc->size = size;
164+
state->live_alloc_count++;
165+
state->live_alloc_total_space += size;
166+
}
167+
168+
static void alloc_state_record_free(Test_alloc_state* state, uintptr_t ptr) {
169+
size_t i;
170+
if (ptr == (uintptr_t)NULL) {
171+
return;
172+
}
173+
for (i = 0; i < state->allocs_capacity; i++) {
174+
Test_alloc_alloc* alloc = &state->allocs[i];
175+
if (alloc->ptr == ptr) {
176+
const size_t size = alloc->size;
177+
assert(state->live_alloc_count >= 1);
178+
assert(state->live_alloc_total_space >= size);
179+
state->live_alloc_count--;
180+
state->live_alloc_total_space -= size;
181+
alloc->ptr = (uintptr_t)NULL;
182+
alloc->size = 0;
183+
return;
184+
}
185+
}
186+
assert(0); /* didn't find matching entry */
187+
}
111188

112189
static void* dummy_malloc(void* state, size_t s)
113190
{
114191
Test_alloc_state* const t = (Test_alloc_state*)state;
115192
void* const p = malloc(s);
116193
if (p==NULL) return NULL;
117194
assert(t != NULL);
118-
t->nbAllocs += 1;
119-
DISPLAYLEVEL(6, "Allocating %u bytes at address %p \n", (unsigned)s, p);
120-
DISPLAYLEVEL(5, "nb allocated memory segments : %i \n", t->nbAllocs);
195+
alloc_state_record_alloc(t, (uintptr_t)p, s);
196+
DISPLAYLEVEL(6, "Allocating %llu bytes at address %p \n", (long long unsigned)s, p);
197+
DISPLAYLEVEL(5, "nb allocated memory segments : %llu \n", (long long unsigned)t->live_alloc_count);
121198
return p;
122199
}
123200

@@ -127,9 +204,9 @@ static void* dummy_calloc(void* state, size_t s)
127204
void* const p = calloc(1, s);
128205
if (p==NULL) return NULL;
129206
assert(t != NULL);
130-
t->nbAllocs += 1;
131-
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);
207+
alloc_state_record_alloc(t, (uintptr_t)p, s);
208+
DISPLAYLEVEL(6, "Allocating and zeroing %llu bytes at address %p \n", (long long unsigned)s, p);
209+
DISPLAYLEVEL(5, "nb allocated memory segments : %llu \n", (long long unsigned)t->live_alloc_count);
133210
return p;
134211
}
135212

@@ -141,11 +218,10 @@ static void dummy_free(void* state, void* p)
141218
return;
142219
}
143220
DISPLAYLEVEL(6, "freeing memory at address %p \n", p);
144-
free(p);
145221
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);
222+
alloc_state_record_free(t, (uintptr_t)p);
223+
free(p);
224+
DISPLAYLEVEL(5, "nb of allocated memory segments after this free : %llu \n", (long long unsigned)t->live_alloc_count);
149225
}
150226

151227
static const LZ4F_CustomMem lz4f_cmem_test = {
@@ -293,6 +369,7 @@ static int unitTests(U32 seed, double compressibility)
293369
int basicTests_error = 0;
294370
LZ4F_preferences_t prefs;
295371
memset(&prefs, 0, sizeof(prefs));
372+
alloc_state_init((Test_alloc_state*)lz4f_cmem_test.opaqueState);
296373

297374
if (!CNBuffer || !compressedBuffer || !decodedBuffer) {
298375
DISPLAY("allocation error, not enough memory to start fuzzer tests \n");
@@ -624,6 +701,7 @@ static int unitTests(U32 seed, double compressibility)
624701
/* dictID tests */
625702
{ size_t cErr;
626703
U32 const dictID = 0x99;
704+
627705
/* test advanced variant with custom allocator functions */
628706
cctx = LZ4F_createCompressionContext_advanced(lz4f_cmem_test, LZ4F_VERSION);
629707
if (cctx==NULL) goto _output_error;
@@ -956,13 +1034,73 @@ static int unitTests(U32 seed, double compressibility)
9561034
DISPLAYLEVEL(3, "Skipped %i bytes \n", (int)(ip - (BYTE*)compressedBuffer - 8));
9571035
}
9581036

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

9681106
_output_error:

0 commit comments

Comments
 (0)