c/cmd/pp/pp.c | 15 +++------------ c/lib/items.c | 20 ++++++++++++-------- c/lib/items.h | 3 ++- c/lib/mempool.c | 224 ++++++++++++++++++++++++++++------------------------- c/lib/mempool.h | 68 +++++++++++++++++++++++++++--------------------------- c/t/bool.t.c | 2 +- c/t/magic.t.c | 4 ++-- c/t/nil.t.c | 2 +- c/t/tai64.t.c | 6 +++--- diff --git a/c/cmd/pp/pp.c b/c/cmd/pp/pp.c index 843fb49e9f107b750c157d8740f11d92908224b98c1e19b166e6c31befedafcc..d9fcd1f6759508950f86252078ad6a0b4e9b4c56cf19dc48f77ee643e388894d 100644 --- a/c/cmd/pp/pp.c +++ b/c/cmd/pp/pp.c @@ -448,20 +448,11 @@ return EXIT_FAILURE; } } if (!noTotals) { - const size_t chunkLen = KEKSMemPoolChunkSize(items.pool); - size_t chunks = 1; - { - const struct KEKSMemPoolChunkHdr *hdr = - (const struct KEKSMemPoolChunkHdr *)(items.pool); - while (hdr->next != NULL) { - chunks++; - hdr = (const struct KEKSMemPoolChunkHdr *)(hdr->next); - } - } - const size_t poolLen = KEKSMemPoolLen(items.pool); + const size_t chunkLen = keksMemPoolChunkSize(&(items.pool)); + const size_t chunks = items.pool.chunks; printf( "items: %zu pool: %zu=%zu*%zu time: %zuns\n", - poolLen, + KEKSMemPoolLen(&(items.pool)), chunkLen * chunks, chunks, chunkLen, diff --git a/c/lib/items.c b/c/lib/items.c index f4cc19a19d871e07b36269579bdeb3762ab3ef050fb8c91d04f15033d614df57..bba59e73cecee78ffb31726121fc827221c7abcaa27f86679edc229c9377113e 100644 --- a/c/lib/items.c +++ b/c/lib/items.c @@ -32,9 +32,12 @@ KEKSItemsInit(struct KEKSItems *items, const size_t chunkLen) { assert(items != NULL); items->entry = NULL; - items->pool = - KEKSMemPoolNew(sizeof(struct KEKSItem), chunkLen, 8, items->allocator); - if (items->pool == NULL) { + items->pool.size = sizeof(struct KEKSItem); + items->pool.cap = chunkLen; + items->pool.align = 8; + items->pool.allocator = items->allocator; + KEKSMemPoolInit(&(items->pool)); + if (items->pool.last == NULL) { return KEKSErrNoMem; } return KEKSErrNo; @@ -45,7 +48,7 @@ KEKSItemsClear(struct KEKSItems *items) { assert(items != NULL); items->entry = NULL; - KEKSMemPoolClear(items->pool); + KEKSMemPoolClear(&(items->pool)); } void @@ -53,7 +56,7 @@ KEKSItemsFree(struct KEKSItems *items) { assert(items != NULL); items->entry = NULL; - KEKSMemPoolFree(items->pool); + KEKSMemPoolFree(&(items->pool)); } static enum KEKSErr @@ -69,20 +72,21 @@ assert(item != NULL); assert(off != NULL); assert(buf != NULL); enum KEKSErr err = KEKSErrInvalid; - *item = KEKSMemPoolGet(items->pool); + *item = KEKSMemPoolGet(&(items->pool)); if (*item == NULL) { return KEKSErrNoMem; } + memset(item, 0, sizeof(struct KEKSItem)); (*item)->offset = *off; size_t got = 0; assert(len >= (*off)); err = KEKSAtomDecode(&got, &((*item)->atom), buf + *off, len - (*off)); if (err != KEKSErrNo) { - KEKSMemPoolPut(items->pool, *item); + KEKSMemPoolPut(&(items->pool), *item); return err; } if ((SIZE_MAX - got) < (*off)) { - KEKSMemPoolPut(items->pool, *item); + KEKSMemPoolPut(&(items->pool), *item); return KEKSErrLenTooBig; } (*off) += got; diff --git a/c/lib/items.h b/c/lib/items.h index 7c5b16be274501307e33a26d17ff349fcea4ed5caa81f4f8980cc1190e3a43ed..0d9c81afb41766dda65c64aec2f82a2812dc56a539eb423ed6987136c524ad7b 100644 --- a/c/lib/items.h +++ b/c/lib/items.h @@ -6,6 +6,7 @@ #include #include "atom.h" #include "err.h" +#include "mempool.h" // Each item contains the .atom structure. But item can be a part // of the list or map, so .next contains the pointer to the next @@ -32,7 +33,7 @@ // When you use KEKSItemsParse, then it parses the buffer and // continuously adds new items to the pool. It is initialised with the // .allocator. If it is NULL, then ordinary malloc() is used. struct KEKSItems { - void *pool; + struct KEKSMemPoolHdr pool; struct KEKSMemPoolAllocator *allocator; struct KEKSItem *entry; }; diff --git a/c/lib/mempool.c b/c/lib/mempool.c index 8d4dfae579768dc3c8b2669794d27d77b4ab1dc3549fc71393e03cff29e1c117..757875cc0d451377557efa1b2c5b7b526df0b4f5332b687aa23232490c35033a 100644 --- a/c/lib/mempool.c +++ b/c/lib/mempool.c @@ -1,3 +1,17 @@ +// Copyright (C) 2026 Sergey Matveev +// +// 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, version 3 of the License. +// +// 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, see . + #include #include #include @@ -38,35 +52,22 @@ return a + b; } size_t -KEKSMemPoolChunkSize(const void *pool) +keksMemPoolChunkSize(const struct KEKSMemPoolHdr *pool) { - const struct KEKSMemPoolChunkHdr *hdr = (const struct KEKSMemPoolChunkHdr *)pool; - size_t want = hdr->size * hdr->cap; - assert(hdr->size == (want / hdr->cap)); - return addWithOverflowCheck(want, hdrLen(hdr->cap, hdr->align)); + size_t want = pool->size * pool->cap; + assert(pool->size == (want / pool->cap)); + return addWithOverflowCheck(want, hdrLen(pool->cap, pool->align)); } -void * -KEKSMemPoolNew( - size_t size, - size_t cap, - const size_t align, - struct KEKSMemPoolAllocator *allocator) +static void * +keksMemPoolChunkNew(struct KEKSMemPoolHdr *pool) { - assert(cap > 0); - size = ceilTo(size, align); - cap = ceilTo(cap, 64); - size_t want = 0; - { - const struct KEKSMemPoolChunkHdr hdr = { - .size = size, .cap = cap, .align = align}; - want = KEKSMemPoolChunkSize(&hdr); - } + size_t want = keksMemPoolChunkSize(pool); unsigned char *buf = NULL; - if (allocator == NULL) { + if (pool->allocator == NULL) { buf = malloc(want); } else { - buf = allocator->func(want, allocator); + buf = pool->allocator->func(want, pool->allocator); } if (buf == NULL) { return NULL; @@ -74,15 +75,23 @@ } memset(buf, 0, want); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wcast-align" - struct KEKSMemPoolChunkHdr *hdr = (struct KEKSMemPoolChunkHdr *)buf; + struct KEKSMemPoolChunkHdr *chunkHdr = (struct KEKSMemPoolChunkHdr *)buf; #pragma clang diagnostic pop - hdr->size = size; - hdr->cap = cap; - hdr->align = align; - hdr->allocator = allocator; + chunkHdr->pool = pool; + pool->chunks++; return buf; } +void +KEKSMemPoolInit(struct KEKSMemPoolHdr *pool) +{ + assert(pool->cap > 0); + pool->size = ceilTo(pool->size, pool->align); + pool->cap = ceilTo(pool->cap, 64); + pool->first = keksMemPoolChunkNew(pool); + pool->last = pool->first; +} + void * KEKSMemPoolAllocatorFixedBufFunc(size_t size, void *self) { @@ -110,122 +119,127 @@ return (struct KEKSMemPoolAllocator *)state; } void -KEKSMemPoolClear(void *pool) // NOLINT(misc-no-recursion) +KEKSMemPoolClear(struct KEKSMemPoolHdr *pool) // NOLINT(misc-no-recursion) { assert(pool != NULL); - struct KEKSMemPoolIterState s = KEKSMemPoolIter(pool); - void *obj = NULL; - for (;;) { - obj = KEKSMemPoolIterNext(&s); - if (obj == NULL) { - break; + const size_t len = + hdrLen(pool->cap, pool->align) - (sizeof(struct KEKSMemPoolChunkHdr) - 8); + struct KEKSMemPoolChunkHdr *chunk = pool->last; + while (chunk != NULL) { + if (chunk->len != 0) { + chunk->len = 0; + memset(&(chunk->freelist), 0, len); } - KEKSMemPoolPut(pool, obj); + chunk = chunk->prev; } + pool->len = 0; } -void -KEKSMemPoolFree(void *pool) // NOLINT(misc-no-recursion) +static void +keksMemPoolChunkFree(void *chunk) // NOLINT(misc-no-recursion) { - assert(pool != NULL); - struct KEKSMemPoolChunkHdr *hdr = (struct KEKSMemPoolChunkHdr *)pool; - if (hdr->next != NULL) { - KEKSMemPoolFree(hdr->next); + assert(chunk != NULL); + struct KEKSMemPoolChunkHdr *hdr = (struct KEKSMemPoolChunkHdr *)chunk; + if (hdr->prev != NULL) { + keksMemPoolChunkFree(hdr->prev); } - hdr->next = NULL; - free(pool); + hdr->prev = NULL; + free(chunk); } -size_t -KEKSMemPoolChunkLen(const void *pool) +void +KEKSMemPoolFree(struct KEKSMemPoolHdr *pool) // NOLINT(misc-no-recursion) { assert(pool != NULL); - const struct KEKSMemPoolChunkHdr *hdr = (const struct KEKSMemPoolChunkHdr *)pool; - size_t n = 0; - size_t bytes = hdr->cap / 8; - if (hdr->cap % 8 != 0) { - bytes++; - } - for (size_t i = 0; i < bytes; i++) { - // __builtin_popcount is not portable, but anyway exists - // in Clang and GNU GCC. - n += (size_t)__builtin_popcount(hdr->freelist[i]); - } - return n; -} - -size_t -KEKSMemPoolLen(const void *pool) // NOLINT(misc-no-recursion) -{ - size_t n = KEKSMemPoolChunkLen(pool); - const struct KEKSMemPoolChunkHdr *hdr = (const struct KEKSMemPoolChunkHdr *)pool; - if (hdr->next != NULL) { - n += KEKSMemPoolLen(hdr->next); - } - return n; + keksMemPoolChunkFree(pool->last); } -void * -KEKSMemPoolChunkGetObj(void *pool, const size_t i) +static void * +keksMemPoolChunkGetObj(void *chunk, const size_t i) { - assert(pool != NULL); - const struct KEKSMemPoolChunkHdr *hdr = (const struct KEKSMemPoolChunkHdr *)pool; + assert(chunk != NULL); + const struct KEKSMemPoolChunkHdr *hdr = (const struct KEKSMemPoolChunkHdr *)chunk; if ((hdr->freelist[i / 8] & ((size_t)1 << (i % 8))) == 0) { return NULL; } - return (unsigned char *)pool + hdrLen(hdr->cap, hdr->align) + i * hdr->size; + return (unsigned char *)chunk + hdrLen(hdr->pool->cap, hdr->pool->align) + + i * hdr->pool->size; } -void * -KEKSMemPoolGet(void *pool) // NOLINT(misc-no-recursion) +static void * +keksMemPoolChunkAlloc(void *chunk) // NOLINT(misc-no-recursion) { - assert(pool != NULL); - struct KEKSMemPoolChunkHdr *hdr = (struct KEKSMemPoolChunkHdr *)pool; - if (KEKSMemPoolChunkLen(pool) != hdr->cap) { - for (size_t i = 0; i < hdr->cap; i++) { + assert(chunk != NULL); + struct KEKSMemPoolChunkHdr *hdr = (struct KEKSMemPoolChunkHdr *)chunk; + if (hdr->len != hdr->pool->cap) { + for (size_t i = 0; i < hdr->pool->cap; i++) { if ((hdr->freelist[i / 8] & ((size_t)1 << (i % 8))) == 0) { hdr->freelist[i / 8] |= ((size_t)1 << (i % 8)); - return (unsigned char *)pool + hdrLen(hdr->cap, hdr->align) + - i * hdr->size; + hdr->len++; + hdr->pool->len++; + return (unsigned char *)chunk + + hdrLen(hdr->pool->cap, hdr->pool->align) + i * hdr->pool->size; } } assert(false); } - if (hdr->next == NULL) { - hdr->next = KEKSMemPoolNew(hdr->size, hdr->cap, hdr->align, hdr->allocator); - if (hdr->next == NULL) { - return NULL; - } + if (hdr->prev != NULL) { + return keksMemPoolChunkAlloc(hdr->prev); } - return KEKSMemPoolGet(hdr->next); + struct KEKSMemPoolChunkHdr *newChunk = keksMemPoolChunkNew(hdr->pool); + if (newChunk == NULL) { + return NULL; + } + hdr->pool->last->next = newChunk; + newChunk->prev = hdr->pool->last; + hdr->pool->last = newChunk; + return keksMemPoolChunkAlloc(newChunk); } -void -KEKSMemPoolPut(void *pool, void *obj) // NOLINT(misc-no-recursion) +void * +KEKSMemPoolGet(struct KEKSMemPoolHdr *pool) { assert(pool != NULL); - struct KEKSMemPoolChunkHdr *hdr = (struct KEKSMemPoolChunkHdr *)pool; + return keksMemPoolChunkAlloc(pool->last); +} + +static void +KEKSMemPoolChunkPut(void *chunk, void *obj) // NOLINT(misc-no-recursion) +{ + assert(chunk != NULL); + struct KEKSMemPoolChunkHdr *hdr = (struct KEKSMemPoolChunkHdr *)chunk; uintptr_t p = (uintptr_t)obj; - assert((p % hdr->align) == 0); - uintptr_t first = (uintptr_t)pool + hdrLen(hdr->cap, hdr->align); - uintptr_t last = first + hdr->size * hdr->cap; + assert((p % hdr->pool->align) == 0); + uintptr_t first = (uintptr_t)chunk + hdrLen(hdr->pool->cap, hdr->pool->align); + uintptr_t last = first + hdr->pool->size * hdr->pool->cap; if ((p >= first) && (p < last)) { - size_t i = (p - first) / hdr->size; + size_t i = (p - first) / hdr->pool->size; // forbid double freeing assert((hdr->freelist[i / 8] & ((size_t)1 << (i % 8))) != 0); hdr->freelist[i / 8] ^= (size_t)1 << (i % 8); - memset(obj, 0, hdr->size); + hdr->len--; + hdr->pool->len--; return; } - assert(hdr->next != NULL); - KEKSMemPoolPut(hdr->next, obj); + assert(hdr->prev != NULL); + KEKSMemPoolChunkPut(hdr->prev, obj); +} + +void +KEKSMemPoolPut(struct KEKSMemPoolHdr *pool, void *obj) +{ + assert(pool != NULL); + KEKSMemPoolChunkPut(pool->last, obj); } struct KEKSMemPoolIterState -KEKSMemPoolIter(void *pool) +KEKSMemPoolIter(struct KEKSMemPoolHdr *pool) { - return (struct KEKSMemPoolIterState){ - .chunk = pool, .i = 0, .len = KEKSMemPoolLen(pool)}; + struct KEKSMemPoolIterState s = {0}; + s.pool = pool; + s.chunk = pool->first; + s.max = pool->chunks * pool->cap; + return s; } void * @@ -234,19 +248,19 @@ { const struct KEKSMemPoolChunkHdr *hdr = (const struct KEKSMemPoolChunkHdr *)state->chunk; for (;;) { - if (state->i == state->len) { + if ((state->i == state->max) || (state->out == state->pool->len)) { return NULL; } - size_t rem = state->i % hdr->cap; + size_t rem = state->i % hdr->pool->cap; if ((state->i != 0) && (rem == 0)) { - state->chunk = hdr->next; - assert(state->chunk != NULL); + state->chunk = ((struct KEKSMemPoolChunkHdr *)(state->chunk))->next; } - void *obj = KEKSMemPoolChunkGetObj(state->chunk, rem); + void *obj = keksMemPoolChunkGetObj(state->chunk, rem); state->i++; if (obj == NULL) { continue; } + state->out++; return obj; } } diff --git a/c/lib/mempool.h b/c/lib/mempool.h index 251cba640901490092762126bd010b59d1decde7af84bbd4d8383c1083d7eda5..a154dc4cf63b882124400dd32516006e54adfbe12abe7005b76bc1290d65784c 100644 --- a/c/lib/mempool.h +++ b/c/lib/mempool.h @@ -13,28 +13,37 @@ // bitmap of free to use objects. If there is none left, then another // chunk is allocated and linked to. Chunk memory region: // hdr | freelist | obj0 | obj1 | ... // hdr+freelist and each object are aligned to specified value. -// -// .allocator is a state with a function which allocates new memory -// region. If .allocator is NULL, then ordinary malloc() is used. To -// use fixed memory region, you may use KEKSMemPoolAllocatorFixedBufNew() -// helper, which prepares KEKSMemPoolAllocator-compatible -// KEKSMemPoolAllocatorFixedBuf state with a pointer to the buffer. -struct KEKSMemPoolChunkHdr { +struct KEKSMemPoolHdr { + // those below are expected to be set by user size_t size; // size of the single object size_t cap; // number of preallocated objects size_t align; // alignment requirement for the object + // .allocator is a state with a function which allocates new memory + // region. If .allocator is NULL, then ordinary malloc() is used. To + // use fixed memory region, you may use KEKSMemPoolAllocatorFixedBufNew() + // helper, which prepares KEKSMemPoolAllocator-compatible + // KEKSMemPoolAllocatorFixedBuf state with a pointer to the buffer. struct KEKSMemPoolAllocator *allocator; - struct KEKSMemPoolChunkHdr *next; // next allocated chunk - unsigned char freelist[8]; // start of the bitmap + + // those are automatically filled + size_t len; // total number of objects + size_t chunks; // number of allocated chunks + struct KEKSMemPoolChunkHdr *last; + struct KEKSMemPoolChunkHdr *first; }; -// Returns NULL if no memory available. -void * -KEKSMemPoolNew( - size_t size, - size_t cap, - const size_t align, - struct KEKSMemPoolAllocator *allocator); +struct KEKSMemPoolChunkHdr { + struct KEKSMemPoolHdr *pool; + struct KEKSMemPoolChunkHdr *prev; // previously allocated chunk + struct KEKSMemPoolChunkHdr *next; + size_t len; // number of allocated objects + unsigned char freelist[8]; // start of the bitmap +}; + +// Allocates memory for the specified pool. +// .last equals to NULL if no memory available. +void +KEKSMemPoolInit(struct KEKSMemPoolHdr *); struct KEKSMemPoolAllocatorFixedBuf { void *(*func)(size_t size, void *self); // KEKSMemPoolAllocatorFixedBufFunc @@ -54,44 +63,35 @@ const size_t len); // Clear and make all objects free. void -KEKSMemPoolClear(void *pool); +KEKSMemPoolClear(struct KEKSMemPoolHdr *); // Free the allocated memory, make pool unusable. void -KEKSMemPoolFree(void *pool); +KEKSMemPoolFree(struct KEKSMemPoolHdr *); // Size of the pool's chunk. size_t -KEKSMemPoolChunkSize(const void *pool); - -// Number of allocated objects in the chunk. -size_t -KEKSMemPoolChunkLen(const void *pool); - -// Number of allocated objects in the pool (in all its chunks). -size_t -KEKSMemPoolLen(const void *pool); +keksMemPoolChunkSize(const struct KEKSMemPoolHdr *); // Allocate object (grab it from the pool). // Returns NULL if no memory available. void * -KEKSMemPoolGet(void *pool); +KEKSMemPoolGet(struct KEKSMemPoolHdr *); // Deallocate object (return it to the pool). void -KEKSMemPoolPut(void *pool, void *obj); - -void * -KEKSMemPoolChunkGetObj(void *pool, const size_t i); +KEKSMemPoolPut(struct KEKSMemPoolHdr *, void *obj); struct KEKSMemPoolIterState { + struct KEKSMemPoolHdr *pool; void *chunk; size_t i; - size_t len; + size_t max; + size_t out; }; struct KEKSMemPoolIterState -KEKSMemPoolIter(void *pool); +KEKSMemPoolIter(struct KEKSMemPoolHdr *); void * KEKSMemPoolIterNext(struct KEKSMemPoolIterState *); diff --git a/c/t/bool.t.c b/c/t/bool.t.c index c6d3f0fa9d24b2912b63d41938df7d988069e08853005d4303aee4a4146a65d7..1a11707fc5043bb9555e8baf52f1f4f5eeb6a3f37043369eb290545f20d18860 100644 --- a/c/t/bool.t.c +++ b/c/t/bool.t.c @@ -34,7 +34,7 @@ size_t off = 0; enum KEKSErr err = KEKSItemsParse(&items, &off, buf, cap); ok(err == KEKSErrNo, "parse"); ok(off == 1, "parse off"); - ok(KEKSMemPoolLen(items.pool) == 1, "parse len"); + ok(KEKSMemPoolLen(&(items.pool)) == 1, "parse len"); ok(items.entry->offset == 0, "parse offset"); struct KEKSItem *item = items.entry; ok(item->next == NULL, "atom next"); diff --git a/c/t/magic.t.c b/c/t/magic.t.c index 778e18a116e59b82c10d7cba1c75d0f46bdbdeb324d0562414ef91048a229195..1a16c89dde4bd0f686a626564974f440700089739855f1406fb0a0b845b9dd10 100644 --- a/c/t/magic.t.c +++ b/c/t/magic.t.c @@ -41,7 +41,7 @@ ok(err == KEKSErrNotEnough, "parse not enough"); err = KEKSItemsParse(&items, &off, buf, cap); ok(err == KEKSErrNo, "parse"); ok(off == 16, "parse off"); - ok(KEKSMemPoolLen(items.pool) == 1, "parse len"); + ok(KEKSMemPoolLen(&(items.pool)) == 1, "parse len"); ok(items.entry->offset == 0, "parse offset"); struct KEKSItem *item = items.entry; ok(item->next == NULL, "atom next"); @@ -83,7 +83,7 @@ off = 0; err = KEKSItemsParse(&items, &off, buf, cap); ok(err == KEKSErrNo, "shorter parse"); ok(off == 16, "parse off"); - ok(KEKSMemPoolLen(items.pool) == 1, "parse len"); + ok(KEKSMemPoolLen(&(items.pool)) == 1, "parse len"); ok(items.entry->offset == 0, "parse offset"); item = items.entry; ok(item->next == NULL, "shorter atom next"); diff --git a/c/t/nil.t.c b/c/t/nil.t.c index 9d3b87e9b47180b2efac320d930d714807fe3755d8401bb4b969881051c51107..9275e1893f57a2e3755d50f24640d02042ac72889be33ad37abda9c521f39df1 100644 --- a/c/t/nil.t.c +++ b/c/t/nil.t.c @@ -29,7 +29,7 @@ size_t off = 0; enum KEKSErr err = KEKSItemsParse(&items, &off, buf, cap); ok(err == KEKSErrNo, "parse"); ok(off == 1, "parse off"); - ok(KEKSMemPoolLen(items.pool) == 1, "parse len"); + ok(KEKSMemPoolLen(&(items.pool)) == 1, "parse len"); ok(items.entry->offset == 0, "parse offset"); struct KEKSItem *item = items.entry; ok(item->next == NULL, "atom next"); diff --git a/c/t/tai64.t.c b/c/t/tai64.t.c index 64bbd9a8329140b14b53c262d608bb0b4ed8ce6f3667d9c8a22d769327ef3d06..09c886df3192524b08eb0f2c36c8f7559cee770d83cddb5e853669014e70fc64 100644 --- a/c/t/tai64.t.c +++ b/c/t/tai64.t.c @@ -75,7 +75,7 @@ ok(err == KEKSErrNotEnough, "64: parse not enough"); err = KEKSItemsParse(&items, &off, buf, cap); ok(err == KEKSErrNo, "64: parse"); ok(off == 8 + 1, "64: parse off"); - ok(KEKSMemPoolLen(items.pool) == 1, "64: parse len"); + ok(KEKSMemPoolLen(&(items.pool)) == 1, "64: parse len"); ok(items.entry->offset == 0, "64: parse offset"); struct KEKSItem *item = items.entry; ok(item->next == NULL, "64: atom next"); @@ -113,7 +113,7 @@ ok(err == KEKSErrNotEnough, "64N: parse not enough"); err = KEKSItemsParse(&items, &off, buf, cap); ok(err == KEKSErrNo, "64N: parse"); ok(off == 12 + 1, "64N: parse off"); - ok(KEKSMemPoolLen(items.pool) == 1, "64N: parse len"); + ok(KEKSMemPoolLen(&(items.pool)) == 1, "64N: parse len"); ok(items.entry->offset == 0, "64N: parse offset"); item = items.entry; ok(item->next == NULL, "64N: atom next"); @@ -162,7 +162,7 @@ ok(err == KEKSErrNotEnough, "64NA: parse not enough"); err = KEKSItemsParse(&items, &off, buf, cap); ok(err == KEKSErrNo, "64NA: parse"); ok(off == 16 + 1, "64NA: parse off"); - ok(KEKSMemPoolLen(items.pool) == 1, "64NA: parse len"); + ok(KEKSMemPoolLen(&(items.pool)) == 1, "64NA: parse len"); ok(items.entry->offset == 0, "64NA: parse offset"); item = items.entry; ok(item->next == 0, "64NA: atom next");