Remove except as it is very clunky to use, there are simpler ways; More corrections...
[SquirrelJME.git] / nanocoat / lib / base / alloc.c
blob7f6ff3a88135a38e2b5e5cba68b0eeeb3161f9d6
1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
3 // SquirrelJME
4 // Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
5 // ---------------------------------------------------------------------------
6 // SquirrelJME is under the Mozilla Public License Version 2.0.
7 // See license.mkd for licensing and copyright information.
8 // -------------------------------------------------------------------------*/
10 #include <string.h>
11 #include <stdio.h>
13 /* Include Valgrind if it is available? */
14 #if defined(SJME_CONFIG_HAS_VALGRIND)
15 #include <valgrind.h>
16 #include <memcheck.h>
17 #endif
19 #include "sjme/alloc.h"
20 #include "sjme/debug.h"
21 #include "sjme/atomic.h"
22 #include "sjme/multithread.h"
24 /** The minimum size permitted for allocation pools. */
25 #define SJME_ALLOC_MIN_SIZE (((SJME_SIZEOF_ALLOC_POOL(0) + \
26 (SJME_SIZEOF_ALLOC_LINK(0) * 3)) | 0x1FF))
28 /** The minimum size for splits. */
29 #define SJME_ALLOC_SPLIT_MIN_SIZE 64
31 /** The front guard value. */
32 #define SJME_ALLOC_GUARD_FRONT INT32_C(0x53716B21)
34 /** The back guard value. */
35 #define SJME_ALLOC_GUARD_BACK INT32_C(0x6C65783F)
37 #if defined(SJME_CONFIG_DEBUG)
38 /**
39 * Prints information on a given link and returns.
41 * @param pool The pool this is in.
42 * @param atLink The link to print info for.
43 * @param trigger The trigger for the failure.
44 * @return Always @c SJME_JNI_TRUE .
45 * @since 2023/12/29
47 static sjme_inline sjme_jboolean sjme_alloc_corruptFail(
48 volatile sjme_alloc_pool* pool,
49 volatile sjme_alloc_link* atLink,
50 const char* trigger)
52 sjme_message("Corrupted Link %p: %s", atLink, trigger);
54 /* Ignore if null. */
55 if (atLink == NULL)
56 return SJME_JNI_TRUE;
58 /* Dump everything about the link. */
59 sjme_message("link->guardFront: %08x", atLink->guardFront);
60 sjme_message("link->pool: %p (should be %p)", atLink->pool, pool);
61 sjme_message("link->prev: %p", atLink->prev);
62 sjme_message("link->next: %p", atLink->next);
63 if (atLink->space == SJME_ALLOC_POOL_SPACE_USED)
64 sjme_message("link->space: USED");
65 else if (atLink->space == SJME_ALLOC_POOL_SPACE_FREE)
66 sjme_message("link->space: FREE");
67 else if (atLink->space == SJME_NUM_ALLOC_POOL_SPACE)
68 sjme_message("link->space: NUM");
69 else
70 sjme_message("link->space: %d", (int)atLink->space);
71 sjme_message("link->weak: %p", atLink->weak);
72 sjme_message("link->freePrev: %p", atLink->freePrev);
73 sjme_message("link->freeNext: %p", atLink->freeNext);
74 sjme_message("link->allocSize: %d", (int)atLink->allocSize);
75 sjme_message("link->blockSize: %d", (int)atLink->blockSize);
76 sjme_message("link->guardBack: %08x", atLink->guardBack);
78 /* Abort. */
79 if (sjme_debug_handlers != NULL && sjme_debug_handlers->abort != NULL)
80 sjme_debug_handlers->abort();
82 /* Always indicate failure here. */
83 return SJME_JNI_TRUE;
85 #else
86 /**
87 * Prints information on a given link and returns.
89 * @param pool The pool this is in.
90 * @param atLink The link to print info for.
91 * @param trigger The trigger for the failure.
92 * @return Always @c SJME_JNI_TRUE .
93 * @since 2023/12/29
95 #define sjme_alloc_corruptFail(pool, atLink, trigger) SJME_JNI_TRUE
96 #endif
98 static sjme_inline sjme_jboolean sjme_alloc_checkCorruptionRange(
99 sjme_alloc_pool* pool, uintptr_t poolStart, uintptr_t poolEnd,
100 sjme_alloc_link* atLink)
102 uintptr_t check;
104 /* Ignore null pointers. */
105 if (atLink == NULL)
106 return SJME_JNI_FALSE;
108 /* Nominal address of the check pointer. */
109 check = (uintptr_t)atLink;
111 /* Must be in range! */
112 if (check < poolStart || check >= poolEnd)
113 return sjme_alloc_corruptFail(pool, atLink,
114 "Out of range link");
116 /* Does not appear corrupt. */
117 return SJME_JNI_FALSE;
121 * Checks the integrity of the memory pool.
123 * @param pool The pool to check in.
124 * @param atLink The link of the pool.
125 * @return If there is corruption or not.
126 * @since 2023/12/29
128 static sjme_jboolean sjme_noOptimize sjme_alloc_checkCorruption(
129 volatile sjme_alloc_pool* pool,
130 volatile sjme_alloc_link* atLink)
132 uintptr_t poolStart, poolEnd;
134 if (pool == NULL)
135 return SJME_JNI_TRUE;
137 /* If no link is specified, ignore. */
138 if (atLink == NULL)
139 return SJME_JNI_FALSE;
141 /* Check front and back guards. */
142 if (atLink->guardFront != SJME_ALLOC_GUARD_FRONT)
143 return sjme_alloc_corruptFail(pool, atLink,
144 "Wrong front guard");
145 if (atLink->guardBack != SJME_ALLOC_GUARD_BACK)
146 return sjme_alloc_corruptFail(pool, atLink,
147 "Wrong back guard");
149 /* Link is in the wrong pool. */
150 if (atLink->pool != pool)
151 return sjme_alloc_corruptFail(pool, atLink,
152 "Wrong pool");
154 /* Allocation size larger than block? */
155 if (atLink->allocSize > atLink->blockSize)
156 return sjme_alloc_corruptFail(pool, atLink,
157 "Allocation size larger than block.");
159 /* Next link is in the wrong location? */
160 if (atLink->next != NULL && (uintptr_t)atLink->next !=
161 (uintptr_t)&atLink->block[atLink->blockSize])
162 return sjme_alloc_corruptFail(pool, atLink,
163 "Next not at block end");
165 /* Is front/end link? */
166 if (atLink == pool->frontLink || atLink == pool->backLink)
168 /* Link space incorrect? */
169 if (atLink->space != SJME_NUM_ALLOC_POOL_SPACE)
170 return sjme_alloc_corruptFail(pool, atLink,
171 "Front/Back link not in correct space");
173 /* Size is not zero? */
174 if (atLink->blockSize != 0 || atLink->allocSize != 0)
175 return sjme_alloc_corruptFail(pool, atLink,
176 "Front/back link sizes non-zero");
178 /* Does not appear corrupt. */
179 return SJME_JNI_FALSE;
182 /* Invalid block size? */
183 if (atLink->blockSize <= 0)
184 return sjme_alloc_corruptFail(pool, atLink,
185 "Zero or negative block size");
187 /* Used for checking the integrity of pointers. */
188 poolStart = (uintptr_t)pool;
189 poolEnd = (uintptr_t)&pool->block[pool->size];
191 /* Free link only. */
192 if (atLink->space == SJME_ALLOC_POOL_SPACE_FREE)
194 /* Check free links. */
195 if (sjme_alloc_checkCorruptionRange(pool, poolStart, poolEnd,
196 atLink->freePrev))
197 return sjme_alloc_corruptFail(pool, atLink,
198 "Corrupt freePrev");
199 if (sjme_alloc_checkCorruptionRange(pool, poolStart, poolEnd,
200 atLink->freeNext))
201 return sjme_alloc_corruptFail(pool, atLink,
202 "Corrupt freeNext");
205 /* Used link only. */
206 else if (atLink->space == SJME_ALLOC_POOL_SPACE_USED)
208 /* Zero or negative size. */
209 if (atLink->allocSize <= 0)
210 return sjme_alloc_corruptFail(pool, atLink,
211 "Zero/negative used allocSize");
213 /* Cannot have any free or previous links. */
214 if (atLink->freePrev != NULL || atLink->freeNext != NULL)
215 return sjme_alloc_corruptFail(pool, atLink,
216 "Used has free links");
219 /* Link space incorrect? */
220 else
221 return sjme_alloc_corruptFail(pool, atLink,
222 "Incorrect space");
224 /* Check common next links. */
225 if (sjme_alloc_checkCorruptionRange(pool, poolStart, poolEnd,
226 atLink->prev))
227 return sjme_alloc_corruptFail(pool, atLink,
228 "Corrupt prev");
229 if (sjme_alloc_checkCorruptionRange(pool, poolStart, poolEnd,
230 atLink->next))
231 return sjme_alloc_corruptFail(pool, atLink,
232 "Corrupt next");
234 /* Does not appear corrupt. */
235 return SJME_JNI_FALSE;
238 static sjme_errorCode sjme_alloc_getLinkOptional(
239 sjme_attrInNotNull sjme_pointer addr,
240 sjme_attrOutNotNull sjme_alloc_link** outLink,
241 sjme_attrInValue sjme_jboolean checkCorruption)
243 sjme_alloc_link* link;
245 if (addr == NULL || outLink == NULL)
246 return SJME_ERROR_NULL_ARGUMENTS;
248 /* Just need to do some reversing math. */
249 link = (sjme_alloc_link*)(((uintptr_t)addr) -
250 offsetof(sjme_alloc_link, block));
252 /* Check the integrity of the link. */
253 if (checkCorruption)
254 sjme_alloc_checkCorruption(link->pool, link);
256 /* Success! */
257 *outLink = link;
258 return SJME_ERROR_NONE;
261 sjme_errorCode sjme_noOptimize sjme_alloc_poolInitMalloc(
262 sjme_attrOutNotNull sjme_alloc_pool** outPool,
263 sjme_attrInPositive sjme_jint size)
265 sjme_pointer result;
266 sjme_jint useSize;
268 /* Make sure the size is not wonky. */
269 useSize = SJME_SIZEOF_ALLOC_POOL(size);
270 if (outPool == NULL || size <= SJME_ALLOC_MIN_SIZE || useSize <= 0 ||
271 size > useSize)
272 return SJME_ERROR_INVALID_ARGUMENT;
274 /* Attempt allocation. */
275 result = malloc(useSize);
276 if (!result)
277 return sjme_error_outOfMemory(NULL, useSize);
279 /* Use static pool initializer to set up structures. */
280 return sjme_alloc_poolInitStatic(outPool, result, useSize);
283 sjme_errorCode sjme_noOptimize sjme_alloc_poolInitStatic(
284 sjme_attrOutNotNull sjme_alloc_pool** outPool,
285 sjme_attrInNotNull sjme_pointer baseAddr,
286 sjme_attrInPositive sjme_jint size)
288 sjme_alloc_pool* pool;
289 sjme_alloc_link* frontLink;
290 sjme_alloc_link* midLink;
291 sjme_alloc_link* backLink;
292 sjme_alloc_link* specialParent;
294 if (outPool == NULL || baseAddr == NULL)
295 return SJME_ERROR_NULL_ARGUMENTS;
297 if (size <= SJME_ALLOC_MIN_SIZE)
298 return SJME_ERROR_INVALID_ARGUMENT;
300 /* Initialize memory to nothing. */
301 memset(baseAddr, 0, size);
303 /* Setup initial pool structure. */
304 pool = baseAddr;
305 pool->size = (size & (~7)) - SJME_SIZEOF_ALLOC_POOL(0);
307 /* Setup front link. */
308 frontLink = (sjme_pointer)&pool->block[0];
309 pool->frontLink = frontLink;
311 /* Setup back link. */
312 backLink = (sjme_pointer)&pool->block[pool->size -
313 SJME_SIZEOF_ALLOC_LINK(0)];
314 pool->backLink = backLink;
316 /* Setup middle link, which is between the two. */
317 midLink = (sjme_pointer)&frontLink->block[0];
318 midLink->prev = frontLink;
319 frontLink->next = midLink;
320 midLink->next = backLink;
321 backLink->prev = midLink;
323 /* Determine size of the middle link, which is free space. */
324 midLink->blockSize = (sjme_jint)((uintptr_t)backLink -
325 (uintptr_t)&midLink->block[0]);
327 /* The mid-link is considered free. */
328 midLink->space = SJME_ALLOC_POOL_SPACE_FREE;
330 /* The front and back links are in the "invalid" space. */
331 frontLink->space = SJME_NUM_ALLOC_POOL_SPACE;
332 backLink->space = SJME_NUM_ALLOC_POOL_SPACE;
334 /* Determine size that can and cannot be used. */
335 pool->space[SJME_ALLOC_POOL_SPACE_FREE].reserved =
336 SJME_SIZEOF_ALLOC_LINK(0);
337 pool->space[SJME_ALLOC_POOL_SPACE_FREE].usable = midLink->blockSize;
339 /* Link in the first and last actual blocks for the free chain. */
340 pool->freeFirstLink = frontLink;
341 frontLink->freeNext = midLink;
342 midLink->freePrev = frontLink;
343 pool->freeLastLink = backLink;
344 backLink->freePrev = midLink;
345 midLink->freeNext = backLink;
347 /* Guards for all links. */
348 frontLink->guardFront = SJME_ALLOC_GUARD_FRONT;
349 frontLink->guardBack = SJME_ALLOC_GUARD_BACK;
350 midLink->guardFront = SJME_ALLOC_GUARD_FRONT;
351 midLink->guardBack = SJME_ALLOC_GUARD_BACK;
352 backLink->guardFront = SJME_ALLOC_GUARD_FRONT;
353 backLink->guardBack = SJME_ALLOC_GUARD_BACK;
355 /* Link in pools. */
356 frontLink->pool = pool;
357 midLink->pool = pool;
358 backLink->pool = pool;
360 #if defined(SJME_CONFIG_DEBUG)
361 /* Debug source line init blocks. */
362 pool->frontLink->debugFile = "<FRONT LINK>";
363 pool->frontLink->debugLine = 1;
364 pool->frontLink->debugFunction = "<FRONT LINK>";
366 pool->backLink->debugFile = "<BACK LINK>";
367 pool->backLink->debugLine = 1;
368 pool->backLink->debugFunction = "<BACK LINK>";
369 #endif
371 #if defined(SJME_CONFIG_HAS_VALGRIND)
372 /* Reserve front side in Valgrind. */
373 VALGRIND_MAKE_MEM_NOACCESS(baseAddr,
374 ((uintptr_t)&midLink->block[0] - (uintptr_t)baseAddr));
376 /* Reserve back side in Valgrind. */
377 VALGRIND_MAKE_MEM_NOACCESS(backLink,
378 (SJME_SIZEOF_ALLOC_LINK(0)));
379 #endif
381 /* If this is a valid link then we are allocating a nested pool. */
382 #if 0
383 specialParent = NULL;
384 if (!sjme_error_is(sjme_alloc_getLinkOptional(baseAddr,
385 &specialParent, SJME_JNI_FALSE)))
386 specialParent->flags |= SJME_ALLOC_LINK_FLAG_NESTED_POOL;
387 #endif
389 /* Use the pool. */
390 *outPool = pool;
391 return SJME_ERROR_NONE;
394 sjme_errorCode sjme_alloc_poolSpaceTotalSize(
395 sjme_attrInNotNull const sjme_alloc_pool* pool,
396 sjme_attrOutNullable sjme_jint* outTotal,
397 sjme_attrOutNullable sjme_jint* outReserved,
398 sjme_attrOutNullable sjme_jint* outUsable)
400 sjme_jint total, i;
401 sjme_jint reserved;
402 sjme_jint usable;
404 if (pool == NULL)
405 return SJME_ERROR_NULL_ARGUMENTS;
407 if (outTotal == NULL && outReserved == NULL && outUsable == NULL)
408 return SJME_ERROR_NULL_ARGUMENTS;
410 /* Run through and tally values for each space. */
411 reserved = 0;
412 usable = 0;
413 for (i = 0; i < SJME_NUM_ALLOC_POOL_SPACE; i++)
415 reserved += pool->space[i].reserved;
416 usable += pool->space[i].usable;
419 /* Total space is both. */
420 total = reserved + usable;
422 /* Store output values. */
423 if (outTotal != NULL)
424 *outTotal = total;
425 if (outReserved != NULL)
426 *outReserved = reserved;
427 if (outUsable != NULL)
428 *outUsable = usable;
430 /* Success! */
431 return SJME_ERROR_NONE;
434 sjme_errorCode sjme_noOptimize SJME_DEBUG_IDENTIFIER(sjme_alloc)(
435 sjme_attrInNotNull volatile sjme_alloc_pool* pool,
436 sjme_attrInPositiveNonZero sjme_jint size,
437 sjme_attrOutNotNull sjme_pointer* outAddr
438 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL)
440 sjme_errorCode error;
441 sjme_alloc_link* scanLink;
442 sjme_alloc_link* rightLink;
443 sjme_jint splitMinSize, roundSize;
444 sjme_jboolean splitBlock;
445 sjme_alloc_pool* nextPool;
446 volatile sjme_alloc_link* nextFree;
448 if (pool == NULL || size <= 0 || outAddr == NULL)
449 return SJME_ERROR_NULL_ARGUMENTS;
451 #if defined(SJME_CONFIG_DEBUG)
452 if ((size * 8) == SJME_CONFIG_HAS_POINTER)
453 sjme_message("Alloc of single pointer in %s (%s:%d).",
454 func, file, line);
455 #endif
457 /* Determine the size this will actually take up, which includes the */
458 /* link to be created following this. */
459 roundSize = (((size & 7) != 0) ? ((size | 7) + 1) : size);
460 splitMinSize = roundSize +
461 (sjme_jint)SJME_SIZEOF_ALLOC_LINK(SJME_ALLOC_SPLIT_MIN_SIZE) +
462 (sjme_jint)SJME_SIZEOF_ALLOC_LINK(0);
463 if (size > splitMinSize || splitMinSize < 0)
464 return SJME_ERROR_INVALID_ARGUMENT;
466 /* Take ownership of lock. */
467 if (sjme_error_is(error = sjme_thread_spinLockGrab(
468 &pool->spinLock)))
469 return sjme_error_default(error);
471 /* Emit barrier. */
472 sjme_thread_barrier();
474 /* Find the first free link that this fits in. */
475 scanLink = NULL;
476 splitBlock = SJME_JNI_FALSE;
477 for (scanLink = pool->freeFirstLink;
478 scanLink != NULL; scanLink = scanLink->freeNext)
480 /* Has memory been corrupted? */
481 nextFree = scanLink->freeNext;
482 if (sjme_alloc_checkCorruption(pool, scanLink) ||
483 (nextFree != NULL &&
484 sjme_alloc_checkCorruption(pool, nextFree)))
486 error = SJME_ERROR_MEMORY_CORRUPTION;
487 goto fail_corrupt;
490 /* Block is in the "invalid" space, skip it. */
491 if (scanLink->space == SJME_NUM_ALLOC_POOL_SPACE)
492 continue;
494 /* Block fits perfectly here, without needing a split? */
495 if (scanLink->blockSize == roundSize)
496 break;
498 /* Block fits here when split, try to not split ridiculously small. */
499 if (scanLink->blockSize >= splitMinSize)
501 splitBlock = SJME_JNI_TRUE;
502 break;
506 /* Out of memory. */
507 if (scanLink == NULL)
509 /* If there is an adjacent pool, if allocation fails then we shall */
510 /* try the next pool, this means multiple pools can work together */
511 /* accordingly. */
512 nextPool = pool->nextPool;
513 if (nextPool != NULL)
515 /* Release ownership of lock. */
516 if (sjme_error_is(error = sjme_thread_spinLockRelease(
517 &pool->spinLock, NULL)))
518 return sjme_error_default(error);
520 #if defined(SJME_CONFIG_DEBUG)
521 return sjme_allocR(nextPool, size, outAddr,
522 file, line, func);
523 #else
524 return sjme_alloc(pool->nextPool, size, outAddr);
525 #endif
528 /* Otherwise fail! */
529 error = sjme_error_outOfMemory(pool, size);
530 goto fail_noMemory;
533 #if 0
534 /* Debug. */
535 sjme_message("Found link at %p: %d bytes, we need %d with split %d.",
536 scanLink, (int)scanLink->blockSize, (int)roundSize, (int)splitBlock);
537 #endif
539 /* Does this block need to be split? */
540 if (splitBlock)
542 /* Check for link corruption on the adjacent links. */
543 if (sjme_alloc_checkCorruption(pool, scanLink->next) ||
544 sjme_alloc_checkCorruption(pool, scanLink->prev) ||
545 sjme_alloc_checkCorruption(pool, scanLink->freeNext) ||
546 sjme_alloc_checkCorruption(pool, scanLink->freePrev))
548 error = SJME_ERROR_MEMORY_CORRUPTION;
549 goto fail_corrupt;
552 /* Make it so this block can actually fit in here. */
553 rightLink = (sjme_alloc_link*)&scanLink->block[roundSize];
555 /* Initialize block to remove any old data. */
556 memset(rightLink, 0, sizeof(*rightLink));
558 /* Guards for link. */
559 rightLink->guardFront = SJME_ALLOC_GUARD_FRONT;
560 rightLink->guardBack = SJME_ALLOC_GUARD_BACK;
562 /* Set the right link's pool accordingly. */
563 rightLink->pool = pool;
565 /* Make sure this block is marked as free. */
566 rightLink->space = SJME_ALLOC_POOL_SPACE_FREE;
568 /* Set size of the right link. */
569 rightLink->blockSize =
570 (sjme_jint)((intptr_t)&scanLink->block[scanLink->blockSize] -
571 (intptr_t)&rightLink->block[0]);
572 rightLink->allocSize = rightLink->blockSize;
574 /* Link in physical links. */
575 rightLink->next = scanLink->next;
576 rightLink->next->prev = rightLink;
577 scanLink->next = rightLink;
578 rightLink->prev = scanLink;
580 /* Link in free links. */
581 rightLink->freeNext = scanLink->freeNext;
582 rightLink->freeNext->freePrev = rightLink;
583 scanLink->freeNext = rightLink;
584 rightLink->freePrev = scanLink;
586 /* Set size of the left block. */
587 scanLink->blockSize =
588 (sjme_jint)((intptr_t)rightLink - (intptr_t)&scanLink->block[0]);
589 scanLink->allocSize = scanLink->blockSize;
591 /* Adjust reserved and usable space. */
592 pool->space[SJME_ALLOC_POOL_SPACE_FREE].reserved +=
593 SJME_SIZEOF_ALLOC_LINK(0);
594 pool->space[SJME_ALLOC_POOL_SPACE_FREE].usable -=
595 SJME_SIZEOF_ALLOC_LINK(0);
597 /* Make sure we did not cause corruption. */
598 if (sjme_alloc_checkCorruption(pool, scanLink) ||
599 sjme_alloc_checkCorruption(pool, rightLink))
601 error = SJME_ERROR_MEMORY_CORRUPTION;
602 goto fail_corrupt;
606 /* Setup block information. */
607 scanLink->space = SJME_ALLOC_POOL_SPACE_USED;
609 /* Unlink from free links. */
610 if (scanLink->freeNext != NULL)
611 scanLink->freeNext->freePrev = scanLink->freePrev;
612 if (scanLink->freePrev != NULL)
613 scanLink->freePrev->freeNext = scanLink->freeNext;
614 scanLink->freePrev = NULL;
615 scanLink->freeNext = NULL;
617 /* Use our given allocation size. */
618 scanLink->allocSize = size;
620 /* Adjust space that can actually be used for data. */
621 pool->space[SJME_ALLOC_POOL_SPACE_FREE].usable -= scanLink->blockSize;
622 pool->space[SJME_ALLOC_POOL_SPACE_USED].usable += scanLink->blockSize;
624 /* Since this block is claimed, the reserved space moves over. */
625 pool->space[SJME_ALLOC_POOL_SPACE_FREE].reserved -=
626 SJME_SIZEOF_ALLOC_LINK(0);
627 pool->space[SJME_ALLOC_POOL_SPACE_USED].reserved +=
628 SJME_SIZEOF_ALLOC_LINK(0);
630 #if defined(SJME_CONFIG_DEBUG)
631 /* Set debug info. */
632 scanLink->debugFile = file;
633 scanLink->debugLine = line;
634 scanLink->debugFunction = func;
635 #endif
637 /* Make sure we did not cause corruption. */
638 if (sjme_alloc_checkCorruption(pool, scanLink) ||
639 sjme_alloc_checkCorruption(pool, scanLink->prev) ||
640 sjme_alloc_checkCorruption(pool, scanLink->next))
642 error = SJME_ERROR_MEMORY_CORRUPTION;
643 goto fail_corrupt;
646 /* Emit barrier. */
647 sjme_thread_barrier();
649 /* Release ownership of lock. */
650 if (sjme_error_is(error = sjme_thread_spinLockRelease(
651 &pool->spinLock, NULL)))
652 return sjme_error_default(error);
654 /* Use the given link. */
655 *outAddr = &scanLink->block[0];
656 return SJME_ERROR_NONE;
658 fail_corrupt:
659 fail_noMemory:
660 /* Release ownership of lock before we leave. */
661 if (sjme_error_is(sjme_thread_spinLockRelease(
662 &pool->spinLock, NULL)))
663 return sjme_error_default(error);
665 return sjme_error_default(error);
668 sjme_errorCode SJME_DEBUG_IDENTIFIER(sjme_alloc_copy)(
669 sjme_attrInNotNull volatile sjme_alloc_pool* pool,
670 sjme_attrInPositiveNonZero sjme_jint size,
671 sjme_attrOutNotNull sjme_pointer* outAddr,
672 sjme_attrInNotNull sjme_pointer inAddr
673 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL)
675 sjme_errorCode error;
676 sjme_pointer dest;
678 if (pool == NULL || outAddr == NULL || inAddr == NULL)
679 return SJME_ERROR_NULL_ARGUMENTS;
681 /* Allocate new copy first. */
682 dest = NULL;
683 if (sjme_error_is(error = SJME_DEBUG_IDENTIFIER(sjme_alloc)(
684 pool, size, &dest
685 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_FILE_LINE_COPY)) || dest == NULL)
686 return sjme_error_default(error);
688 /* Copy over. */
689 memmove(dest, inAddr, size);
690 *outAddr = dest;
692 /* Success! */
693 return SJME_ERROR_NONE;
696 sjme_errorCode SJME_DEBUG_IDENTIFIER(sjme_alloc_format)(
697 sjme_attrInNotNull sjme_alloc_pool* inPool,
698 sjme_attrOutNotNull sjme_lpstr* outString,
699 SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL SJME_DEBUG_ONLY_COMMA
700 sjme_attrInNotNull sjme_attrFormatArg const char* format,
701 ...)
703 #define BUF_SIZE 512
704 char buf[BUF_SIZE];
705 va_list arg;
706 int len;
708 if (inPool == NULL || outString == NULL || format == NULL)
709 return SJME_ERROR_NULL_ARGUMENTS;
711 /* Start variable arguments. */
712 va_start(arg, format);
714 /* Format string to the buffer. */
715 memset(buf, 0, sizeof(buf));
716 vsnprintf(buf, BUF_SIZE - 1, format, arg);
718 /* Force to end with a NUL. */
719 buf[BUF_SIZE - 1] = 0;
721 /* End them. */
722 va_end(arg);
724 /* Calculate length of string for copying. */
725 len = strlen(buf);
727 /* Copy it. */
728 return SJME_DEBUG_IDENTIFIER(sjme_alloc_copy)(inPool, len + 1,
729 (sjme_pointer*)outString, buf
730 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_FILE_LINE_COPY);
731 #undef BUF_SIZE
734 static sjme_errorCode sjme_alloc_mergeFree(sjme_alloc_link* link)
736 sjme_alloc_pool* pool;
737 sjme_alloc_link* right;
738 sjme_alloc_link* oldRightFreeNext;
739 sjme_alloc_link* rightRight;
740 sjme_alloc_link* checkLeft;
741 sjme_jint addedSize;
743 if (link == NULL)
744 return SJME_ERROR_NULL_ARGUMENTS;
746 /* Need pool for all operations. */
747 pool = link->pool;
749 /* If the previous block is free, pivot to there. */
750 checkLeft = link->prev;
751 if (checkLeft->space == SJME_ALLOC_POOL_SPACE_FREE)
752 return sjme_alloc_mergeFree(checkLeft);
754 /* Is the block on the right a candidate for merge? */
755 right = link->next;
756 if (right->space != SJME_ALLOC_POOL_SPACE_FREE)
757 return SJME_ERROR_NONE;
759 /* We need the block after to relink. */
760 rightRight = right->next;
762 /* Disconnect in the middle. */
763 link->next = rightRight;
764 rightRight->prev = link;
766 /* Remove from the free chain. */
767 oldRightFreeNext = right->freeNext;
768 right->freePrev->freeNext = right->freeNext;
769 oldRightFreeNext->freePrev = right->freePrev;
771 /* Reclaim the right link data area. */
772 addedSize = right->blockSize + SJME_SIZEOF_ALLOC_LINK(0);
773 link->blockSize += addedSize;
775 /* Update pool sizes. */
776 pool->space[SJME_ALLOC_POOL_SPACE_FREE].usable += addedSize;
777 pool->space[SJME_ALLOC_POOL_SPACE_FREE].reserved -=
778 SJME_SIZEOF_ALLOC_LINK(0);
780 /* Synchronize allocation size. */
781 link->allocSize = link->blockSize;
783 /* Wipe next side block to remove any stale data. */
784 memset(right, 0, sizeof(*right));
786 /* Should not have corrupted the block. */
787 if (sjme_alloc_checkCorruption(pool, link) ||
788 sjme_alloc_checkCorruption(pool, link->prev) ||
789 sjme_alloc_checkCorruption(pool, link->next) ||
790 sjme_alloc_checkCorruption(pool, link->freePrev) ||
791 sjme_alloc_checkCorruption(pool, link->freeNext))
792 return SJME_ERROR_MEMORY_CORRUPTION;
794 /* We merged a block, so check again. */
795 return sjme_alloc_mergeFree(link);
798 sjme_errorCode sjme_noOptimize sjme_alloc_free(
799 sjme_attrInNotNull sjme_pointer addr)
801 sjme_alloc_link* link;
802 volatile sjme_alloc_pool* pool;
803 sjme_errorCode error;
804 sjme_alloc_weak weak;
806 if (addr == NULL)
807 return SJME_ERROR_NULL_ARGUMENTS;
809 /* Emit barrier. */
810 sjme_thread_barrier();
812 /* Get the link. */
813 link = NULL;
814 if (sjme_error_is(error = sjme_alloc_getLink(addr, &link)))
815 return sjme_error_default(error);
817 /* Get the pool we are in. */
818 pool = link->pool;
820 /* Take ownership of lock. */
821 if (sjme_error_is(error = sjme_thread_spinLockGrab(
822 &pool->spinLock)))
823 return sjme_error_default(error);
825 /* Check the integrity of the block before we free it. */
826 pool = link->pool;
827 if (sjme_alloc_checkCorruption(pool, link))
829 error = SJME_ERROR_MEMORY_CORRUPTION;
830 goto fail_corrupt;
833 /* If there is a weak reference, clear it. */
834 weak = link->weak;
835 if (weak != NULL)
837 /* Call enqueue handler. */
838 if (weak->enqueue != NULL)
839 if (sjme_error_is(error = weak->enqueue(weak,
840 weak->enqueueData, SJME_JNI_TRUE)))
842 /* Keeping a weak reference is not considered an error */
843 /* although at this point it has no effect. */
844 if (error != SJME_ERROR_ENQUEUE_KEEP_WEAK)
845 goto fail_weakEnqueueCall;
848 /* Remove everything. */
849 link->weak = NULL;
850 weak->link = NULL;
851 weak->pointer = NULL;
854 /* Mark block as free. */
855 link->space = SJME_ALLOC_POOL_SPACE_FREE;
857 /* Clear flags, if any. */
858 link->flags = 0;
860 /* Clear block memory so stale memory is not around. */
861 memset(&link->block[0], 0, link->blockSize);
863 /* Restore allocation size to block size. */
864 link->allocSize = link->blockSize;
866 #if defined(SJME_CONFIG_DEBUG)
867 /* Remove debug information. */
868 link->debugFile = NULL;
869 link->debugLine = 0;
870 link->debugFunction = NULL;
871 #endif
873 /* Link into free chain. */
874 link->freeNext = pool->freeFirstLink->freeNext;
875 pool->freeFirstLink->freeNext = link;
876 link->freeNext->freePrev = link;
877 link->freePrev = pool->freeFirstLink;
879 /* Merge together free blocks. */
880 if (sjme_error_is(error = sjme_alloc_mergeFree(link)))
881 goto fail_merge;
883 /* Emit barrier. */
884 sjme_thread_barrier();
886 /* Release ownership of lock. */
887 if (sjme_error_is(error = sjme_thread_spinLockRelease(
888 &pool->spinLock, NULL)))
889 return sjme_error_default(error);
891 /* Success! */
892 return SJME_ERROR_NONE;
894 /* Release ownership of lock. */
895 fail_corrupt:
896 fail_weakEnqueueCall:
897 fail_merge:
898 if (sjme_error_is(sjme_thread_spinLockRelease(
899 &pool->spinLock, NULL)))
900 return sjme_error_default(error);
902 return sjme_error_default(error);
905 sjme_errorCode sjme_alloc_getLink(
906 sjme_attrInNotNull sjme_pointer addr,
907 sjme_attrOutNotNull sjme_alloc_link** outLink)
909 return sjme_alloc_getLinkOptional(addr, outLink,
910 SJME_JNI_TRUE);
913 sjme_errorCode SJME_DEBUG_IDENTIFIER(sjme_alloc_realloc)(
914 sjme_attrInOutNotNull sjme_pointer* inOutAddr,
915 sjme_attrInPositive sjme_jint newSize
916 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL)
918 sjme_alloc_link* link;
919 sjme_pointer result;
920 sjme_pointer source;
921 sjme_jint limit;
922 sjme_errorCode error;
924 if (inOutAddr == NULL || *inOutAddr == NULL)
925 return SJME_ERROR_NULL_ARGUMENTS;
927 if (newSize < 0)
928 return SJME_ERROR_INDEX_OUT_OF_BOUNDS;
930 /* Emit barrier. */
931 sjme_thread_barrier();
933 /* Alias for free. */
934 source = *inOutAddr;
935 if (newSize == 0)
937 /* Just do a normal free of it since zero was requested. */
938 if (sjme_error_is(error = sjme_alloc_free(source)))
939 return sjme_error_default(error);
941 /* Clear pointer. */
942 *inOutAddr = NULL;
944 /* Success! */
945 return SJME_ERROR_NULL_ARGUMENTS;
948 /* Recover the link. */
949 link = NULL;
950 if (sjme_error_is(error = sjme_alloc_getLink(source,
951 &link)) || link == NULL)
952 return sjme_error_default(error);
954 /* If there is a weak reference, then we cannot touch this. */
955 if (link->weak != NULL)
956 return SJME_ERROR_WEAK_REFERENCE_ATTACHED;
958 /* Pointless operation. */
959 if (newSize == link->allocSize)
960 return SJME_ERROR_NONE;
962 /* There are some padding bytes we can consume. */
963 else if (newSize > link->allocSize && newSize < link->blockSize)
965 /* Just set the new allocation size. */
966 link->allocSize = newSize;
968 /* Emit barrier. */
969 sjme_thread_barrier();
971 /* Success! */
972 return SJME_ERROR_NONE;
975 /* No space to grow or shrink, move it. */
976 else
978 /* How much do we actually want to copy? */
979 if (newSize < link->allocSize)
980 limit = newSize;
981 else
982 limit = link->allocSize;
984 /* Debug. */
985 sjme_message("Realloc copy %d -> %d (%d)",
986 link->allocSize, newSize, limit);
988 /* Allocate new block. */
989 result = NULL;
990 if (sjme_error_is(error = SJME_DEBUG_IDENTIFIER(sjme_alloc)(
991 link->pool, newSize, &result
992 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_FILE_LINE_COPY)) ||
993 result == NULL)
994 return sjme_error_defaultOr(error,
995 sjme_error_outOfMemory(link->pool, limit));
997 /* Copy all the data over. */
998 memmove(result, source, limit);
1000 /* Free the old block. */
1001 if (sjme_error_is(error = sjme_alloc_free(source)))
1002 return sjme_error_default(error);
1004 /* Emit barrier. */
1005 sjme_thread_barrier();
1007 /* Success! */
1008 *inOutAddr = result;
1009 return SJME_ERROR_NONE;
1013 sjme_errorCode SJME_DEBUG_IDENTIFIER(sjme_alloc_strdup)(
1014 sjme_attrInNotNull sjme_alloc_pool* inPool,
1015 sjme_attrOutNotNull sjme_lpcstr* outString,
1016 sjme_attrInNotNull sjme_lpcstr stringToCopy
1017 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL)
1019 sjme_jint charLen;
1021 if (inPool == NULL || outString == NULL || stringToCopy == NULL)
1022 return SJME_ERROR_NULL_ARGUMENTS;
1024 /* Use standard string length, include NUL. */
1025 charLen = strlen(stringToCopy) + 1;
1027 /* Then just forward to copy. */
1028 #if defined(SJME_CONFIG_DEBUG)
1029 return sjme_alloc_copyR(inPool, charLen,
1030 (sjme_pointer*)outString, (sjme_pointer)stringToCopy,
1031 file, line, func);
1032 #else
1033 return sjme_alloc_copy(inPool, charLen,
1034 (sjme_pointer*)outString, (sjme_pointer)stringToCopy);
1035 #endif
1038 sjme_errorCode sjme_noOptimize sjme_alloc_weakDelete(
1039 sjme_attrInOutNotNull sjme_alloc_weak* inOutWeak)
1041 sjme_errorCode error;
1042 sjme_alloc_weak weak;
1043 sjme_jint count;
1044 sjme_jboolean keepWeak;
1045 sjme_alloc_link* link;
1046 sjme_pointer block;
1048 if (inOutWeak == NULL)
1049 return SJME_ERROR_NULL_ARGUMENTS;
1051 /* Operate on this weak. */
1052 weak = *inOutWeak;
1054 /* Already free? */
1055 if (weak == NULL)
1056 return SJME_ERROR_NONE;
1058 /* Emit barrier. */
1059 sjme_thread_barrier();
1061 /* Get the current count. */
1062 count = sjme_atomic_sjme_jint_get(&weak->count);
1064 /* If zero is reached, it is eligible for free. */
1065 /* Provided, the data is still there. */
1066 link = weak->link;
1067 block = weak->pointer;
1068 if (count <= 1 && link != NULL && block != NULL)
1070 /* Call enqueue handler if it exists. */
1071 keepWeak = SJME_JNI_FALSE;
1072 if (weak->enqueue != NULL)
1073 if (sjme_error_is(error = weak->enqueue(weak,
1074 weak->enqueueData, SJME_JNI_FALSE)))
1076 /* Only fail if we are not keeping it. */
1077 keepWeak = (error == SJME_ERROR_ENQUEUE_KEEP_WEAK);
1078 if (!keepWeak)
1079 return sjme_error_default(error);
1082 /* Clear weak count to zero. */
1083 sjme_atomic_sjme_jint_set(&weak->count, 0);
1085 /* Unlink weak reference from block. */
1086 /* So that enqueue is not called multiple times. */
1087 link->weak = NULL;
1088 weak->link = NULL;
1089 weak->pointer = NULL;
1091 /* Free the block we point to. */
1092 if (sjme_error_is(error = sjme_alloc_free(block)))
1093 return sjme_error_default(error);
1095 /* Delete the weak reference as well? */
1096 if (!keepWeak)
1098 /* Inform our pointer that it is gone. */
1099 *inOutWeak = NULL;
1101 /* Free the weak reference itself. */
1102 if (sjme_error_is(error = sjme_alloc_free(weak)))
1103 return sjme_error_default(error);
1107 /* Otherwise, just count it down. */
1108 else if (count > 1)
1109 sjme_atomic_sjme_jint_set(&weak->count, count - 1);
1111 /* Emit barrier. */
1112 sjme_thread_barrier();
1114 /* Success! */
1115 return SJME_ERROR_NONE;
1118 sjme_errorCode sjme_alloc_weakGetPointer(
1119 sjme_attrInNotNull sjme_alloc_weak inWeak,
1120 sjme_attrOutNotNull sjme_pointer* outPointer)
1122 if (inWeak == NULL || outPointer == NULL)
1123 return SJME_ERROR_NULL_ARGUMENTS;
1125 /* Emit barrier. */
1126 sjme_thread_barrier();
1128 if (inWeak->link == NULL || inWeak->pointer == NULL)
1129 *outPointer = NULL;
1130 else
1131 *outPointer = inWeak->pointer;
1133 /* Emit barrier. */
1134 sjme_thread_barrier();
1136 /* Success! */
1137 return SJME_ERROR_NONE;
1140 static sjme_errorCode sjme_noOptimize sjme_alloc_weakRefInternal(
1141 sjme_attrInNotNull sjme_pointer addr,
1142 sjme_attrOutNotNull sjme_alloc_weak* outWeak,
1143 sjme_attrInNullable sjme_alloc_weakEnqueueFunc inEnqueue,
1144 sjme_attrInNullable sjme_pointer inEnqueueData)
1146 sjme_errorCode error;
1147 sjme_alloc_link* link;
1148 sjme_alloc_weak result;
1150 if (addr == NULL || outWeak == NULL ||
1151 (inEnqueue == NULL && inEnqueueData != NULL))
1152 return SJME_ERROR_NULL_ARGUMENTS;
1154 /* Emit barrier. */
1155 sjme_thread_barrier();
1157 /* Recover the link. */
1158 link = NULL;
1159 if (sjme_error_is(error = sjme_alloc_getLink(addr,
1160 &link)) || link == NULL)
1161 return sjme_error_default(error);
1163 /* Is there already a weak reference? */
1164 result = link->weak;
1165 if (result != NULL)
1167 /* Enqueue can be set, but not overwritten. */
1168 if (inEnqueue != NULL)
1170 /* Set it? */
1171 if (result->enqueue == NULL)
1173 result->enqueue = inEnqueue;
1174 result->enqueueData = inEnqueueData;
1177 /* Must be the same function. */
1178 else if (result->enqueue != inEnqueue ||
1179 result->enqueueData != inEnqueueData)
1180 return SJME_ERROR_ENQUEUE_ALREADY_SET;
1183 /* Count up. */
1184 sjme_atomic_sjme_jint_getAdd(&result->count, 1);
1186 /* Emit barrier. */
1187 sjme_thread_barrier();
1189 /* Use it. */
1190 *outWeak = result;
1191 return SJME_ERROR_NONE;
1194 /* We need to allocate the link. */
1195 if (sjme_error_is(error = sjme_alloc(link->pool, sizeof(*result),
1196 (sjme_pointer*)&result)))
1197 return sjme_error_default(error);
1199 /* Setup link information. */
1200 sjme_atomic_sjme_jint_set(&result->valid,
1201 SJME_ALLOC_WEAK_VALID);
1202 result->link = link;
1203 result->pointer = addr;
1204 result->enqueue = inEnqueue;
1205 result->enqueueData = inEnqueueData;
1206 sjme_atomic_sjme_jint_set(&result->count, 1);
1208 /* Join link back to this. */
1209 link->weak = result;
1211 /* Emit barrier. */
1212 sjme_thread_barrier();
1214 /* Success! */
1215 *outWeak = result;
1216 return SJME_ERROR_NONE;
1219 sjme_errorCode sjme_noOptimize SJME_DEBUG_IDENTIFIER(sjme_alloc_weakNew)(
1220 sjme_attrInNotNull volatile sjme_alloc_pool* inPool,
1221 sjme_attrInPositiveNonZero sjme_jint size,
1222 sjme_attrInNullable sjme_alloc_weakEnqueueFunc inEnqueue,
1223 sjme_attrInNullable sjme_pointer inEnqueueData,
1224 sjme_attrOutNotNull sjme_pointer* outAddr,
1225 sjme_attrOutNotNull sjme_alloc_weak* outWeak
1226 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL)
1228 sjme_pointer resultPtr;
1229 sjme_alloc_weak resultWeak;
1230 sjme_errorCode error;
1232 if (inPool == NULL || outAddr == NULL ||
1233 (inEnqueueData != NULL && inEnqueue == NULL))
1234 return SJME_ERROR_NULL_ARGUMENTS;
1236 /* Take ownership of lock. */
1237 if (sjme_error_is(error = sjme_thread_spinLockGrab(
1238 &inPool->spinLock)))
1239 return sjme_error_default(error);
1241 /* Emit barrier. */
1242 sjme_thread_barrier();
1244 /* Attempt block allocation first. */
1245 resultPtr = NULL;
1246 #if defined(SJME_CONFIG_DEBUG)
1247 if (sjme_error_is(error = sjme_allocR(inPool, size,
1248 (sjme_pointer*)&resultPtr, file, line, func)) ||
1249 resultPtr == NULL)
1250 #else
1251 if (sjme_error_is(error = sjme_alloc(inPool, size,
1252 (sjme_pointer*)&resultPtr)) ||
1253 resultPtr == NULL)
1254 #endif
1255 goto fail_allocBlock;
1257 /* Then create the weak reference. */
1258 resultWeak = NULL;
1259 if (sjme_error_is(error = sjme_alloc_weakRefInternal(resultPtr,
1260 &resultWeak, inEnqueue, inEnqueueData)) || resultWeak == NULL)
1261 goto fail_allocWeak;
1263 /* Emit barrier. */
1264 sjme_thread_barrier();
1266 /* Release ownership of lock. */
1267 if (sjme_error_is(error = sjme_thread_spinLockRelease(
1268 &inPool->spinLock, NULL)))
1269 return sjme_error_default(error);
1271 /* Success! */
1272 *outAddr = resultPtr;
1273 if (outWeak != NULL)
1274 *outWeak = resultWeak;
1275 return SJME_ERROR_NONE;
1277 fail_allocWeak:
1278 fail_allocBlock:
1279 if (resultPtr != NULL)
1280 sjme_alloc_free(resultPtr);
1282 /* Release ownership of lock. */
1283 if (sjme_error_is(sjme_thread_spinLockRelease(
1284 &inPool->spinLock, NULL)))
1285 return sjme_error_default(error);
1287 return sjme_error_default(error);
1290 sjme_errorCode sjme_alloc_weakRef(
1291 sjme_attrInNotNull sjme_pointer addr,
1292 sjme_attrOutNotNull sjme_alloc_weak* outWeak,
1293 sjme_attrInNullable sjme_alloc_weakEnqueueFunc inEnqueue,
1294 sjme_attrInNullable sjme_pointer inEnqueueData)
1296 volatile sjme_alloc_pool* pool;
1297 sjme_errorCode error;
1298 sjme_alloc_link* link;
1300 if (addr == NULL || outWeak == NULL ||
1301 (inEnqueue == NULL && inEnqueueData != NULL))
1302 return SJME_ERROR_NULL_ARGUMENTS;
1304 /* Emit barrier. */
1305 sjme_thread_barrier();
1307 /* Recover the link. */
1308 link = NULL;
1309 if (sjme_error_is(error = sjme_alloc_getLink(addr,
1310 &link)) || link == NULL)
1311 return sjme_error_default(error);
1313 /* No weak reference here? */
1314 if (link->weak == NULL)
1315 return SJME_ERROR_NOT_WEAK_REFERENCE;
1317 /* Take ownership of lock. */
1318 pool = link->pool;
1319 if (sjme_error_is(error = sjme_thread_spinLockGrab(
1320 &pool->spinLock)))
1321 return sjme_error_default(error);
1323 /* Forward. */
1324 error = sjme_alloc_weakRefInternal(addr, outWeak, inEnqueue,
1325 inEnqueueData);
1327 /* Release ownership of lock. */
1328 if (sjme_error_is(sjme_thread_spinLockRelease(
1329 &pool->spinLock, NULL)))
1330 return sjme_error_default(error);
1332 return error;
1335 #if defined(SJME_CONFIG_DEBUG)
1337 sjme_errorCode sjme_alloc_poolDump(
1338 sjme_attrInNotNull sjme_alloc_pool* inPool)
1340 sjme_alloc_link* rover;
1342 if (inPool == NULL)
1343 return SJME_ERROR_NULL_ARGUMENTS;
1345 /* Dump information on every link. */
1346 for (rover = inPool->frontLink; rover != NULL; rover = rover->next)
1348 sjme_messageR(NULL, -1, NULL,
1349 SJME_JNI_TRUE,
1350 "Link %08p: %s %dB in %s (%s:%d)",
1351 rover,
1352 (rover->space == SJME_ALLOC_POOL_SPACE_USED ? "USED" : "FREE"),
1353 rover->blockSize,
1354 rover->debugFunction,
1355 rover->debugFile,
1356 rover->debugLine);
1359 return SJME_ERROR_NONE;
1362 #endif