Index: lucene/core/src/java/org/apache/lucene/util/SorterTemplate.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/util/SorterTemplate.java	(révision 1457068)
+++ lucene/core/src/java/org/apache/lucene/util/SorterTemplate.java	(copie de travail)
@@ -1,5 +1,6 @@
 package org.apache.lucene.util;
 
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -29,9 +30,27 @@
  */
 public abstract class SorterTemplate {
 
+  private static final int TIMSORT_MINRUN = 32;
+  private static final int TIMSORT_THRESHOLD = 64;
+  private static final int TIMSORT_STACKSIZE = 40; // change if you change TIMSORT_MINRUN
   private static final int MERGESORT_THRESHOLD = 12;
   private static final int QUICKSORT_THRESHOLD = 7;
 
+  static {
+    // check whether TIMSORT_STACKSIZE is large enough
+    // for a run length of TIMSORT_MINRUN and an array
+    // of 2B values when TimSort invariants are verified
+    final long[] lengths = new long[TIMSORT_STACKSIZE];
+    lengths[0] = TIMSORT_MINRUN;
+    lengths[1] = lengths[0] + 1;
+    for (int i = 2; i < TIMSORT_STACKSIZE; ++i) {
+      lengths[i] = lengths[i-2] + lengths[i-1] + 1;
+    }
+    if (lengths[TIMSORT_STACKSIZE - 1] < Integer.MAX_VALUE) {
+      throw new Error("TIMSORT_STACKSIZE is too small");
+    }
+  }
+
   /** Implement this method, that swaps slots {@code i} and {@code j} in your data */
   protected abstract void swap(int i, int j);
   
@@ -46,7 +65,7 @@
    * Should be implemented like <code>pivot.compareTo(<em>valueOf(j)</em>)</code> */
   protected abstract int comparePivot(int j);
   
-  /** Sorts via stable in-place InsertionSort algorithm
+  /** Sorts via stable in-place InsertionSort algorithm (O(n<sup>2</sup>))
    *(ideal for small collections which are mostly presorted). */
   public final void insertionSort(int lo, int hi) {
     for (int i = lo + 1 ; i <= hi; i++) {
@@ -60,6 +79,28 @@
     }
   }
 
+  /** Sorts via stable in-place BinarySort algorithm (O(n<sup>2</sup>))
+   * (ideal for small collections which are in random order). */
+  public final void binarySort(int lo, int hi) {
+    for (int i = lo + 1; i <= hi; ++i) {
+      int l = lo;
+      int h = i - 1;
+      setPivot(i);
+      while (l <= h) {
+        final int mid = (l + h) >>> 1;
+        final int cmp = comparePivot(mid);
+        if (cmp < 0) {
+          h = mid - 1;
+        } else {
+          l = mid + 1;
+        }
+      }
+      for (int j = i; j > l; --j) {
+        swap(j - 1, j);
+      }
+    }
+  }
+
   /** Sorts via in-place, but unstable, QuickSort algorithm.
    * For small collections falls back to {@link #insertionSort(int,int)}. */
   public final void quickSort(final int lo, final int hi) {
@@ -117,7 +158,174 @@
     quickSort(lo, left, maxDepth);
     quickSort(left + 1, hi, maxDepth);
   }
-  
+
+  /** TimSort implementation. The only difference with the spec is that this
+   *  impl reuses {@link SorterTemplate#merge(int, int, int, int, int)} to
+   *  merge runs (in place) instead of the original merging routine from
+   *  TimSort (which requires extra memory but might be slightly faster). */
+  private class TimSort {
+
+    final int hi;
+    final int minRun;
+    final int[] runEnds;
+    int stackSize;
+
+    TimSort(int lo, int hi) {
+      assert hi > lo;
+      // +1 because the first slot is reserved and always lo
+      runEnds = new int[TIMSORT_STACKSIZE + 1];
+      runEnds[0] = lo;
+      stackSize = 0;
+      this.hi = hi;
+      minRun = minRun(hi - lo + 1);
+    }
+
+    /** Minimum run length for an array of length <code>length</code>. */
+    int minRun(int length) {
+      assert length >= TIMSORT_MINRUN;
+      int n = length;
+      int r = 0;
+      while (n >= 64) {
+        r |= n & 1;
+        n >>>= 1;
+      }
+      final int minRun = n + r;
+      assert minRun >= TIMSORT_MINRUN && minRun <= 64;
+      return minRun;
+    }
+
+    int runLen(int i) {
+      final int off = stackSize - i;
+      return runEnds[off] - runEnds[off - 1];
+    }
+
+    int runBase(int i) {
+      return runEnds[stackSize - i - 1];
+    }
+
+    int runEnd(int i) {
+      return runEnds[stackSize - i];
+    }
+
+    void setRunEnd(int i, int runEnd) {
+      runEnds[stackSize - i] = runEnd;
+    }
+
+    void pushRunLen(int len) {
+      runEnds[stackSize + 1] = runEnds[stackSize] + len;
+      ++stackSize;
+    }
+
+    /** Merge run i with run i+1 */
+    void mergeAt(int i) {
+      assert stackSize > i + 1;
+      final int l = runBase(i+1);
+      final int pivot = runBase(i);
+      final int h = runEnd(i);
+      merge(l, pivot, h, pivot - l, h - pivot);
+      for (int j = 1; j <= i+1; ++j) {
+        setRunEnd(j, runEnd(j-1));
+      }
+      --stackSize;
+    }
+
+    /** Compute the length of the next run, make the run sorted and return its
+     *  length. */
+    int nextRun() {
+      final int runBase = runEnd(0);
+      if (runBase == hi) {
+        return 1;
+      }
+      int l = 1; // length of the run
+      if (compare(runBase, runBase+1) > 0) {
+        // run must be strictly descending
+        while (runBase + l <= hi && compare(runBase + l - 1, runBase + l) > 0) {
+          ++l;
+        }
+        if (l < minRun && runBase + l <= hi) {
+          l = Math.min(hi - runBase + 1, minRun);
+          binarySort(runBase, runBase + l - 1);
+        } else {
+          // revert
+          for (int i = 0, halfL = l >>> 1; i < halfL; ++i) {
+            swap(runBase + i, runBase + l - i - 1);
+          }
+        }
+      } else {
+        // run must be non-descending
+        while (runBase + l <= hi && compare(runBase + l - 1, runBase + l) <= 0) {
+          ++l;
+        }
+        if (l < minRun && runBase + l <= hi) {
+          l = Math.min(hi - runBase + 1, minRun);
+          binarySort(runBase, runBase + l - 1);
+        } // else nothing to do, the run is already sorted
+      }
+      return l;
+    }
+
+    void ensureInvariants() {
+      while (stackSize > 1) {
+        final int runLen0 = runLen(0);
+        final int runLen1 = runLen(1);
+
+        if (stackSize > 2) {
+          final int runLen2 = runLen(2);
+
+          if (runLen2 <= runLen1 + runLen0) {
+            // merge the smaller of 0 and 2 with 1
+            if (runLen2 < runLen0) {
+              mergeAt(1);
+            } else {
+              mergeAt(0);
+            }
+            continue;
+          }
+        }
+
+        if (runLen1 <= runLen0) {
+          mergeAt(0);
+          continue;
+        }
+
+        break;
+      }
+    }
+
+    void exhaustStack() {
+      while (stackSize > 1) {
+        mergeAt(0);
+      }
+    }
+
+    void sort() {
+      do {
+        ensureInvariants();
+
+        // Push a new run onto the stack
+        pushRunLen(nextRun());
+
+      } while (runEnd(0) <= hi);
+
+      exhaustStack();
+    }
+
+  }
+
+  /** Sorts using TimSort, see http://svn.python.org/projects/python/trunk/Objects/listsort.txt
+   *  and http://svn.python.org/projects/python/trunk/Objects/listobject.c.
+   *  TimSort is a stable sorting algorithm based on MergeSort but known to
+   *  perform extremely well on partially-sorted inputs.
+   *  For small collections, falls back to {@link #binarySort(int, int)}. */
+  public final void timSort(int lo, int hi) {
+    if (hi - lo <= TIMSORT_THRESHOLD) {
+      binarySort(lo, hi);
+      return;
+    }
+
+    new TimSort(lo, hi).sort();
+  }
+
   /** Sorts via stable in-place MergeSort algorithm
    * For small collections falls back to {@link #insertionSort(int,int)}. */
   public final void mergeSort(int lo, int hi) {
Index: lucene/core/src/test/org/apache/lucene/util/TestSorterTemplate.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/util/TestSorterTemplate.java	(révision 0)
+++ lucene/core/src/test/org/apache/lucene/util/TestSorterTemplate.java	(copie de travail)
@@ -0,0 +1,167 @@
+package org.apache.lucene.util;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Arrays;
+
+public class TestSorterTemplate extends LuceneTestCase {
+
+  private static final int SLOW_SORT_THRESHOLD = 1000;
+
+  // A sorter template that compares only the last 32 bits
+  static class Last32BitsSorterTemplate extends SorterTemplate {
+
+    final long[] arr;
+    long pivot;
+
+    Last32BitsSorterTemplate(long[] arr) {
+      this.arr = arr;
+    }
+
+    @Override
+    protected void swap(int i, int j) {
+      final long tmp = arr[i];
+      arr[i] = arr[j];
+      arr[j] = tmp;
+    }
+
+    private int compareValues(long i, long j) {
+      // only compare the last 32 bits
+      final long a = i & 0xFFFFFFFFL;
+      final long b = j & 0xFFFFFFFFL;
+      return a < b ? -1 : a == b ? 0 : 1;
+    }
+
+    @Override
+    protected int compare(int i, int j) {
+      return compareValues(arr[i], arr[j]);
+    }
+
+    @Override
+    protected void setPivot(int i) {
+      pivot = arr[i];
+    }
+
+    @Override
+    protected int comparePivot(int j) {
+      return compareValues(pivot, arr[j]);
+    }
+
+  }
+
+  void testSort(int[] intArr) {
+    // we modify the array as a long[] and store the original ord in the first 32 bits
+    // to be able to check stability
+    final long[] arr = toLongsAndOrds(intArr);
+
+    // use MergeSort as a reference
+    // assertArrayEquals checks for sorting + stability
+    // assertArrayEquals(toInts) checks for sorting only
+    final long[] mergeSorted = Arrays.copyOf(arr, arr.length);
+    new Last32BitsSorterTemplate(mergeSorted).mergeSort(0, arr.length - 1);
+
+    if (arr.length < SLOW_SORT_THRESHOLD) {
+      final long[] insertionSorted = Arrays.copyOf(arr, arr.length);
+      new Last32BitsSorterTemplate(insertionSorted).insertionSort(0, arr.length - 1);
+      assertArrayEquals(mergeSorted, insertionSorted);
+      
+      final long[] binarySorted = Arrays.copyOf(arr, arr.length);
+      new Last32BitsSorterTemplate(binarySorted).binarySort(0, arr.length - 1);
+      assertArrayEquals(mergeSorted, binarySorted);
+    }
+
+    final long[] quickSorted = Arrays.copyOf(arr, arr.length);
+    new Last32BitsSorterTemplate(quickSorted).quickSort(0, arr.length - 1);
+    assertArrayEquals(toInts(mergeSorted), toInts(quickSorted));
+
+    final long[] timSorted = Arrays.copyOf(arr, arr.length);
+    new Last32BitsSorterTemplate(timSorted).timSort(0, arr.length - 1);
+    assertArrayEquals(mergeSorted, timSorted);
+  }
+
+  private int[] toInts(long[] longArr) {
+    int[] arr = new int[longArr.length];
+    for (int i = 0; i < longArr.length; ++i) {
+      arr[i] = (int) longArr[i];
+    }
+    return arr;
+  }
+
+  private long[] toLongsAndOrds(int[] intArr) {
+    final long[] arr = new long[intArr.length];
+    for (int i = 0; i < intArr.length; ++i) {
+      arr[i] = (((long) i) << 32) | (intArr[i] & 0xFFFFFFFFL);
+    }
+    return arr;
+  }
+
+  int randomLength() {
+    return random().nextBoolean()
+        ? random().nextInt(SLOW_SORT_THRESHOLD)
+        : random().nextInt(100000);
+  }
+
+  public void testAscending() {
+    final int length = randomLength();
+    final int[] arr = new int[length];
+    arr[0] = random().nextInt(10);
+    for (int i = 1; i < arr.length; ++i) {
+      arr[i] = arr[i-1] + _TestUtil.nextInt(random(), 0, 10);
+    }
+    testSort(arr);
+  }
+
+  public void testDescending() {
+    final int length = randomLength();
+    final int[] arr = new int[length];
+    arr[0] = random().nextInt(10);
+    for (int i = 1; i < arr.length; ++i) {
+      arr[i] = arr[i-1] - _TestUtil.nextInt(random(), 0, 10);
+    }
+    testSort(arr);
+  }
+
+  public void testStrictlyDescending() {
+    final int length = randomLength();
+    final int[] arr = new int[length];
+    arr[0] = random().nextInt(10);
+    for (int i = 1; i < arr.length; ++i) {
+      arr[i] = arr[i-1] - _TestUtil.nextInt(random(), 1, 10);
+    }
+    testSort(arr);
+  }
+
+  public void testRandom1() {
+    final int length = randomLength();
+    final int[] arr = new int[length];
+    for (int i = 1; i < arr.length; ++i) {
+      arr[i] = random().nextInt();
+    }
+    testSort(arr);
+  }
+
+  public void testRandom2() {
+    final int length = randomLength();
+    final int[] arr = new int[length];
+    for (int i = 1; i < arr.length; ++i) {
+      arr[i] = random().nextInt(10);
+    }
+    testSort(arr);
+  }
+
+}

Modification de propriétés sur lucene/core/src/test/org/apache/lucene/util/TestSorterTemplate.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: lucene/misc/src/java/org/apache/lucene/index/sorter/Sorter.java
===================================================================
--- lucene/misc/src/java/org/apache/lucene/index/sorter/Sorter.java	(révision 1457068)
+++ lucene/misc/src/java/org/apache/lucene/index/sorter/Sorter.java	(copie de travail)
@@ -147,8 +147,9 @@
     }
     
     SorterTemplate sorter = new DocValueSorterTemplate(docs, comparator);
-    // TODO: use a stable sort instead?
-    sorter.quickSort(0, docs.length - 1); // docs is now the newToOld mapping
+    // It can be common to sort a reader, add docs, sort it again, ... and in
+    // that case timSort can save a lot of time
+    sorter.timSort(0, docs.length - 1); // docs is now the newToOld mapping
 
     // The reason why we use MonotonicAppendingLongBuffer here is that it
     // wastes very little memory if the index is in random order but can save
Index: lucene/misc/src/java/org/apache/lucene/index/sorter/SortingAtomicReader.java
===================================================================
--- lucene/misc/src/java/org/apache/lucene/index/sorter/SortingAtomicReader.java	(révision 1457068)
+++ lucene/misc/src/java/org/apache/lucene/index/sorter/SortingAtomicReader.java	(copie de travail)
@@ -307,9 +307,11 @@
         docs[i] = docs[j];
         docs[j] = tmpDoc;
         
-        int tmpFreq = freqs[i];
-        freqs[i] = freqs[j];
-        freqs[j] = tmpFreq;
+        if (freqs != null) {
+          int tmpFreq = freqs[i];
+          freqs[i] = freqs[j];
+          freqs[j] = tmpFreq;
+        }
       }
     }
     
