4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #pragma ident "%Z%%M% %I% %E% SMI"
30 #define INSERTION_THRESHOLD 12
33 swap_range(int i
, int j
, int n
, line_rec_t
**I
)
45 * offset_is_algorithm() implements a simple insertion sort on line records that
46 * allows comparison from an offset into the l_collate field (since the subfiles
47 * should have already been sorted to that depth).
50 offset_is_algorithm(line_rec_t
**X
, ssize_t n
,
51 int (*collate_fcn
)(line_rec_t
*, line_rec_t
*, ssize_t
, flag_t
),
52 ssize_t depth
, flag_t coll_flags
)
56 __S(stats_incr_subfiles());
59 * Place lowest element in X[0].
61 for (i
= 0; i
< n
; i
++) {
62 if (collate_fcn(X
[0], X
[i
], depth
, coll_flags
) > 0) {
63 swap((void **)&X
[0], (void **)&X
[i
]);
64 ASSERT(collate_fcn(X
[0], X
[i
], depth
, coll_flags
) <= 0);
71 for (i
= 2; i
< n
; i
++) {
74 while (collate_fcn(t
, X
[j
- 1], depth
, coll_flags
) < 0) {
84 * tqs_algorithm() is called from rqs_algorithm() when a subpartition is
85 * encountered whose line records share indistinguishable l_collate fields. It
86 * implements a semi-iterative ternary quicksort.
89 tqs_algorithm(line_rec_t
**X
, ssize_t n
,
90 int (*collate_fcn
)(line_rec_t
*, line_rec_t
*, ssize_t
, flag_t
),
93 ssize_t l
; /* boundary of left partition */
94 ssize_t le
; /* boundary of left equal partition */
95 ssize_t r
; /* boundary of right partition */
96 ssize_t re
; /* boundary of right equal partition */
98 ssize_t p
, q
; /* scratch for indices, comparisons */
100 coll_flags
|= COLL_DATA_ONLY
;
105 * completion criteria
110 if (n
<= INSERTION_THRESHOLD
) {
111 offset_is_algorithm(X
, n
, collate_fcn
, 0, coll_flags
);
116 * selection of partition element
119 swap((void **)&X
[0], (void **)&X
[le
]);
126 (p
= collate_fcn(X
[l
], X
[0], 0, coll_flags
)) <= 0) {
128 swap((void **)&X
[le
++], (void **)&X
[l
]);
133 (p
= collate_fcn(X
[r
], X
[0], 0, coll_flags
)) >= 0) {
135 swap((void **)&X
[r
], (void **)&X
[re
--]);
142 swap((void **)&X
[l
++], (void **)&X
[r
--]);
146 * swap equal partitions into middle
149 swap_range(0, l
- p
, p
, X
);
150 p
= MIN(re
- r
, n
- re
- 1);
151 swap_range(l
, n
- p
, p
, X
);
154 * Iterate with larger subpartition, recurse into smaller.
160 tqs_algorithm(&X
[n
- q
], q
, collate_fcn
, coll_flags
);
164 tqs_algorithm(X
, p
, collate_fcn
, coll_flags
);
175 * The following semi-iterative radix quicksort is derived from that presented
177 * J. Bentley and R. Sedgewick, Fast Algorithms for Sorting and Searching
178 * Strings, in Eighth Annual ACM-SIAM Symposium on Discrete Algorithms,
181 * R. Sedgewick, Algorithms in C, 3rd ed., vol. 1, Addison-Wesley, 1998.
185 rqs_algorithm(line_rec_t
**X
, ssize_t n
, ssize_t depth
,
186 int (*collate_fcn
)(line_rec_t
*, line_rec_t
*, ssize_t
, flag_t
),
189 uchar_t v
; /* partition radix value */
191 ssize_t l
; /* boundary of left partition */
192 ssize_t le
; /* boundary of left equal partition */
193 ssize_t r
; /* boundary of right partition */
194 ssize_t re
; /* boundary of right equal partition */
196 ssize_t p
; /* scratch for indices, comparisons */
197 line_rec_t
*t
; /* scratch for swaps */
202 * completion criteria
207 if (n
<= INSERTION_THRESHOLD
) {
208 offset_is_algorithm(X
, n
, collate_fcn
, depth
, coll_flags
);
213 * selection of partition element
216 swap((void **)&X
[0], (void **)&X
[le
]);
217 v
= X
[0]->l_collate
.usp
[depth
];
224 (p
= *(X
[l
]->l_collate
.usp
+ depth
) - v
) <= 0) {
235 (p
= *(X
[r
]->l_collate
.usp
+ depth
) - v
) >= 0) {
256 * swap equal partitions into middle
259 swap_range(0, l
- p
, p
, X
);
260 p
= MIN(re
- r
, n
- re
- 1);
261 swap_range(l
, n
- p
, p
, X
);
264 * recurse into subpartitions as necessary
268 rqs_algorithm(&X
[n
- p
], p
, depth
, collate_fcn
, coll_flags
);
272 rqs_algorithm(X
, p
, depth
, collate_fcn
, coll_flags
);
274 if (le
+ n
- re
- 1 <= 1)
278 * - 1 so that we don't count the final null.
280 if (X
[p
]->l_collate_length
- 1 > depth
) {
282 * Equivalent recursion: rqs_algorithm(&X[p], le + n - re - 1,
283 * depth + 1, collate_fcn, coll_only);
291 if (!(coll_flags
& COLL_UNIQUE
)) {
292 __S(stats_incr_tqs_calls());
293 tqs_algorithm(&X
[p
], le
+ n
- re
- 1, collate_fcn
, coll_flags
);
298 radix_quicksort(stream_t
*C
, flag_t coll_flags
)
300 ASSERT((C
->s_status
& STREAM_SOURCE_MASK
) == STREAM_ARRAY
);
302 if (C
->s_element_size
== sizeof (char))
303 rqs_algorithm(C
->s_type
.LA
.s_array
, C
->s_type
.LA
.s_array_size
,
304 0, collated
, coll_flags
);
306 rqs_algorithm(C
->s_type
.LA
.s_array
, C
->s_type
.LA
.s_array_size
,
307 0, collated_wide
, coll_flags
);
311 internal_sort(sort_t
*S
)
313 size_t input_mem
, sort_mem
;
314 size_t prev_sort_mem
= 0;
315 void *prev_sort_buf
= NULL
;
317 int numerator
, denominator
;
319 int currently_primed
;
322 stream_t
*sort_stream
= NULL
;
323 stream_t
*cur_stream
;
325 set_memory_ratio(S
, &numerator
, &denominator
);
326 set_cleanup_chain(&S
->m_temporary_streams
);
328 if (S
->m_field_options
& FIELD_REVERSE_COMPARISONS
)
329 coll_flags
= COLL_REVERSE
;
334 * For the entire line special case, we can speed comparisons by
335 * recognizing that the collation vector contains all the information
336 * required to order the line against other lines of the file.
337 * COLL_UNIQUE provides such an exit; if we use the standard put-line
338 * operation for the output stream, we achieve the desired fast path.
340 if (S
->m_entire_line
)
341 coll_flags
|= COLL_UNIQUE
;
343 hold_file_descriptor();
345 cur_stream
= S
->m_input_streams
;
346 while (cur_stream
!= NULL
) {
347 if (!(cur_stream
->s_status
& STREAM_OPEN
)) {
348 if (stream_open_for_read(S
, cur_stream
) == -1)
349 die(EMSG_DESCRIPTORS
);
352 if (cur_stream
->s_status
& STREAM_MMAP
) {
355 input_mem
= (size_t)(((u_longlong_t
)numerator
*
356 S
->m_memory_available
) / denominator
);
357 stream_set_size(cur_stream
, input_mem
);
360 sort_mem
= S
->m_memory_available
- input_mem
;
362 currently_primed
= SOP_PRIME(cur_stream
);
364 sort_stream
= safe_realloc(sort_stream
, sizeof (stream_t
));
365 stream_clear(sort_stream
);
366 sort_stream
->s_buffer
= prev_sort_buf
;
367 sort_stream
->s_buffer_size
= prev_sort_mem
;
368 stream_set(sort_stream
, STREAM_OPEN
| STREAM_ARRAY
);
369 sort_stream
->s_element_size
= S
->m_single_byte_locale
?
370 sizeof (char) : sizeof (wchar_t);
371 stream_set_size(sort_stream
, sort_mem
);
372 prev_sort_buf
= sort_stream
->s_buffer
;
373 prev_sort_mem
= sort_stream
->s_buffer_size
;
376 if (currently_primed
== PRIME_SUCCEEDED
) {
378 stream_insert(S
, cur_stream
, sort_stream
);
380 if (memory_left
!= ST_MEM_AVAIL
)
384 if (SOP_EOS(cur_stream
)) {
385 if (cur_stream
->s_consumer
== NULL
) {
386 (void) SOP_FREE(cur_stream
);
387 (void) SOP_CLOSE(cur_stream
);
390 cur_stream
= cur_stream
->s_next
;
392 if (cur_stream
== NULL
)
395 if (!(cur_stream
->s_status
& STREAM_OPEN
) &&
396 (stream_open_for_read(S
, cur_stream
) == -1))
399 if (!(cur_stream
->s_status
& STREAM_MMAP
)) {
400 input_mem
= numerator
*
401 S
->m_memory_available
/ denominator
;
402 stream_set_size(cur_stream
,
405 currently_primed
= SOP_PRIME(cur_stream
);
409 radix_quicksort(sort_stream
, coll_flags
);
411 #ifndef DEBUG_NO_CACHE_TEMP
413 * cur_stream is set to NULL only when memory isn't filled and
414 * no more input streams remain. In this case, we can safely
415 * cache the sort results.
417 * Otherwise, we have either exhausted available memory or
418 * available file descriptors. If we've use all the available
419 * file descriptors, we aren't able to open the next input file.
420 * In this case, we can't cache the sort results, because more
421 * input files remain.
423 * If memory was filled, then there must be at least one
424 * leftover line unprocessed in the input stream, even though
425 * stream will indicated EOS if asked. We can't cache in this
426 * case, as we need one more round for the pending line or
429 if (cur_stream
== NULL
) {
430 (void) stream_push_to_temporary(&S
->m_temporary_streams
,
431 sort_stream
, ST_CACHE
|
432 (S
->m_single_byte_locale
? 0 : ST_WIDE
));
436 release_file_descriptor();
437 (void) stream_push_to_temporary(&S
->m_temporary_streams
,
438 sort_stream
, ST_NOCACHE
|
439 (S
->m_single_byte_locale
? 0 : ST_WIDE
));
441 hold_file_descriptor();
442 #ifdef DEBUG_NO_CACHE_TEMP
443 if (cur_stream
== NULL
)
446 stream_unset(cur_stream
, STREAM_NOT_FREEABLE
);
447 stream_close_all_previous(cur_stream
);
448 cur_stream
->s_consumer
= NULL
;
449 #ifndef DEBUG_NO_CACHE_TEMP
454 release_file_descriptor();