Browse Source

* use a more robust QuickSort implementation, that is guaranteed to never loop
forever and never access index out of bounds elements from the array when
being passed an incorrect comparison function. The resulting sort order is
still undefined in this case, though.

git-svn-id: trunk@41229 -

nickysn 6 years ago
parent
commit
f5f25f7ae6
1 changed files with 114 additions and 81 deletions
  1. 114 81
      rtl/inc/sortbase.pp

+ 114 - 81
rtl/inc/sortbase.pp

@@ -76,37 +76,49 @@ begin
    PivotIdx := (L + R) div 2;
    PivotIdx := (L + R) div 2;
    P := ItemPtrs[PivotIdx];
    P := ItemPtrs[PivotIdx];
    repeat
    repeat
-     while Comparer(P, ItemPtrs[i]) > 0 do
+     while (I < PivotIdx) and (Comparer(P, ItemPtrs[i]) >= 0) do
        Inc(I);
        Inc(I);
-     while Comparer(P, ItemPtrs[J]) < 0 do
+     while (J > PivotIdx) and (Comparer(P, ItemPtrs[J]) < 0) do
        Dec(J);
        Dec(J);
-     If I <= J then
+     if I < J then
      begin
      begin
        Q := ItemPtrs[I];
        Q := ItemPtrs[I];
        ItemPtrs[I] := ItemPtrs[J];
        ItemPtrs[I] := ItemPtrs[J];
        ItemPtrs[J] := Q;
        ItemPtrs[J] := Q;
        if PivotIdx = I then
        if PivotIdx = I then
-         PivotIdx := J
+       begin
+         PivotIdx := J;
+         Inc(I);
+       end
        else if PivotIdx = J then
        else if PivotIdx = J then
+       begin
          PivotIdx := I;
          PivotIdx := I;
-       Inc(I);
-       Dec(J);
+         Dec(J);
+       end
+       else
+       begin
+         Inc(I);
+         Dec(J);
+       end;
      end;
      end;
-   until I > J;
+   until I >= J;
    // sort the smaller range recursively
    // sort the smaller range recursively
    // sort the bigger range via the loop
    // sort the bigger range via the loop
    // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
    // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
-   if J - L < R - I then
+   if (PivotIdx - L) < (R - PivotIdx) then
    begin
    begin
-     if L < J then
-       QuickSort_PtrList_NoContext(ItemPtrs, L, J, Comparer);
-     L := I;
+     if (L + 1) < PivotIdx then
+       QuickSort_PtrList_NoContext(ItemPtrs, L, PivotIdx - 1, Comparer);
+     L := PivotIdx + 1;
    end
    end
    else
    else
    begin
    begin
-     if I < R then
-       QuickSort_PtrList_NoContext(ItemPtrs, I, R, Comparer);
-     R := J;
+     if (PivotIdx + 1) < R then
+       QuickSort_PtrList_NoContext(ItemPtrs, PivotIdx + 1, R, Comparer);
+     if (L + 1) < PivotIdx then
+       R := PivotIdx - 1
+     else
+       exit;
    end;
    end;
  until L >= R;
  until L >= R;
 end;
 end;
