Index: libcfa/src/heap.cfa
===================================================================
--- libcfa/src/heap.cfa	(revision b2516e680a1351afc7324e6bbaf410c4fed4a14a)
+++ libcfa/src/heap.cfa	(revision 433905a9e171762b3a0680e099a6479705f8f04a)
@@ -10,6 +10,6 @@
 // Created On       : Tue Dec 19 21:58:35 2017
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Mon Apr 25 18:51:36 2022
-// Update Count     : 1147
+// Last Modified On : Fri Apr 29 19:05:03 2022
+// Update Count     : 1167
 //
 
@@ -87,163 +87,5 @@
 
 
-#ifdef __CFA_DEBUG__
-static size_t allocUnfreed;								// running total of allocations minus frees
-
-static void prtUnfreed() {
-	if ( allocUnfreed != 0 ) {
-		// DO NOT USE STREAMS AS THEY MAY BE UNAVAILABLE AT THIS POINT.
-		char helpText[512];
-		int len = snprintf( helpText, sizeof(helpText), "CFA warning (UNIX pid:%ld) : program terminating with %zu(0x%zx) bytes of storage allocated but not freed.\n"
-							"Possible cause is unfreed storage allocated by the program or system/library routines called from the program.\n",
-							(long int)getpid(), allocUnfreed, allocUnfreed ); // always print the UNIX pid
-		__cfaabi_bits_write( STDERR_FILENO, helpText, len ); // print debug/nodebug
-	} // if
-} // prtUnfreed
-
-extern int cfa_main_returned;							// from interpose.cfa
-extern "C" {
-	void heapAppStart() {								// called by __cfaabi_appready_startup
-		allocUnfreed = 0;
-	} // heapAppStart
-
-	void heapAppStop() {								// called by __cfaabi_appready_startdown
-		fclose( stdin ); fclose( stdout );
-		if ( cfa_main_returned ) prtUnfreed();			// do not check unfreed storage if exit called
-	} // heapAppStop
-} // extern "C"
-#endif // __CFA_DEBUG__
-
-
-// statically allocated variables => zero filled.
-static size_t heapExpand;								// sbrk advance
-static size_t mmapStart;								// cross over point for mmap
-static unsigned int maxBucketsUsed;						// maximum number of buckets in use
-// extern visibility, used by runtime kernel
-size_t __page_size;										// architecture pagesize
-int __map_prot;											// common mmap/mprotect protection
-
-
-#define SPINLOCK 0
-#define LOCKFREE 1
-#define BUCKETLOCK SPINLOCK
-#if BUCKETLOCK == SPINLOCK
-#elif BUCKETLOCK == LOCKFREE
-#include <stackLockFree.hfa>
-#else
-	#error undefined lock type for bucket lock
-#endif // LOCKFREE
-
-// Recursive definitions: HeapManager needs size of bucket array and bucket area needs sizeof HeapManager storage.
-// Break recursion by hardcoding number of buckets and statically checking number is correct after bucket array defined.
-enum { NoBucketSizes = 91 };							// number of buckets sizes
-
-struct Heap {
-	struct Storage {
-		struct Header {									// header
-			union Kind {
-				struct RealHeader {
-					union {
-						struct {						// 4-byte word => 8-byte header, 8-byte word => 16-byte header
-							union {
-								// 2nd low-order bit => zero filled, 3rd low-order bit => mmapped
-								// FreeHeader * home;		// allocated block points back to home locations (must overlay alignment)
-								void * home;			// allocated block points back to home locations (must overlay alignment)
-								size_t blockSize;		// size for munmap (must overlay alignment)
-								#if BUCKETLOCK == SPINLOCK
-								Storage * next;			// freed block points to next freed block of same size
-								#endif // SPINLOCK
-							};
-							size_t size;				// allocation size in bytes
-						};
-						#if BUCKETLOCK == LOCKFREE
-						Link(Storage) next;				// freed block points next freed block of same size (double-wide)
-						#endif // LOCKFREE
-					};
-				} real; // RealHeader
-
-				struct FakeHeader {
-					uintptr_t alignment;				// 1st low-order bit => fake header & alignment
-					uintptr_t offset;
-				} fake; // FakeHeader
-			} kind; // Kind
-		} header; // Header
-
-		char pad[libAlign() - sizeof( Header )];
-		char data[0];									// storage
-	}; // Storage
-
-	static_assert( libAlign() >= sizeof( Storage ), "minimum alignment < sizeof( Storage )" );
-
-	struct FreeHeader {
-		#if BUCKETLOCK == SPINLOCK
-		__spinlock_t lock;								// must be first field for alignment
-		Storage * freeList;
-		#else
-		StackLF(Storage) freeList;
-		#endif // BUCKETLOCK
-		size_t blockSize;								// size of allocations on this list
-	}; // FreeHeader
-
-	// must be first fields for alignment
-	__spinlock_t extlock;								// protects allocation-buffer extension
-	FreeHeader freeLists[NoBucketSizes];				// buckets for different allocation sizes
-
-	void * heapBegin;									// start of heap
-	void * heapEnd;										// logical end of heap
-	size_t heapRemaining;								// amount of storage not allocated in the current chunk
-}; // Heap
-
-#if BUCKETLOCK == LOCKFREE
-static inline {
-	Link(Heap.Storage) * ?`next( Heap.Storage * this ) { return &this->header.kind.real.next; }
-	void ?{}( Heap.FreeHeader & ) {}
-	void ^?{}( Heap.FreeHeader & ) {}
-} // distribution
-#endif // LOCKFREE
-
-static inline size_t getKey( const Heap.FreeHeader & freeheader ) { return freeheader.blockSize; }
-
-
-#ifdef FASTLOOKUP
-enum { LookupSizes = 65_536 + sizeof(Heap.Storage) }; // number of fast lookup sizes
-static unsigned char lookup[LookupSizes];				// O(1) lookup for small sizes
-#endif // FASTLOOKUP
-
-static const off_t mmapFd = -1;							// fake or actual fd for anonymous file
-#ifdef __CFA_DEBUG__
-static bool heapBoot = 0;								// detect recursion during boot
-#endif // __CFA_DEBUG__
-
-
-// Size of array must harmonize with NoBucketSizes and individual bucket sizes must be multiple of 16.
-// Smaller multiples of 16 and powers of 2 are common allocation sizes, so make them generate the minimum required bucket size.
-// malloc(0) returns 0p, so no bucket is necessary for 0 bytes returning an address that can be freed.
-static const unsigned int bucketSizes[] @= {			// different bucket sizes
-	16 + sizeof(Heap.Storage), 32 + sizeof(Heap.Storage), 48 + sizeof(Heap.Storage), 64 + sizeof(Heap.Storage), // 4
-	96 + sizeof(Heap.Storage), 112 + sizeof(Heap.Storage), 128 + sizeof(Heap.Storage), // 3
-	160, 192, 224, 256 + sizeof(Heap.Storage), // 4
-	320, 384, 448, 512 + sizeof(Heap.Storage), // 4
-	640, 768, 896, 1_024 + sizeof(Heap.Storage), // 4
-	1_536, 2_048 + sizeof(Heap.Storage), // 2
-	2_560, 3_072, 3_584, 4_096 + sizeof(Heap.Storage), // 4
-	6_144, 8_192 + sizeof(Heap.Storage), // 2
-	9_216, 10_240, 11_264, 12_288, 13_312, 14_336, 15_360, 16_384 + sizeof(Heap.Storage), // 8
-	18_432, 20_480, 22_528, 24_576, 26_624, 28_672, 30_720, 32_768 + sizeof(Heap.Storage), // 8
-	36_864, 40_960, 45_056, 49_152, 53_248, 57_344, 61_440, 65_536 + sizeof(Heap.Storage), // 8
-	73_728, 81_920, 90_112, 98_304, 106_496, 114_688, 122_880, 131_072 + sizeof(Heap.Storage), // 8
-	147_456, 163_840, 180_224, 196_608, 212_992, 229_376, 245_760, 262_144 + sizeof(Heap.Storage), // 8
-	294_912, 327_680, 360_448, 393_216, 425_984, 458_752, 491_520, 524_288 + sizeof(Heap.Storage), // 8
-	655_360, 786_432, 917_504, 1_048_576 + sizeof(Heap.Storage), // 4
-	1_179_648, 1_310_720, 1_441_792, 1_572_864, 1_703_936, 1_835_008, 1_966_080, 2_097_152 + sizeof(Heap.Storage), // 8
-	2_621_440, 3_145_728, 3_670_016, 4_194_304 + sizeof(Heap.Storage), // 4
-};
-
-static_assert( NoBucketSizes == sizeof(bucketSizes) / sizeof(bucketSizes[0] ), "size of bucket array wrong" );
-
-// The constructor for heapManager is called explicitly in memory_startup.
-static Heap heapManager __attribute__(( aligned (128) )) @= {}; // size of cache line to prevent false sharing
-
-
-//####################### Memory Allocation Routines Helpers ####################
+//####################### Heap Statistics ####################
 
 
@@ -307,5 +149,159 @@
 	return lhs;
 } // ?+=?
-
+#endif // __STATISTICS__
+
+
+#define SPINLOCK 0
+#define LOCKFREE 1
+#define BUCKETLOCK SPINLOCK
+#if BUCKETLOCK == SPINLOCK
+#elif BUCKETLOCK == LOCKFREE
+#include <stackLockFree.hfa>
+#else
+	#error undefined lock type for bucket lock
+#endif // LOCKFREE
+
+// Recursive definitions: HeapManager needs size of bucket array and bucket area needs sizeof HeapManager storage.
+// Break recursion by hardcoding number of buckets and statically checking number is correct after bucket array defined.
+enum { NoBucketSizes = 91 };							// number of buckets sizes
+
+struct Heap {
+	struct Storage {
+		struct Header {									// header
+			union Kind {
+				struct RealHeader {
+					union {
+						struct {						// 4-byte word => 8-byte header, 8-byte word => 16-byte header
+							union {
+								// 2nd low-order bit => zero filled, 3rd low-order bit => mmapped
+								// FreeHeader * home;		// allocated block points back to home locations (must overlay alignment)
+								void * home;			// allocated block points back to home locations (must overlay alignment)
+								size_t blockSize;		// size for munmap (must overlay alignment)
+								#if BUCKETLOCK == SPINLOCK
+								Storage * next;			// freed block points to next freed block of same size
+								#endif // SPINLOCK
+							};
+							size_t size;				// allocation size in bytes
+						};
+						#if BUCKETLOCK == LOCKFREE
+						Link(Storage) next;				// freed block points next freed block of same size (double-wide)
+						#endif // LOCKFREE
+					};
+				} real; // RealHeader
+
+				struct FakeHeader {
+					uintptr_t alignment;				// 1st low-order bit => fake header & alignment
+					uintptr_t offset;
+				} fake; // FakeHeader
+			} kind; // Kind
+		} header; // Header
+
+		char pad[libAlign() - sizeof( Header )];
+		char data[0];									// storage
+	}; // Storage
+
+	static_assert( libAlign() >= sizeof( Storage ), "minimum alignment < sizeof( Storage )" );
+
+	struct FreeHeader {
+		size_t blockSize __attribute__(( aligned (8) )); // size of allocations on this list
+		#if BUCKETLOCK == SPINLOCK
+		__spinlock_t lock;
+		Storage * freeList;
+		#else
+		StackLF(Storage) freeList;
+		#endif // BUCKETLOCK
+	} __attribute__(( aligned (8) )); // FreeHeader
+
+	FreeHeader freeLists[NoBucketSizes];				// buckets for different allocation sizes
+
+	__spinlock_t extlock;								// protects allocation-buffer extension
+	void * heapBegin;									// start of heap
+	void * heapEnd;										// logical end of heap
+	size_t heapRemaining;								// amount of storage not allocated in the current chunk
+}; // Heap
+
+#if BUCKETLOCK == LOCKFREE
+static inline {
+	Link(Heap.Storage) * ?`next( Heap.Storage * this ) { return &this->header.kind.real.next; }
+	void ?{}( Heap.FreeHeader & ) {}
+	void ^?{}( Heap.FreeHeader & ) {}
+} // distribution
+#endif // LOCKFREE
+
+static inline size_t getKey( const Heap.FreeHeader & freeheader ) { return freeheader.blockSize; }
+
+
+#ifdef FASTLOOKUP
+enum { LookupSizes = 65_536 + sizeof(Heap.Storage) }; // number of fast lookup sizes
+static unsigned char lookup[LookupSizes];				// O(1) lookup for small sizes
+#endif // FASTLOOKUP
+
+static const off_t mmapFd = -1;							// fake or actual fd for anonymous file
+#ifdef __CFA_DEBUG__
+static bool heapBoot = 0;								// detect recursion during boot
+#endif // __CFA_DEBUG__
+
+
+// Size of array must harmonize with NoBucketSizes and individual bucket sizes must be multiple of 16.
+// Smaller multiples of 16 and powers of 2 are common allocation sizes, so make them generate the minimum required bucket size.
+// malloc(0) returns 0p, so no bucket is necessary for 0 bytes returning an address that can be freed.
+static const unsigned int bucketSizes[] @= {			// different bucket sizes
+	16 + sizeof(Heap.Storage), 32 + sizeof(Heap.Storage), 48 + sizeof(Heap.Storage), 64 + sizeof(Heap.Storage), // 4
+	96 + sizeof(Heap.Storage), 112 + sizeof(Heap.Storage), 128 + sizeof(Heap.Storage), // 3
+	160, 192, 224, 256 + sizeof(Heap.Storage), // 4
+	320, 384, 448, 512 + sizeof(Heap.Storage), // 4
+	640, 768, 896, 1_024 + sizeof(Heap.Storage), // 4
+	1_536, 2_048 + sizeof(Heap.Storage), // 2
+	2_560, 3_072, 3_584, 4_096 + sizeof(Heap.Storage), // 4
+	6_144, 8_192 + sizeof(Heap.Storage), // 2
+	9_216, 10_240, 11_264, 12_288, 13_312, 14_336, 15_360, 16_384 + sizeof(Heap.Storage), // 8
+	18_432, 20_480, 22_528, 24_576, 26_624, 28_672, 30_720, 32_768 + sizeof(Heap.Storage), // 8
+	36_864, 40_960, 45_056, 49_152, 53_248, 57_344, 61_440, 65_536 + sizeof(Heap.Storage), // 8
+	73_728, 81_920, 90_112, 98_304, 106_496, 114_688, 122_880, 131_072 + sizeof(Heap.Storage), // 8
+	147_456, 163_840, 180_224, 196_608, 212_992, 229_376, 245_760, 262_144 + sizeof(Heap.Storage), // 8
+	294_912, 327_680, 360_448, 393_216, 425_984, 458_752, 491_520, 524_288 + sizeof(Heap.Storage), // 8
+	655_360, 786_432, 917_504, 1_048_576 + sizeof(Heap.Storage), // 4
+	1_179_648, 1_310_720, 1_441_792, 1_572_864, 1_703_936, 1_835_008, 1_966_080, 2_097_152 + sizeof(Heap.Storage), // 8
+	2_621_440, 3_145_728, 3_670_016, 4_194_304 + sizeof(Heap.Storage), // 4
+};
+
+static_assert( NoBucketSizes == sizeof(bucketSizes) / sizeof(bucketSizes[0] ), "size of bucket array wrong" );
+
+// The constructor for heapManager is called explicitly in memory_startup.
+static Heap heapManager __attribute__(( aligned (128) )) @= {}; // size of cache line to prevent false sharing
+
+
+//####################### Memory Allocation Routines Helpers ####################
+
+
+#ifdef __CFA_DEBUG__
+static size_t allocUnfreed;								// running total of allocations minus frees
+
+static void prtUnfreed() {
+	if ( allocUnfreed != 0 ) {
+		// DO NOT USE STREAMS AS THEY MAY BE UNAVAILABLE AT THIS POINT.
+		char helpText[512];
+		__cfaabi_bits_print_buffer( STDERR_FILENO, helpText, sizeof(helpText),
+									"CFA warning (UNIX pid:%ld) : program terminating with %zu(0x%zx) bytes of storage allocated but not freed.\n"
+									"Possible cause is unfreed storage allocated by the program or system/library routines called from the program.\n",
+									(long int)getpid(), allocUnfreed, allocUnfreed ); // always print the UNIX pid
+	} // if
+} // prtUnfreed
+
+extern int cfa_main_returned;							// from interpose.cfa
+extern "C" {
+	void heapAppStart() {								// called by __cfaabi_appready_startup
+		allocUnfreed = 0;
+	} // heapAppStart
+
+	void heapAppStop() {								// called by __cfaabi_appready_startdown
+		fclose( stdin ); fclose( stdout );
+		if ( cfa_main_returned ) prtUnfreed();			// do not check unfreed storage if exit called
+	} // heapAppStop
+} // extern "C"
+#endif // __CFA_DEBUG__
+
+
+#ifdef __STATISTICS__
 static HeapStatistics stats;							// zero filled
 static unsigned int sbrk_calls;
