Skip to content

Commit 2b6aefd

Browse files
committed
FEAT: Block SORT comparator
resolves: Oldes/Rebol-issues#2684
1 parent dd3632d commit 2b6aefd

File tree

4 files changed

+64
-5
lines changed

4 files changed

+64
-5
lines changed

src/boot/actions.reb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ sort: action [
378378
/skip {Treat the series as records of fixed size}
379379
size [integer!] {Size of each record}
380380
/compare {Comparator offset or function}
381-
comparator [integer! any-function!]
381+
comparator [integer! block! any-function!]
382382
/part {Limits the sorting to a given length or position}
383383
range [number! series!]
384384
/all {Compare all fields}

src/core/t-block.c

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,28 @@ static void No_Nones_Or_Logic(REBVAL *arg) {
459459
return result;
460460
}
461461

462+
/***********************************************************************
463+
**
464+
*/ static int Compare_Val_Multi(const void* v1, const void* v2)
465+
/*
466+
** Used to compare multiple values in the record (sort/compare with block).
467+
**
468+
***********************************************************************/
469+
{
470+
REBVAL* val = DS_GET(DSP - 1);
471+
REBU64 flags = VAL_UNT64(DS_TOP);
472+
REBLEN offset = 0;
473+
REBINT result = 0;
474+
ASSERT1(IS_BLOCK(val), RP_BAD_EVALTYPE);
475+
REBVAL* ofs = VAL_BLK_DATA(val);
476+
while (result == 0 && IS_INTEGER(ofs)) {
477+
offset = AS_REBLEN(VAL_INT64(ofs++) - 1);
478+
result = Cmp_Value((REBVAL*)v1 + offset, (REBVAL*)v2 + offset, GET_FLAG(flags, SORT_FLAG_CASE));
479+
}
480+
if (GET_FLAG(flags, SORT_FLAG_REVERSE)) result = -result;
481+
return result;
482+
}
483+
462484
/***********************************************************************
463485
**
464486
*/ static int Compare_All_Val(const void *v1, const void *v2)
@@ -579,10 +601,28 @@ static void No_Nones_Or_Logic(REBVAL *arg) {
579601
// Use fast quicksort library function:
580602
if (skip > 1) len /= skip, size *= skip;
581603

582-
if (ANY_FUNC(compv))
583-
reb_qsort((void *)VAL_BLK_DATA(block), len, size, Compare_Call);
584-
else
585-
reb_qsort((void *)VAL_BLK_DATA(block), len, size, (all && skip > 1) ? Compare_All_Val : Compare_Val);
604+
int (*cmp)(const void*, const void*);
605+
606+
if (ANY_FUNC(compv)) {
607+
cmp = Compare_Call;
608+
}
609+
else if (all && skip > 1) {
610+
cmp = Compare_All_Val;
611+
}
612+
else if (IS_BLOCK(compv)) {
613+
// Validate first...
614+
REBVAL* tmp = VAL_BLK_DATA(compv);
615+
while (!IS_END(tmp)) {
616+
if (!IS_INTEGER(tmp) || VAL_INT64(tmp) < 1)
617+
Trap1(RE_INVALID_ARG, tmp);
618+
tmp++;
619+
}
620+
cmp = Compare_Val_Multi;
621+
}
622+
else {
623+
cmp = Compare_Val;
624+
}
625+
reb_qsort((void*)VAL_BLK_DATA(block), len, size, cmp);
586626

587627
// Stored comparator and flags are not needed anymore
588628
DS_DROP;

src/core/t-string.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,10 @@ static const cmp_func sfunc_table[2][2][2][2] = {
638638
DS_PUSH_INTEGER(flags);
639639
sfunc = Compare_Comp;
640640
}
641+
else if (IS_BLOCK(compv)) {
642+
// Not implemented for string series.
643+
Trap0(RE_FEATURE_NA);
644+
}
641645
else {
642646
// Store the skip (used to implement /all)
643647
DS_PUSH_INTEGER(all ? skip : 1);

src/tests/units/series-test.r3

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1742,6 +1742,21 @@ Rebol [
17421742
;@@ https://github.com/Oldes/Rebol-issues/issues/721
17431743
--assert [4 3 2 1] = sort/compare [1 2 3 4] :greater?
17441744

1745+
;@@ https://github.com/Oldes/Rebol-issues/issues/2684
1746+
--assert (sort/skip ["A2" "B3" "C1" "A1" "B2" "C3" "A3" "B1" "C2"] 3)
1747+
== ["A1" "B2" "C3" "A2" "B3" "C1" "A3" "B1" "C2"]
1748+
--assert (sort/skip/compare ["A2" "B3" "C1" "A1" "B2" "C3" "A3" "B1" "C2" ] 3 [ 3 2 1 ])
1749+
== ["A2" "B3" "C1" "A3" "B1" "C2" "A1" "B2" "C3"]
1750+
--assert (sort/skip/compare [ "A2" "B3" "C1" "A1" "B2" "C3" "A3" "B1" "C2" ] 3 [ 2 3 1 ])
1751+
== ["A3" "B1" "C2" "A1" "B2" "C3" "A2" "B3" "C1"]
1752+
--assert (sort/skip/compare [ "A2" "B3" "C1" "A1" "B2" "C3" "A3" "B1" "C2" ] 3 [ 2 1 3 ])
1753+
== ["A3" "B1" "C2" "A1" "B2" "C3" "A2" "B3" "C1"]
1754+
--assert (sort/skip/compare [ "A2" "B3" "C1" "A1" "B2" "C3" "A3" "B1" "C2" ] 3 [ 1 2 3 ])
1755+
== ["A1" "B2" "C3" "A2" "B3" "C1" "A3" "B1" "C2"]
1756+
--assert all [error? e: try [sort/skip/compare [3 B 1 B] 2 [2 -1]] e/id = 'invalid-arg]
1757+
--assert all [error? e: try [sort/skip/compare [3 B 1 B] 2 [2 0]] e/id = 'invalid-arg]
1758+
--assert all [error? e: try [sort/skip/compare [3 B 1 B] 2 [2 x]] e/id = 'invalid-arg]
1759+
17451760
--test-- "SORT/compare string!"
17461761
;@@ https://github.com/Oldes/Rebol-issues/issues/1100
17471762
comp: func [a b] [a > b]

0 commit comments

Comments
 (0)