@@ -131,38 +143,49 @@ procedure QuickSort_PtrList_Context(ItemPtrs: PPointer; ItemCount: SizeUInt; Com
       PivotIdx := (L + R) div 2;
       PivotIdx := (L + R) div 2;
       P := ItemPtrs[PivotIdx];
       P := ItemPtrs[PivotIdx];
       repeat
       repeat
-        while Comparer(P, ItemPtrs[I], Context) > 0 do
+        while (I < PivotIdx) and (Comparer(P, ItemPtrs[I], Context) >= 0) do
           Inc(I);
           Inc(I);
-        while Comparer(P, ItemPtrs[J], Context) < 0 do
+        while (J > PivotIdx) and (Comparer(P, ItemPtrs[J], Context) < 0) do
           Dec(J);
           Dec(J);
-        If I <= J then
+        if I < J then
         begin
         begin
           Q := ItemPtrs[I];
           Q := ItemPtrs[I];
           ItemPtrs[I] := ItemPtrs[J];
           ItemPtrs[I] := ItemPtrs[J];
           ItemPtrs[J] := Q;
           ItemPtrs[J] := Q;
           if PivotIdx = I then
           if PivotIdx = I then
-            PivotIdx := J
+          begin
+            PivotIdx := J;
+            Inc(I);
+          end
           else if PivotIdx = J then
           else if PivotIdx = J then
+          begin
             PivotIdx := I;
             PivotIdx := I;
-
-          Inc(I);
-          Dec(J);
+            Dec(J);
+          end
+          else
+          begin
+            Inc(I);
+            Dec(J);
+          end;
         end;
         end;
-      until I > J;
+      until I >= J;
       // sort the smaller range recursively
       // sort the smaller range recursively
       // sort the bigger range via the loop
       // sort the bigger range via the loop
       // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
       // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
-      if J - L < R - I then
+      if (PivotIdx - L) < (R - PivotIdx) then
       begin
       begin
-        if L < J then
-          QuickSort(L, J);
-        L := I;
+        if (L + 1) < PivotIdx then
+          QuickSort(L, PivotIdx - 1);
+        L := PivotIdx + 1;
       end
       end
       else
       else
       begin
       begin
-        if I < R then
-          QuickSort(I, R);
-        R := J;
+        if (PivotIdx + 1) < R then
+          QuickSort(PivotIdx + 1, R);
+        if (L + 1) < PivotIdx then
+          R := PivotIdx - 1
+        else
+          exit;
       end;
       end;
     until L >= R;
     until L >= R;
   end;
   end;
@@ -189,46 +212,51 @@ var
       PivotIdx := (L + R) div 2;
       PivotIdx := (L + R) div 2;
       P := Items + ItemSize*PivotIdx;
       P := Items + ItemSize*PivotIdx;
       repeat
       repeat
-        while Comparer(P, Items + ItemSize*I, Context) > 0 do
+        while (I < PivotIdx) and (Comparer(P, Items + ItemSize*I, Context) >= 0) do
           Inc(I);
           Inc(I);
-        while Comparer(P, Items + ItemSize*J, Context) < 0 do
+        while (J > PivotIdx) and (Comparer(P, Items + ItemSize*J, Context) < 0) do
           Dec(J);
           Dec(J);
-        If I <= J then
+        if I < J then
         begin
         begin
-          if I < J then
+          Move((Items + ItemSize*I)^, TempBuf^, ItemSize);
+          Move((Items + ItemSize*J)^, (Items + ItemSize*I)^, ItemSize);
+          Move(TempBuf^, (Items + ItemSize*J)^, ItemSize);
+          if PivotIdx = I then
+          begin
+            PivotIdx := J;
+            P := Items + ItemSize*PivotIdx;
+            Inc(I);
+          end
+          else if PivotIdx = J then
           begin
           begin
-            Move((Items + ItemSize*I)^, TempBuf^, ItemSize);
-            Move((Items + ItemSize*J)^, (Items + ItemSize*I)^, ItemSize);
-            Move(TempBuf^, (Items + ItemSize*J)^, ItemSize);
-            if PivotIdx = I then
-            begin
-              PivotIdx := J;
-              P := Items + ItemSize*PivotIdx
-            end
-            else if PivotIdx = J then
-            begin
-              PivotIdx := I;
-              P := Items + ItemSize*PivotIdx;
-            end;
+            PivotIdx := I;
+            P := Items + ItemSize*PivotIdx;
+            Dec(J);
+          end
+          else
+          begin
+            Inc(I);
+            Dec(J);
           end;
           end;
-          Inc(I);
-          Dec(J);
         end;
         end;
-      until I > J;
+      until I >= J;
       // sort the smaller range recursively
       // sort the smaller range recursively
       // sort the bigger range via the loop
       // sort the bigger range via the loop
       // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
       // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
-      if J - L < R - I then
+      if (PivotIdx - L) < (R - PivotIdx) then
       begin
       begin
-        if L < J then
-          QuickSort(L, J);
-        L := I;
+        if (L + 1) < PivotIdx then
+          QuickSort(L, PivotIdx - 1);
+        L := PivotIdx + 1;
       end
       end
       else
       else
       begin
       begin
-        if I < R then
-          QuickSort(I, R);
-        R := J;
+        if (PivotIdx + 1) < R then
+          QuickSort(PivotIdx + 1, R);
+        if (L + 1) < PivotIdx then
+          R := PivotIdx - 1
+        else
+          exit;
       end;
       end;
     until L >= R;
     until L >= R;
   end;
   end;
@@ -262,44 +290,49 @@ procedure QuickSort_ItemList_CustomItemExchanger_Context(
       PivotIdx := (L + R) div 2;
       PivotIdx := (L + R) div 2;
       P := Items + ItemSize*PivotIdx;
       P := Items + ItemSize*PivotIdx;
       repeat
       repeat
-        while Comparer(P, Items + ItemSize*I, Context) > 0 do
+        while (I < PivotIdx) and (Comparer(P, Items + ItemSize*I, Context) >= 0) do
           Inc(I);
           Inc(I);
-        while Comparer(P, Items + ItemSize*J, Context) < 0 do
+        while (J > PivotIdx) and (Comparer(P, Items + ItemSize*J, Context) < 0) do
           Dec(J);
           Dec(J);
-        If I <= J then
+        if I < J then
         begin
         begin
-          if I < J then
+          Exchanger(Items + ItemSize*I, Items + ItemSize*J, Context);
+          if PivotIdx = I then
           begin
           begin
-            Exchanger(Items + ItemSize*I, Items + ItemSize*J, Context);
-            if PivotIdx = I then
-            begin
-              PivotIdx := J;
-              P := Items + ItemSize*PivotIdx
-            end
-            else if PivotIdx = J then
-            begin
-              PivotIdx := I;
-              P := Items + ItemSize*PivotIdx;
-            end;
+            PivotIdx := J;
+            P := Items + ItemSize*PivotIdx;
+            Inc(I);
+          end
+          else if PivotIdx = J then
+          begin
+            PivotIdx := I;
+            P := Items + ItemSize*PivotIdx;
+            Dec(J);
+          end
+          else
+          begin
+            Inc(I);
+            Dec(J);
           end;
           end;
-          Inc(I);
-          Dec(J);
         end;
         end;
-      until I > J;
+      until I >= J;
       // sort the smaller range recursively
       // sort the smaller range recursively
       // sort the bigger range via the loop
       // sort the bigger range via the loop
       // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
       // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion
-      if J - L < R - I then
+      if (PivotIdx - L) < (R - PivotIdx) then
       begin
       begin
-        if L < J then
-          QuickSort(L, J);
-        L := I;
+        if (L + 1) < PivotIdx then
+          QuickSort(L, PivotIdx - 1);
+        L := PivotIdx + 1;
       end
       end
       else
       else
       begin
       begin
-        if I < R then
-          QuickSort(I, R);
-        R := J;
+        if (PivotIdx + 1) < R then
+          QuickSort(PivotIdx + 1, R);
+        if (L + 1) < PivotIdx then
+          R := PivotIdx - 1
+        else
+          exit;
       end;
       end;
     until L >= R;
     until L >= R;
   end;
   end;