| /* | 
 |  * sort_list: a stable sort for lists. | 
 |  * | 
 |  * Time complexity: O(n*log n) | 
 |  *   [assuming limited zero-element fragments] | 
 |  * | 
 |  * Space complexity: O(1). | 
 |  * | 
 |  * Stable: yes. | 
 |  */ | 
 |  | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 |  | 
 | #include "lib.h" | 
 | #include "allocate.h" | 
 |  | 
 | #undef PARANOIA | 
 | #undef COVERAGE | 
 |  | 
 | #ifdef PARANOIA | 
 | #include <assert.h> | 
 | #else | 
 | #define assert(x) | 
 | #endif | 
 |  | 
 | #ifdef COVERAGE | 
 | static unsigned char been_there[256]; | 
 | #define BEEN_THERE(_c)					\ | 
 |   do {							\ | 
 | 	if (!been_there[_c]) {				\ | 
 | 		been_there[_c] = 1;			\ | 
 | 		printf ("Been there: %c\n", _c);	\ | 
 | 	}						\ | 
 |   } while (0) | 
 | #else | 
 | #define BEEN_THERE(_c) do { } while (0) | 
 | #endif | 
 |  | 
 | // Sort one fragment.  LIST_NODE_NR (==29) is a bit too high for my | 
 | // taste for something this simple.  But, hey, it's O(1). | 
 | // | 
 | // I would use libc qsort for this, but its comparison function | 
 | // gets a pointer indirection extra. | 
 | static void array_sort(void **ptr, int nr, int (*cmp)(const void *, const void *)) | 
 | { | 
 | 	int i; | 
 | 	for (i = 1; i < nr; i++) { | 
 | 		void *p = ptr[i]; | 
 | 		if (cmp(ptr[i-1],p) > 0) { | 
 | 			int j = i; | 
 | 			do { | 
 | 				ptr[j] = ptr[j-1]; | 
 | 				if (!--j) | 
 | 					break; | 
 | 			} while (cmp(ptr[j-1], p) > 0); | 
 | 			ptr[j] = p; | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | #ifdef PARANOIA | 
 | static void verify_seq_sorted (struct ptr_list *l, int n, | 
 | 			       int (*cmp)(const void *, const void *)) | 
 | { | 
 | 	int i = 0; | 
 | 	const void *a; | 
 | 	struct ptr_list *head = l; | 
 |  | 
 | 	while (l->nr == 0) { | 
 | 		l = l->next; | 
 | 		if (--n == 0) | 
 | 			return; | 
 | 		assert (l != head); | 
 | 	} | 
 |  | 
 | 	a = l->list[0]; | 
 | 	while (n > 0) { | 
 | 		const void *b; | 
 | 		if (++i >= l->nr) { | 
 | 			i = 0; | 
 | 			l = l->next; | 
 | 			n--; | 
 | 			assert (l != head || n == 0); | 
 | 			continue; | 
 | 		} | 
 | 		b = l->list[i]; | 
 | 		assert (cmp (a, b) <= 0); | 
 | 		a = b; | 
 | 	} | 
 | } | 
 | #endif | 
 |  | 
 |  | 
 | #define FLUSH_TO(b)						\ | 
 |   do {								\ | 
 | 	int nr = (b)->nr;					\ | 
 | 	assert (nbuf >= nr);					\ | 
 | 	memcpy ((b)->list, buffer, nr * sizeof (void *));	\ | 
 | 	nbuf -= nr;						\ | 
 | 	memcpy (buffer, buffer + nr, nbuf * sizeof (void *));	\ | 
 |   } while (0) | 
 |  | 
 | #define DUMP_TO(b)						\ | 
 |   do {								\ | 
 |         assert (nbuf <= (b)->nr);				\ | 
 | 	memcpy ((b)->list, buffer, nbuf * sizeof (void *));	\ | 
 |   } while (0) | 
 |  | 
 |  | 
 | // Merge two already-sorted sequences of blocks: | 
 | //   (b1_1, ..., b1_n)  and  (b2_1, ..., b2_m) | 
 | // Since we may be moving blocks around, we return the new head | 
 | // of the merged list. | 
 | static struct ptr_list * | 
 | merge_block_seqs (struct ptr_list *b1, int n, | 
 | 		  struct ptr_list *b2, int m, | 
 | 		  int (*cmp)(const void *, const void *)) | 
 | { | 
 | 	int i1 = 0, i2 = 0; | 
 | 	const void *buffer[2 * LIST_NODE_NR]; | 
 | 	int nbuf = 0; | 
 | 	struct ptr_list *newhead = b1; | 
 |  | 
 | 	// printf ("Merging %d blocks at %p with %d blocks at %p\n", n, b1, m, b2); | 
 |  | 
 | 	// Skip empty blocks in b2. | 
 | 	while (b2->nr == 0) { | 
 | 		BEEN_THERE('F'); | 
 | 		b2 = b2->next; | 
 | 		if (--m == 0) { | 
 | 			BEEN_THERE('G'); | 
 | 			return newhead; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	// Do a quick skip in case entire blocks from b1 are | 
 | 	// already less than smallest element in b2. | 
 | 	while (b1->nr == 0 || | 
 | 	       cmp (PTR_ENTRY(b1, b1->nr - 1), PTR_ENTRY(b2,0)) < 0) { | 
 | 		// printf ("Skipping whole block.\n"); | 
 | 		BEEN_THERE('H'); | 
 | 		b1 = b1->next; | 
 | 		if (--n == 0) { | 
 | 			BEEN_THERE('I'); | 
 | 			return newhead;	 | 
 | 		} | 
 | 	} | 
 |  | 
 | 	while (1) { | 
 | 		const void *d1 = PTR_ENTRY(b1,i1); | 
 | 		const void *d2 = PTR_ENTRY(b2,i2); | 
 |  | 
 | 		assert (i1 >= 0 && i1 < b1->nr); | 
 | 		assert (i2 >= 0 && i2 < b2->nr); | 
 | 		assert (b1 != b2); | 
 | 		assert (n > 0); | 
 | 		assert (m > 0); | 
 |  | 
 | 		if (cmp (d1, d2) <= 0) { | 
 | 			BEEN_THERE('J'); | 
 | 			buffer[nbuf++] = d1; | 
 | 			// Element from b1 is smaller | 
 | 			if (++i1 >= b1->nr) { | 
 | 				BEEN_THERE('L'); | 
 | 				FLUSH_TO(b1); | 
 | 				do { | 
 | 					b1 = b1->next; | 
 | 					if (--n == 0) { | 
 | 						BEEN_THERE('O'); | 
 | 						while (b1 != b2) { | 
 | 							BEEN_THERE('P'); | 
 | 							FLUSH_TO(b1); | 
 | 							b1 = b1->next; | 
 | 						} | 
 | 						assert (nbuf == i2); | 
 | 						DUMP_TO(b2); | 
 | 						return newhead; | 
 | 					} | 
 | 				} while (b1->nr == 0); | 
 | 				i1 = 0; | 
 | 			} | 
 | 		} else { | 
 | 			BEEN_THERE('K'); | 
 | 			// Element from b2 is smaller | 
 | 			buffer[nbuf++] = d2; | 
 | 			if (++i2 >= b2->nr) { | 
 | 				struct ptr_list *l = b2; | 
 | 				BEEN_THERE('M'); | 
 | 				// OK, we finished with b2.  Pull it out | 
 | 				// and plug it in before b1. | 
 |  | 
 | 				b2 = b2->next; | 
 | 				b2->prev = l->prev; | 
 | 				b2->prev->next = b2; | 
 | 				l->next = b1; | 
 | 				l->prev = b1->prev; | 
 | 				l->next->prev = l; | 
 | 				l->prev->next = l; | 
 |  | 
 | 				if (b1 == newhead) { | 
 | 					BEEN_THERE('N'); | 
 | 					newhead = l; | 
 | 				} | 
 |  | 
 | 				FLUSH_TO(l); | 
 | 				b2 = b2->prev; | 
 | 				do { | 
 | 					b2 = b2->next; | 
 | 					if (--m == 0) { | 
 | 						BEEN_THERE('Q'); | 
 | 						assert (nbuf == i1); | 
 | 						DUMP_TO(b1); | 
 | 						return newhead; | 
 | 					} | 
 | 				} while (b2->nr == 0); | 
 | 				i2 = 0; | 
 | 			} | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 |  | 
 | void sort_list(struct ptr_list **plist, int (*cmp)(const void *, const void *)) | 
 | { | 
 | 	struct ptr_list *head = *plist, *list = head; | 
 | 	int blocks = 1; | 
 |  | 
 | 	if (!head) | 
 | 		return; | 
 |  | 
 | 	// Sort all the sub-lists | 
 | 	do { | 
 | 		array_sort(list->list, list->nr, cmp); | 
 | #ifdef PARANOIA | 
 | 		verify_seq_sorted (list, 1, cmp); | 
 | #endif | 
 | 		list = list->next; | 
 | 	} while (list != head); | 
 |  | 
 | 	// Merge the damn things together | 
 | 	while (1) { | 
 | 		struct ptr_list *block1 = head; | 
 |  | 
 | 		do { | 
 | 			struct ptr_list *block2 = block1; | 
 | 			struct ptr_list *next, *newhead; | 
 | 			int i; | 
 |  | 
 | 			for (i = 0; i < blocks; i++) { | 
 | 				block2 = block2->next; | 
 | 				if (block2 == head) { | 
 | 					if (block1 == head) { | 
 | 						BEEN_THERE('A'); | 
 | 						*plist = head; | 
 | 						return; | 
 | 					} | 
 | 					BEEN_THERE('B'); | 
 | 					goto next_pass; | 
 | 				}						 | 
 | 			} | 
 |  | 
 | 			next = block2; | 
 | 			for (i = 0; i < blocks; ) { | 
 | 				next = next->next; | 
 | 				i++; | 
 | 				if (next == head) { | 
 | 					BEEN_THERE('C'); | 
 | 					break; | 
 | 				} | 
 | 				BEEN_THERE('D'); | 
 | 			} | 
 |  | 
 | 			newhead = merge_block_seqs (block1, blocks, | 
 | 						    block2, i, | 
 | 						    cmp); | 
 | #ifdef PARANOIA | 
 | 			verify_seq_sorted (newhead, blocks + i, cmp); | 
 | #endif | 
 | 			if (block1 == head) { | 
 | 				BEEN_THERE('E'); | 
 | 				head = newhead; | 
 | 			} | 
 | 			block1 = next; | 
 | 		} while (block1 != head); | 
 | 	next_pass: | 
 | 		blocks <<= 1; | 
 | 	} | 
 | } |