@@ -387,4 +383,13 @@
 
 
+// statically allocated variables => zero filled.
+static size_t heapExpand;								// sbrk advance
+static size_t mmapStart;								// cross over point for mmap
+static unsigned int maxBucketsUsed;						// maximum number of buckets in use
+// extern visibility, used by runtime kernel
+size_t __page_size;										// architecture pagesize
+int __map_prot;											// common mmap/mprotect protection
+
+
 // thunk problem
 size_t Bsearchl( unsigned int key, const unsigned int * vals, size_t dim ) {
@@ -490,6 +495,6 @@
 	} else {
 		fakeHeader( header, alignment );
-		if ( unlikely( MmappedBit( header ) ) ) {
-			assert( addr < heapBegin || heapEnd < addr );
+		if ( unlikely( MmappedBit( header ) ) ) {		// mmapped ?
+			verify( addr < heapBegin || heapEnd < addr );
 			size = ClearStickyBits( header->kind.real.blockSize ); // mmap size
 			return true;
@@ -503,9 +508,12 @@
 	checkHeader( header < (Heap.Storage.Header *)heapBegin || (Heap.Storage.Header *)heapEnd < header, name, addr ); // bad address ? (offset could be + or -)
 
-	if ( freeHead < &freeLists[0] || &freeLists[NoBucketSizes] <= freeHead ) {
-		abort( "Attempt to %s storage %p with corrupted header.\n"
- 			   "Possible cause is duplicate free on same block or overwriting of header information.",
- 			   name, addr );
- 	} // if
+	Heap * homeManager;
+	if ( unlikely( freeHead == 0p || // freed and only free-list node => null link
+				   // freed and link points at another free block not to a bucket in the bucket array.
+				   freeHead < &freeLists[0] || &freeLists[NoBucketSizes] <= freeHead ) ) {
+		abort( "**** Error **** attempt to %s storage %p with corrupted header.\n"
+			   "Possible cause is duplicate free on same block or overwriting of header information.",
+			   name, addr );
+	} // if
 	#endif // __CFA_DEBUG__
 
@@ -560,4 +568,5 @@
 		sbrk_storage += increase;
 		#endif // __STATISTICS__
+
 		#ifdef __CFA_DEBUG__
 		// Set new memory to garbage so subsequent uninitialized usages might fail.
@@ -565,4 +574,5 @@
 		//Memset( (char *)heapEnd + heapRemaining, increase );
 		#endif // __CFA_DEBUG__
+
 		rem = heapRemaining + increase - size;
 	} // if
@@ -651,8 +661,7 @@
 	__atomic_add_fetch( &allocUnfreed, tsize, __ATOMIC_SEQ_CST );
 	if ( traceHeap() ) {
-		enum { BufferSize = 64 };
-		char helpText[BufferSize];
-		int len = snprintf( helpText, BufferSize, "%p = Malloc( %zu ) (allocated %zu)\n", addr, size, tsize );
-		__cfaabi_bits_write( STDERR_FILENO, helpText, len ); // print debug/nodebug
+		char helpText[64];
+		__cfaabi_bits_print_buffer( STDERR_FILENO, helpText, sizeof(helpText),
+									"%p = Malloc( %zu ) (allocated %zu)\n", addr, size, tsize ); // print debug/nodebug
 	} // if
 	#endif // __CFA_DEBUG__
@@ -711,6 +720,6 @@
 	if ( traceHeap() ) {
 		char helpText[64];
-		int len = snprintf( helpText, sizeof(helpText), "Free( %p ) size:%zu\n", addr, size );
-		__cfaabi_bits_write( STDERR_FILENO, helpText, len ); // print debug/nodebug
+		__cfaabi_bits_print_buffer( STDERR_FILENO, helpText, sizeof(helpText),
+									"Free( %p ) size:%zu\n", addr, size ); // print debug/nodebug
 	} // if
 	#endif // __CFA_DEBUG__
