xref: /onnv-gate/usr/src/cmd/sort/common/internal.c (revision 5836:a07f85025dc3)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*5836Snakanon  * Common Development and Distribution License (the "License").
6*5836Snakanon  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*5836Snakanon  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #include "internal.h"
290Sstevel@tonic-gate 
300Sstevel@tonic-gate #define	INSERTION_THRESHOLD	12
310Sstevel@tonic-gate 
320Sstevel@tonic-gate static void
swap_range(int i,int j,int n,line_rec_t ** I)330Sstevel@tonic-gate swap_range(int i, int j, int n, line_rec_t **I)
340Sstevel@tonic-gate {
350Sstevel@tonic-gate 	while (n-- > 0) {
360Sstevel@tonic-gate 		line_rec_t *t;
370Sstevel@tonic-gate 
380Sstevel@tonic-gate 		t = I[i];
390Sstevel@tonic-gate 		I[i++] = I[j];
400Sstevel@tonic-gate 		I[j++] = t;
410Sstevel@tonic-gate 	}
420Sstevel@tonic-gate }
430Sstevel@tonic-gate 
440Sstevel@tonic-gate /*
450Sstevel@tonic-gate  * offset_is_algorithm() implements a simple insertion sort on line records that
460Sstevel@tonic-gate  * allows comparison from an offset into the l_collate field (since the subfiles
470Sstevel@tonic-gate  * should have already been sorted to that depth).
480Sstevel@tonic-gate  */
490Sstevel@tonic-gate static void
offset_is_algorithm(line_rec_t ** X,ssize_t n,int (* collate_fcn)(line_rec_t *,line_rec_t *,ssize_t,flag_t),ssize_t depth,flag_t coll_flags)500Sstevel@tonic-gate offset_is_algorithm(line_rec_t **X, ssize_t n,
510Sstevel@tonic-gate     int (*collate_fcn)(line_rec_t *, line_rec_t *, ssize_t, flag_t),
520Sstevel@tonic-gate     ssize_t depth, flag_t coll_flags)
530Sstevel@tonic-gate {
540Sstevel@tonic-gate 	ssize_t i;
550Sstevel@tonic-gate 
560Sstevel@tonic-gate 	__S(stats_incr_subfiles());
570Sstevel@tonic-gate 
580Sstevel@tonic-gate 	/*
590Sstevel@tonic-gate 	 * Place lowest element in X[0].
600Sstevel@tonic-gate 	 */
610Sstevel@tonic-gate 	for (i = 0; i < n; i++) {
620Sstevel@tonic-gate 		if (collate_fcn(X[0], X[i], depth, coll_flags) > 0) {
630Sstevel@tonic-gate 			swap((void **)&X[0], (void **)&X[i]);
640Sstevel@tonic-gate 			ASSERT(collate_fcn(X[0], X[i], depth, coll_flags) <= 0);
650Sstevel@tonic-gate 		}
660Sstevel@tonic-gate 	}
670Sstevel@tonic-gate 
680Sstevel@tonic-gate 	/*
690Sstevel@tonic-gate 	 * Insertion sort.
700Sstevel@tonic-gate 	 */
710Sstevel@tonic-gate 	for (i = 2; i < n; i++) {
720Sstevel@tonic-gate 		ssize_t j = i;
730Sstevel@tonic-gate 		line_rec_t *t = X[i];
740Sstevel@tonic-gate 		while (collate_fcn(t, X[j - 1], depth, coll_flags) < 0) {
750Sstevel@tonic-gate 			X[j] = X[j - 1];
760Sstevel@tonic-gate 			j--;
770Sstevel@tonic-gate 			ASSERT(j > 0);
780Sstevel@tonic-gate 		}
790Sstevel@tonic-gate 		X[j] = t;
800Sstevel@tonic-gate 	}
810Sstevel@tonic-gate }
820Sstevel@tonic-gate 
830Sstevel@tonic-gate /*
840Sstevel@tonic-gate  * tqs_algorithm() is called from rqs_algorithm() when a subpartition is
850Sstevel@tonic-gate  * encountered whose line records share indistinguishable l_collate fields.  It
860Sstevel@tonic-gate  * implements a semi-iterative ternary quicksort.
870Sstevel@tonic-gate  */
880Sstevel@tonic-gate static void
tqs_algorithm(line_rec_t ** X,ssize_t n,int (* collate_fcn)(line_rec_t *,line_rec_t *,ssize_t,flag_t),flag_t coll_flags)890Sstevel@tonic-gate tqs_algorithm(line_rec_t **X, ssize_t n,
900Sstevel@tonic-gate     int (*collate_fcn)(line_rec_t *, line_rec_t *, ssize_t, flag_t),
910Sstevel@tonic-gate     flag_t coll_flags)
920Sstevel@tonic-gate {
930Sstevel@tonic-gate 	ssize_t	l;			/* boundary of left partition */
940Sstevel@tonic-gate 	ssize_t	le;			/* boundary of left equal partition */
950Sstevel@tonic-gate 	ssize_t	r;			/* boundary of right partition */
960Sstevel@tonic-gate 	ssize_t	re;			/* boundary of right equal partition */
970Sstevel@tonic-gate 
980Sstevel@tonic-gate 	ssize_t	p, q;			/* scratch for indices, comparisons */
990Sstevel@tonic-gate 
1000Sstevel@tonic-gate 	coll_flags |= COLL_DATA_ONLY;
1010Sstevel@tonic-gate 
1020Sstevel@tonic-gate tqs_start:
1030Sstevel@tonic-gate 
1040Sstevel@tonic-gate 	/*
1050Sstevel@tonic-gate 	 * completion criteria
1060Sstevel@tonic-gate 	 */
1070Sstevel@tonic-gate 	if (n <= 1)
1080Sstevel@tonic-gate 		return;
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate 	if (n <= INSERTION_THRESHOLD) {
1110Sstevel@tonic-gate 		offset_is_algorithm(X, n, collate_fcn, 0, coll_flags);
1120Sstevel@tonic-gate 		return;
1130Sstevel@tonic-gate 	}
1140Sstevel@tonic-gate 
1150Sstevel@tonic-gate 	/*
1160Sstevel@tonic-gate 	 * selection of partition element
1170Sstevel@tonic-gate 	 */
1180Sstevel@tonic-gate 	le = rand() % n;
1190Sstevel@tonic-gate 	swap((void **)&X[0], (void **)&X[le]);
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate 	le = l = 1;
1220Sstevel@tonic-gate 	r = re = n - 1;
1230Sstevel@tonic-gate 
1240Sstevel@tonic-gate 	for (;;) {
1250Sstevel@tonic-gate 		while (l <= r &&
1260Sstevel@tonic-gate 		    (p = collate_fcn(X[l], X[0], 0, coll_flags)) <= 0) {
1270Sstevel@tonic-gate 			if (p == 0)
1280Sstevel@tonic-gate 				swap((void **)&X[le++], (void **)&X[l]);
1290Sstevel@tonic-gate 			l++;
1300Sstevel@tonic-gate 		}
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate 		while (l <= r &&
1330Sstevel@tonic-gate 		    (p = collate_fcn(X[r], X[0], 0, coll_flags)) >= 0) {
1340Sstevel@tonic-gate 			if (p == 0)
1350Sstevel@tonic-gate 				swap((void **)&X[r], (void **)&X[re--]);
1360Sstevel@tonic-gate 			r--;
1370Sstevel@tonic-gate 		}
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate 		if (l > r)
1400Sstevel@tonic-gate 			break;
1410Sstevel@tonic-gate 
1420Sstevel@tonic-gate 		swap((void **)&X[l++], (void **)&X[r--]);
1430Sstevel@tonic-gate 	}
1440Sstevel@tonic-gate 
1450Sstevel@tonic-gate 	/*
1460Sstevel@tonic-gate 	 * swap equal partitions into middle
1470Sstevel@tonic-gate 	 */
1480Sstevel@tonic-gate 	p = MIN(le, l - le);
1490Sstevel@tonic-gate 	swap_range(0, l - p, p, X);
1500Sstevel@tonic-gate 	p = MIN(re - r, n - re - 1);
1510Sstevel@tonic-gate 	swap_range(l, n - p, p, X);
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate 	/*
1540Sstevel@tonic-gate 	 * Iterate with larger subpartition, recurse into smaller.
1550Sstevel@tonic-gate 	 */
1560Sstevel@tonic-gate 	p = l - le;
1570Sstevel@tonic-gate 	q = re - r;
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate 	if (p > q) {
1600Sstevel@tonic-gate 		tqs_algorithm(&X[n - q], q, collate_fcn, coll_flags);
1610Sstevel@tonic-gate 
1620Sstevel@tonic-gate 		n = p;
1630Sstevel@tonic-gate 	} else {
1640Sstevel@tonic-gate 		tqs_algorithm(X, p, collate_fcn, coll_flags);
1650Sstevel@tonic-gate 
1660Sstevel@tonic-gate 		X = &X[n - q];
1670Sstevel@tonic-gate 		n = q;
1680Sstevel@tonic-gate 	}
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate 	goto tqs_start;
1710Sstevel@tonic-gate 	/*NOTREACHED*/
1720Sstevel@tonic-gate }
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate /*
1750Sstevel@tonic-gate  * The following semi-iterative radix quicksort is derived from that presented
1760Sstevel@tonic-gate  * in
1770Sstevel@tonic-gate  *	J. Bentley and R. Sedgewick, Fast Algorithms for Sorting and Searching
1780Sstevel@tonic-gate  *	Strings, in Eighth Annual ACM-SIAM Symposium on Discrete Algorithms,
1790Sstevel@tonic-gate  *	1997 (SODA 1997),
1800Sstevel@tonic-gate  * and
1810Sstevel@tonic-gate  *	R. Sedgewick, Algorithms in C, 3rd ed., vol. 1, Addison-Wesley, 1998.
1820Sstevel@tonic-gate  */
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate static void
rqs_algorithm(line_rec_t ** X,ssize_t n,ssize_t depth,int (* collate_fcn)(line_rec_t *,line_rec_t *,ssize_t,flag_t),flag_t coll_flags)1850Sstevel@tonic-gate rqs_algorithm(line_rec_t **X, ssize_t n, ssize_t depth,
1860Sstevel@tonic-gate     int (*collate_fcn)(line_rec_t *, line_rec_t *, ssize_t, flag_t),
1870Sstevel@tonic-gate     flag_t coll_flags)
1880Sstevel@tonic-gate {
1890Sstevel@tonic-gate 	uchar_t v;			/* partition radix value */
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate 	ssize_t	l;			/* boundary of left partition */
1920Sstevel@tonic-gate 	ssize_t	le;			/* boundary of left equal partition */
1930Sstevel@tonic-gate 	ssize_t	r;			/* boundary of right partition */
1940Sstevel@tonic-gate 	ssize_t	re;			/* boundary of right equal partition */
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate 	ssize_t	p;			/* scratch for indices, comparisons */
1970Sstevel@tonic-gate 	line_rec_t *t;			/* scratch for swaps */
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate rqs_start:
2000Sstevel@tonic-gate 
2010Sstevel@tonic-gate 	/*
2020Sstevel@tonic-gate 	 * completion criteria
2030Sstevel@tonic-gate 	 */
2040Sstevel@tonic-gate 	if (n <= 1)
2050Sstevel@tonic-gate 		return;
2060Sstevel@tonic-gate 
2070Sstevel@tonic-gate 	if (n <= INSERTION_THRESHOLD) {
2080Sstevel@tonic-gate 		offset_is_algorithm(X, n, collate_fcn, depth, coll_flags);
2090Sstevel@tonic-gate 		return;
2100Sstevel@tonic-gate 	}
2110Sstevel@tonic-gate 
2120Sstevel@tonic-gate 	/*
2130Sstevel@tonic-gate 	 * selection of partition element
2140Sstevel@tonic-gate 	 */
2150Sstevel@tonic-gate 	le = rand() % n;
2160Sstevel@tonic-gate 	swap((void **)&X[0], (void **)&X[le]);
2170Sstevel@tonic-gate 	v = X[0]->l_collate.usp[depth];
2180Sstevel@tonic-gate 
2190Sstevel@tonic-gate 	le = l = 1;
2200Sstevel@tonic-gate 	r = re = n - 1;
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate 	for (;;) {
2230Sstevel@tonic-gate 		while (l <= r &&
2240Sstevel@tonic-gate 		    (p = *(X[l]->l_collate.usp + depth) - v) <= 0) {
2250Sstevel@tonic-gate 			if (p == 0) {
2260Sstevel@tonic-gate 				t = X[le];
2270Sstevel@tonic-gate 				X[le] = X[l];
2280Sstevel@tonic-gate 				X[l] = t;
2290Sstevel@tonic-gate 				le++;
2300Sstevel@tonic-gate 			}
2310Sstevel@tonic-gate 			(l)++;
2320Sstevel@tonic-gate 		}
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate 		while (l <= r &&
2350Sstevel@tonic-gate 		    (p = *(X[r]->l_collate.usp + depth) - v) >= 0) {
2360Sstevel@tonic-gate 			if (p == 0) {
2370Sstevel@tonic-gate 				t = X[r];
2380Sstevel@tonic-gate 				X[r] = X[re];
2390Sstevel@tonic-gate 				X[re] = t;
2400Sstevel@tonic-gate 				(re)--;
2410Sstevel@tonic-gate 			}
2420Sstevel@tonic-gate 			(r)--;
2430Sstevel@tonic-gate 		}
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate 		if (l > r)
2460Sstevel@tonic-gate 			break;
2470Sstevel@tonic-gate 
2480Sstevel@tonic-gate 		t = X[l];
2490Sstevel@tonic-gate 		X[l] = X[r];
2500Sstevel@tonic-gate 		X[r] = t;
2510Sstevel@tonic-gate 		(l)++;
2520Sstevel@tonic-gate 		(r)--;
2530Sstevel@tonic-gate 	}
2540Sstevel@tonic-gate 
2550Sstevel@tonic-gate 	/*
2560Sstevel@tonic-gate 	 * swap equal partitions into middle
2570Sstevel@tonic-gate 	 */
2580Sstevel@tonic-gate 	p = MIN(le, l - le);
2590Sstevel@tonic-gate 	swap_range(0, l - p, p, X);
2600Sstevel@tonic-gate 	p = MIN(re - r, n - re - 1);
2610Sstevel@tonic-gate 	swap_range(l, n - p, p, X);
2620Sstevel@tonic-gate 
2630Sstevel@tonic-gate 	/*
2640Sstevel@tonic-gate 	 * recurse into subpartitions as necessary
2650Sstevel@tonic-gate 	 */
2660Sstevel@tonic-gate 	p = re - r;
2670Sstevel@tonic-gate 	if (p > 0)
2680Sstevel@tonic-gate 		rqs_algorithm(&X[n - p], p, depth, collate_fcn, coll_flags);
2690Sstevel@tonic-gate 
2700Sstevel@tonic-gate 	p = l - le;
2710Sstevel@tonic-gate 	if (p > 0)
2720Sstevel@tonic-gate 		rqs_algorithm(X, p, depth, collate_fcn, coll_flags);
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate 	if (le + n - re - 1 <= 1)
2750Sstevel@tonic-gate 		return;
2760Sstevel@tonic-gate 
2770Sstevel@tonic-gate 	/*
2780Sstevel@tonic-gate 	 * - 1 so that we don't count the final null.
2790Sstevel@tonic-gate 	 */
2800Sstevel@tonic-gate 	if (X[p]->l_collate_length - 1 > depth) {
2810Sstevel@tonic-gate 		/*
2820Sstevel@tonic-gate 		 * Equivalent recursion: rqs_algorithm(&X[p], le + n - re - 1,
2830Sstevel@tonic-gate 		 * depth + 1, collate_fcn, coll_only);
2840Sstevel@tonic-gate 		 */
2850Sstevel@tonic-gate 		X = &X[p];
2860Sstevel@tonic-gate 		n = le + n - re - 1;
2870Sstevel@tonic-gate 		depth++;
2880Sstevel@tonic-gate 		goto rqs_start;
2890Sstevel@tonic-gate 	}
2900Sstevel@tonic-gate 
2910Sstevel@tonic-gate 	if (!(coll_flags & COLL_UNIQUE)) {
2920Sstevel@tonic-gate 		__S(stats_incr_tqs_calls());
2930Sstevel@tonic-gate 		tqs_algorithm(&X[p], le + n - re - 1, collate_fcn, coll_flags);
2940Sstevel@tonic-gate 	}
2950Sstevel@tonic-gate }
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate static void
radix_quicksort(stream_t * C,flag_t coll_flags)2980Sstevel@tonic-gate radix_quicksort(stream_t *C, flag_t coll_flags)
2990Sstevel@tonic-gate {
3000Sstevel@tonic-gate 	ASSERT((C->s_status & STREAM_SOURCE_MASK) == STREAM_ARRAY);
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 	if (C->s_element_size == sizeof (char))
3030Sstevel@tonic-gate 		rqs_algorithm(C->s_type.LA.s_array, C->s_type.LA.s_array_size,
3040Sstevel@tonic-gate 		    0, collated, coll_flags);
3050Sstevel@tonic-gate 	else
3060Sstevel@tonic-gate 		rqs_algorithm(C->s_type.LA.s_array, C->s_type.LA.s_array_size,
3070Sstevel@tonic-gate 		    0, collated_wide, coll_flags);
3080Sstevel@tonic-gate }
3090Sstevel@tonic-gate 
3100Sstevel@tonic-gate void
internal_sort(sort_t * S)3110Sstevel@tonic-gate internal_sort(sort_t *S)
3120Sstevel@tonic-gate {
3130Sstevel@tonic-gate 	size_t input_mem, sort_mem;
3140Sstevel@tonic-gate 	size_t prev_sort_mem = 0;
3150Sstevel@tonic-gate 	void *prev_sort_buf = NULL;
3160Sstevel@tonic-gate 
3170Sstevel@tonic-gate 	int numerator, denominator;
3180Sstevel@tonic-gate 	int memory_left;
3190Sstevel@tonic-gate 	int currently_primed;
3200Sstevel@tonic-gate 	flag_t coll_flags;
3210Sstevel@tonic-gate 
3220Sstevel@tonic-gate 	stream_t *sort_stream = NULL;
3230Sstevel@tonic-gate 	stream_t *cur_stream;
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 	set_memory_ratio(S, &numerator, &denominator);
3260Sstevel@tonic-gate 	set_cleanup_chain(&S->m_temporary_streams);
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate 	if (S->m_field_options & FIELD_REVERSE_COMPARISONS)
3290Sstevel@tonic-gate 		coll_flags = COLL_REVERSE;
3300Sstevel@tonic-gate 	else
3310Sstevel@tonic-gate 		coll_flags = 0;
3320Sstevel@tonic-gate 
3330Sstevel@tonic-gate 	/*
3340Sstevel@tonic-gate 	 * For the entire line special case, we can speed comparisons by
3350Sstevel@tonic-gate 	 * recognizing that the collation vector contains all the information
3360Sstevel@tonic-gate 	 * required to order the line against other lines of the file.
3370Sstevel@tonic-gate 	 * COLL_UNIQUE provides such an exit; if we use the standard put-line
3380Sstevel@tonic-gate 	 * operation for the output stream, we achieve the desired fast path.
3390Sstevel@tonic-gate 	 */
3400Sstevel@tonic-gate 	if (S->m_entire_line)
3410Sstevel@tonic-gate 		coll_flags |= COLL_UNIQUE;
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate 	hold_file_descriptor();
3440Sstevel@tonic-gate 
3450Sstevel@tonic-gate 	cur_stream = S->m_input_streams;
3460Sstevel@tonic-gate 	while (cur_stream != NULL) {
3470Sstevel@tonic-gate 		if (!(cur_stream->s_status & STREAM_OPEN)) {
3480Sstevel@tonic-gate 			if (stream_open_for_read(S, cur_stream) == -1)
3490Sstevel@tonic-gate 				die(EMSG_DESCRIPTORS);
3500Sstevel@tonic-gate 		}
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate 		if (cur_stream->s_status & STREAM_MMAP) {
3530Sstevel@tonic-gate 			input_mem = 0;
3540Sstevel@tonic-gate 		} else {
3550Sstevel@tonic-gate 			input_mem = (size_t)(((u_longlong_t)numerator *
3560Sstevel@tonic-gate 			    S->m_memory_available) / denominator);
3570Sstevel@tonic-gate 			stream_set_size(cur_stream, input_mem);
3580Sstevel@tonic-gate 		}
3590Sstevel@tonic-gate 
3600Sstevel@tonic-gate 		sort_mem = S->m_memory_available - input_mem;
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate 		currently_primed = SOP_PRIME(cur_stream);
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate 		sort_stream = safe_realloc(sort_stream, sizeof (stream_t));
3650Sstevel@tonic-gate 		stream_clear(sort_stream);
3660Sstevel@tonic-gate 		sort_stream->s_buffer = prev_sort_buf;
3670Sstevel@tonic-gate 		sort_stream->s_buffer_size = prev_sort_mem;
3680Sstevel@tonic-gate 		stream_set(sort_stream, STREAM_OPEN | STREAM_ARRAY);
3690Sstevel@tonic-gate 		sort_stream->s_element_size = S->m_single_byte_locale ?
3700Sstevel@tonic-gate 		    sizeof (char) : sizeof (wchar_t);
3710Sstevel@tonic-gate 		stream_set_size(sort_stream, sort_mem);
3720Sstevel@tonic-gate 		prev_sort_buf = sort_stream->s_buffer;
3730Sstevel@tonic-gate 		prev_sort_mem = sort_stream->s_buffer_size;
3740Sstevel@tonic-gate 
375*5836Snakanon 		for (;;) {
376*5836Snakanon 			if (currently_primed == PRIME_SUCCEEDED) {
377*5836Snakanon 				memory_left =
378*5836Snakanon 				    stream_insert(S, cur_stream, sort_stream);
379*5836Snakanon 
380*5836Snakanon 				if (memory_left != ST_MEM_AVAIL)
381*5836Snakanon 					break;
382*5836Snakanon 			}
3830Sstevel@tonic-gate 
3840Sstevel@tonic-gate 			if (SOP_EOS(cur_stream)) {
3850Sstevel@tonic-gate 				if (cur_stream->s_consumer == NULL) {
3860Sstevel@tonic-gate 					(void) SOP_FREE(cur_stream);
3870Sstevel@tonic-gate 					(void) SOP_CLOSE(cur_stream);
3880Sstevel@tonic-gate 				}
3890Sstevel@tonic-gate 
3900Sstevel@tonic-gate 				cur_stream = cur_stream->s_next;
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate 				if (cur_stream == NULL)
3930Sstevel@tonic-gate 					break;
3940Sstevel@tonic-gate 
3950Sstevel@tonic-gate 				if (!(cur_stream->s_status & STREAM_OPEN) &&
3960Sstevel@tonic-gate 				    (stream_open_for_read(S, cur_stream) == -1))
3970Sstevel@tonic-gate 					break;
3980Sstevel@tonic-gate 
3990Sstevel@tonic-gate 				if (!(cur_stream->s_status & STREAM_MMAP)) {
4000Sstevel@tonic-gate 					input_mem = numerator *
4010Sstevel@tonic-gate 					    S->m_memory_available / denominator;
4020Sstevel@tonic-gate 					stream_set_size(cur_stream,
4030Sstevel@tonic-gate 					    input_mem);
4040Sstevel@tonic-gate 				}
4050Sstevel@tonic-gate 				currently_primed = SOP_PRIME(cur_stream);
4060Sstevel@tonic-gate 			}
4070Sstevel@tonic-gate 		}
4080Sstevel@tonic-gate 
4090Sstevel@tonic-gate 		radix_quicksort(sort_stream, coll_flags);
4100Sstevel@tonic-gate 
4110Sstevel@tonic-gate #ifndef DEBUG_NO_CACHE_TEMP
412*5836Snakanon 		/*
413*5836Snakanon 		 * cur_stream is set to NULL only when memory isn't filled and
414*5836Snakanon 		 * no more input streams remain.  In this case, we can safely
415*5836Snakanon 		 * cache the sort results.
416*5836Snakanon 		 *
417*5836Snakanon 		 * Otherwise, we have either exhausted available memory or
418*5836Snakanon 		 * available file descriptors.  If we've use all the available
419*5836Snakanon 		 * file descriptors, we aren't able to open the next input file.
420*5836Snakanon 		 * In this case, we can't cache the sort results, because more
421*5836Snakanon 		 * input files remain.
422*5836Snakanon 		 *
423*5836Snakanon 		 * If memory was filled, then there must be at least one
424*5836Snakanon 		 * leftover line unprocessed in the input stream, even though
425*5836Snakanon 		 * stream will indicated EOS if asked. We can't cache in this
426*5836Snakanon 		 * case, as we need one more round for the pending line or
427*5836Snakanon 		 * lines.
428*5836Snakanon 		 */
429*5836Snakanon 		if (cur_stream == NULL) {
4300Sstevel@tonic-gate 			(void) stream_push_to_temporary(&S->m_temporary_streams,
4310Sstevel@tonic-gate 			    sort_stream, ST_CACHE |
4320Sstevel@tonic-gate 			    (S->m_single_byte_locale ? 0 : ST_WIDE));
433*5836Snakanon 			break;
4340Sstevel@tonic-gate 		} else {
4350Sstevel@tonic-gate #endif
4360Sstevel@tonic-gate 			release_file_descriptor();
4370Sstevel@tonic-gate 			(void) stream_push_to_temporary(&S->m_temporary_streams,
4380Sstevel@tonic-gate 			    sort_stream, ST_NOCACHE |
4390Sstevel@tonic-gate 			    (S->m_single_byte_locale ? 0 : ST_WIDE));
4400Sstevel@tonic-gate 
4410Sstevel@tonic-gate 			hold_file_descriptor();
4420Sstevel@tonic-gate #ifdef DEBUG_NO_CACHE_TEMP
4430Sstevel@tonic-gate 			if (cur_stream == NULL)
4440Sstevel@tonic-gate 				break;
4450Sstevel@tonic-gate #endif
4460Sstevel@tonic-gate 			stream_unset(cur_stream, STREAM_NOT_FREEABLE);
4470Sstevel@tonic-gate 			stream_close_all_previous(cur_stream);
4480Sstevel@tonic-gate 			cur_stream->s_consumer = NULL;
4490Sstevel@tonic-gate #ifndef DEBUG_NO_CACHE_TEMP
4500Sstevel@tonic-gate 		}
4510Sstevel@tonic-gate #endif
4520Sstevel@tonic-gate 	}
4530Sstevel@tonic-gate 
4540Sstevel@tonic-gate 	release_file_descriptor();
4550Sstevel@tonic-gate }
456