@@ -335,8 +337,6 @@
           freqs[i] = in.freq();
           ++i;
         }
-        SorterTemplate sorter = new DocFreqSorterTemplate(docs, freqs);
-        sorter.quickSort(0, i - 1);
       } else {
         freqs = null;
         while ((doc = in.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS){
@@ -345,8 +345,10 @@
           }
           docs[i++] = docMap.oldToNew(doc);
         }
-        Arrays.sort(docs, 0, i);
       }
+      // TimSort can save much time compared to other sorts in case of
+      // reverse sorting, or when sorting a concatenation of sorted readers
+      new DocFreqSorterTemplate(docs, freqs).timSort(0, i - 1);
       upto = i;
     }
     
@@ -451,12 +453,9 @@
       int i = 0;
       while ((doc = in.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
         if (i == docs.length) {
-          docs = ArrayUtil.grow(docs, docs.length + 1);
-          // don't grow() offsets since growing pattern for long and int is not the same.
-          // since we want docs and offsets at the same length, just grow it manually.
-          long[] tmp = new long[docs.length];
-          System.arraycopy(offsets, 0, tmp, 0, offsets.length);
-          offsets = tmp;
+          final int newLength = ArrayUtil.oversize(i + 1, 4);
+          docs = Arrays.copyOf(docs, newLength);
+          offsets = Arrays.copyOf(offsets, newLength);
         }
         docs[i] = docMap.oldToNew(doc);
         offsets[i] = out.getFilePointer();
@@ -464,8 +463,7 @@
         i++;
       }
       upto = i;
-      SorterTemplate sorter = new DocOffsetSorterTemplate(docs, offsets);
-      sorter.quickSort(0, upto - 1);
+      new DocOffsetSorterTemplate(docs, offsets).timSort(0, upto - 1);
       out.close();
       this.postingInput = new RAMInputStream("", file);
     }
