Index: lucene/test-framework/src/java/org/apache/lucene/util/TestRuleSetupAndRestoreClassEnv.java
===================================================================
--- lucene/test-framework/src/java/org/apache/lucene/util/TestRuleSetupAndRestoreClassEnv.java	(revision 1362013)
+++ lucene/test-framework/src/java/org/apache/lucene/util/TestRuleSetupAndRestoreClassEnv.java	(working copy)
@@ -157,7 +157,7 @@
     PREFLEX_IMPERSONATION_IS_ACTIVE = false;
     savedCodec = Codec.getDefault();
     int randomVal = random.nextInt(10);
-
+    
     /* note: re-enable this if we make a 4.x impersonator
       if ("Lucene3x".equals(TEST_CODEC) || ("random".equals(TEST_CODEC) &&
                                           "random".equals(TEST_POSTINGSFORMAT) &&
Index: lucene/core/src/test/org/apache/lucene/index/TestPostingsFormat.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/index/TestPostingsFormat.java	(revision 0)
+++ lucene/core/src/test/org/apache/lucene/index/TestPostingsFormat.java	(working copy)
@@ -0,0 +1,821 @@
+package org.apache.lucene.index;
+
+/*
+ * 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.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.apache.lucene.codecs.Codec;
+import org.apache.lucene.codecs.FieldsConsumer;
+import org.apache.lucene.codecs.FieldsProducer;
+import org.apache.lucene.codecs.PostingsConsumer;
+import org.apache.lucene.codecs.TermStats;
+import org.apache.lucene.codecs.TermsConsumer;
+import org.apache.lucene.index.FieldInfo.IndexOptions;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.IOContext;
+import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.Constants;
+import org.apache.lucene.util.FixedBitSet;
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.util._TestUtil;
+import org.junit.BeforeClass;
+
+/* NOTE: This test focuses on the postings
+ * (docs/freqs/positions/payloads/offsets) impl, not the
+ * terms dict.  The [stretch] goal is for this test to be
+ * so thorough in testing a new PostingsFormat that if this
+ * test passes, then all Lucene/Solr tests should also pass.  Ie,
+ * if there is some bug in a given PostingsFormat that this
+ * test fails to catch then this test needs to be improved! */
+
+// nocommit can we make it easy for testing to pair up a "random terms dict impl" with your postings base format...
+
+// nocommit test when you reuse after skipping a term or two, eg the block reuse case
+
+// nocommit hmm contract says .doc() can return NO_MORE_DOCS
+// before nextDoc too...?
+
+/* TODO
+  - threads
+  - assert doc=-1 before any nextDoc
+  - if a PF passes this test but fails other tests then this
+    test has a bug!!
+  - test tricky reuse cases, eg across fields
+  - verify you get null if you pass needFreq/needOffset but
+    they weren't indexed
+*/
+
+public class TestPostingsFormat extends LuceneTestCase {
+
+  private enum Option {
+    // Sometimes use .advance():
+    SKIPPING,
+
+    // Sometimes reuse the Docs/AndPositionsEnum across terms:
+    REUSE_ENUMS,
+
+    // Sometimes pass non-null live docs:
+    LIVE_DOCS,
+
+    // Sometimes seek to term using previously saved TermState:
+    TERM_STATE,
+
+    // Sometimes don't fully consume docs from the enum
+    PARTIAL_DOC_CONSUME,
+
+    // Sometimes don't fully consume positions at each doc
+    PARTIAL_POS_CONSUME,
+
+    // Sometimes check payloads
+    PAYLOADS,
+
+    // Test w/ multiple threads
+    THREADS};
+
+  private static class FieldAndTerm {
+    String field;
+    BytesRef term;
+
+    public FieldAndTerm(String field, BytesRef term) {
+      this.field = field;
+      this.term = BytesRef.deepCopyOf(term);
+    }
+  }
+
+  private static class Position {
+    int position;
+    byte[] payload;
+    int startOffset;
+    int endOffset;
+  }
+
+  private static class Posting implements Comparable<Posting>{
+    int docID;
+    List<Position> positions;
+
+    public int compareTo(Posting other) {
+      return docID - other.docID;
+    }
+  }
+
+  // Holds all postings:
+  private static Map<String,Map<BytesRef,List<Posting>>> fields = new TreeMap<String,Map<BytesRef,List<Posting>>>();
+
+  // Holds only live doc postings:
+  private static Map<String,Map<BytesRef,List<Posting>>> fieldsLive = new TreeMap<String,Map<BytesRef,List<Posting>>>();
+
+  private static FieldInfos fieldInfos;
+
+  private static int maxDocID;
+
+  private static FixedBitSet globalLiveDocs;
+
+  private static List<FieldAndTerm> allTerms;
+
+  @BeforeClass
+  public static void createPostings() throws IOException {
+
+    // nocommit
+    //final int numFields = _TestUtil.nextInt(random(), 1, 3);
+    final int numFields = 1;
+    if (VERBOSE) {
+      System.out.println("TEST: " + numFields + " fields");
+    }
+
+    FieldInfo[] fieldInfoArray = new FieldInfo[numFields];
+    int fieldUpto = 0;
+    int numMediumTerms = 0;
+    int numBigTerms = 0;
+    int numManyPositions = 0;
+    while (fieldUpto < numFields) {
+      String field = _TestUtil.randomSimpleString(random());
+      if (fields.containsKey(field)) {
+        continue;
+      }
+
+      boolean fieldHasPayloads = random().nextBoolean();
+
+      fieldInfoArray[fieldUpto] = new FieldInfo(field, true, fieldUpto, false, false, fieldHasPayloads,
+                                                IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS,
+                                                null, DocValues.Type.FIXED_INTS_8, null);
+      fieldUpto++;
+
+      Map<BytesRef,List<Posting>> postings = new TreeMap<BytesRef,List<Posting>>();
+      fields.put(field, postings);
+      Set<String> seenTerms = new HashSet<String>();
+
+      // nocommit
+      //final int numTerms = atLeast(10);
+      final int numTerms = 4;
+      for(int termUpto=0;termUpto<numTerms;termUpto++) {
+        String term = _TestUtil.randomSimpleString(random());
+        if (seenTerms.contains(term)) {
+          continue;
+        }
+        seenTerms.add(term);
+
+        int numDocs;
+        if (random().nextInt(10) == 3 && numBigTerms < 3) {
+          // 10% of the time make a highish freq term:
+          numDocs = _TestUtil.nextInt(random(), 50000, 70000);
+          numBigTerms++;
+          term = "big_" + term;
+        } else if (random().nextInt(10) == 3 && numMediumTerms < 10) {
+          // 10% of the time make a medium freq term:
+          // nocommit not high enough to test level 1 skipping:
+          numDocs = atLeast(3000);
+          numMediumTerms++;
+          term = "medium_" + term;
+        } else {
+          // Low freq term:
+          numDocs = _TestUtil.nextInt(random(), 1, 40);
+          term = "low_" + term;
+        }
+
+        numDocs *= RANDOM_MULTIPLIER;
+
+        List<Posting> termPostings = new ArrayList<Posting>();
+        postings.put(new BytesRef(term), termPostings);
+
+        int docID = 0;
+
+        // TODO: more realistic to inversely tie this to numDocs:
+        int maxDocSpacing = _TestUtil.nextInt(random(), 1, 100);
+
+        // 10% of the time create big payloads:
+        int payloadSize;
+        if (!fieldHasPayloads) {
+          payloadSize = 0;
+        } else if (random().nextInt(10) == 7) {
+          payloadSize = random().nextInt(50);
+        } else {
+          payloadSize = random().nextInt(10);
+        }
+
+        boolean fixedPayloads = random().nextBoolean();
+
+        for(int docUpto=0;docUpto<numDocs;docUpto++) {
+          if (docUpto == 0 && random().nextBoolean()) {
+            // Sometimes index docID = 0
+          } else if (maxDocSpacing == 1) {
+            docID++;
+          } else {
+            // nocommit: sometimes have a biggish gap here!
+            docID += _TestUtil.nextInt(random(), 1, maxDocSpacing);
+          }
+
+          Posting posting = new Posting();
+          posting.docID = docID;
+          maxDocID = Math.max(docID, maxDocID);
+          posting.positions = new ArrayList<Position>();
+          termPostings.add(posting);
+
+          int freq;
+          if (random().nextInt(30) == 17 && numManyPositions < 10) {
+            freq = _TestUtil.nextInt(random(), 1, 1000);
+            numManyPositions++;
+          } else {
+            freq = _TestUtil.nextInt(random(), 1, 20);
+          }
+          int pos = 0;
+          int offset = 0;
+          int posSpacing = _TestUtil.nextInt(random(), 1, 100);
+          for(int posUpto=0;posUpto<freq;posUpto++) {
+            if (posUpto == 0 && random().nextBoolean()) {
+              // Sometimes index pos = 0
+            } else if (posSpacing == 1) {
+              pos++;
+            } else {
+              pos += _TestUtil.nextInt(random(), 1, posSpacing);
+            }
+
+            Position position = new Position();
+            posting.positions.add(position);
+            position.position = pos;
+            if (payloadSize != 0) {
+              if (fixedPayloads) {
+                position.payload = new byte[payloadSize];
+              } else {
+                int thisPayloadSize = random().nextInt(payloadSize);
+                if (thisPayloadSize != 0) {
+                  position.payload = new byte[thisPayloadSize];
+                }
+              }
+            }
+
+            if (position.payload != null) {
+              random().nextBytes(position.payload); 
+           }
+            // TODO: offsets
+          }
+        }
+      }
+    }
+
+    fieldInfos = new FieldInfos(fieldInfoArray);
+
+    globalLiveDocs = new FixedBitSet(1+maxDocID);
+    double liveRatio = random().nextDouble();
+    for(int i=0;i<1+maxDocID;i++) {
+      if (random().nextDouble() <= liveRatio) {
+        globalLiveDocs.set(i);
+      }
+    }
+
+    // Pre-filter postings by globalLiveDocs:
+    for(Map.Entry<String,Map<BytesRef,List<Posting>>> fieldEnt : fields.entrySet()) {
+      Map<BytesRef,List<Posting>> postingsLive = new TreeMap<BytesRef,List<Posting>>();
+      fieldsLive.put(fieldEnt.getKey(), postingsLive);
+      for(Map.Entry<BytesRef,List<Posting>> termEnt : fieldEnt.getValue().entrySet()) {
+        List<Posting> termPostingsLive = new ArrayList<Posting>();
+        postingsLive.put(termEnt.getKey(), termPostingsLive);
+        for(Posting posting : termEnt.getValue()) {
+          if (globalLiveDocs.get(posting.docID)) {
+            termPostingsLive.add(posting);
+          }
+        }
+      }
+    }
+
+    allTerms = new ArrayList<FieldAndTerm>();
+    for(Map.Entry<String,Map<BytesRef,List<Posting>>> fieldEnt : fields.entrySet()) {
+      String field = fieldEnt.getKey();
+      for(Map.Entry<BytesRef,List<Posting>> termEnt : fieldEnt.getValue().entrySet()) {
+        allTerms.add(new FieldAndTerm(field, termEnt.getKey()));
+      }
+    }
+
+    if (VERBOSE) {
+      System.out.println("TEST: done init postings; maxDocID=" + maxDocID + " " + allTerms.size() + " total term count across " + fieldInfos.size() + " fields");
+    }
+  }
+
+  // nocommit maybe instead of @BeforeClass just make a single test run: build postings & index & test it?
+
+  private FieldInfos currentFieldInfos;
+
+  // maxAllowed = the "highest" we can index, but we will still
+  // randomly index at lower IndexOption
+  private FieldsProducer buildIndex(Directory dir, IndexOptions maxAllowed, boolean allowPayloads) throws IOException {
+    SegmentInfo segmentInfo = new SegmentInfo(dir, Constants.LUCENE_MAIN_VERSION, "_0", 1+maxDocID, false, Codec.getDefault(), null, null);
+
+    int maxIndexOption = Arrays.asList(IndexOptions.values()).indexOf(maxAllowed);
+    if (VERBOSE) {
+      System.out.println("\nTEST: now build index");
+    }
+
+    // nocommit use allowPayloads
+
+    FieldInfo[] newFieldInfoArray = new FieldInfo[fields.size()];
+    for(int fieldUpto=0;fieldUpto<fields.size();fieldUpto++) {
+      FieldInfo oldFieldInfo = fieldInfos.fieldInfo(fieldUpto);
+
+      // Randomly picked the IndexOptions to index this
+      // field with:
+      IndexOptions indexOptions = IndexOptions.values()[random().nextInt(1+maxIndexOption)];
+      boolean doPayloads = indexOptions.compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) >= 0 && allowPayloads;
+
+      newFieldInfoArray[fieldUpto] = new FieldInfo(oldFieldInfo.name,
+                                                   true,
+                                                   fieldUpto,
+                                                   false,
+                                                   false,
+                                                   doPayloads,
+                                                   indexOptions,
+                                                   null,
+                                                   DocValues.Type.FIXED_INTS_8,
+                                                   null);
+    }
+
+    FieldInfos newFieldInfos = new FieldInfos(newFieldInfoArray);
+
+    SegmentWriteState writeState = new SegmentWriteState(null, dir,
+                                                         segmentInfo, newFieldInfos,
+                                                         32, null, IOContext.DEFAULT);
+
+    FieldsConsumer fieldsConsumer = Codec.getDefault().postingsFormat().fieldsConsumer(writeState);
+
+    for(Map.Entry<String,Map<BytesRef,List<Posting>>> fieldEnt : fields.entrySet()) {
+      String field = fieldEnt.getKey();
+      Map<BytesRef,List<Posting>> terms = fieldEnt.getValue();
+
+      FieldInfo fieldInfo = newFieldInfos.fieldInfo(field);
+      if (VERBOSE) {
+        System.out.println("field=" + field);
+      }
+
+      IndexOptions indexOptions = fieldInfo.getIndexOptions();
+
+      boolean doFreq = indexOptions.compareTo(IndexOptions.DOCS_AND_FREQS) >= 0;
+      boolean doPos = indexOptions.compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) >= 0;
+      boolean doOffsets = indexOptions.compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS) >= 0;
+      boolean doPayloads = indexOptions.compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) >= 0 && allowPayloads;
+      
+      TermsConsumer termsConsumer = fieldsConsumer.addField(fieldInfo);
+      long sumTotalTF = 0;
+      long sumDF = 0;
+      FixedBitSet seenDocs = new FixedBitSet(maxDocID+1);
+      for(Map.Entry<BytesRef,List<Posting>> termEnt : terms.entrySet()) {
+        BytesRef term = termEnt.getKey();
+        List<Posting> postings = termEnt.getValue();
+        if (VERBOSE) {
+          System.out.println("  term=" + field + ":" + term.utf8ToString() + " docFreq=" + postings.size());
+        }
+        
+        PostingsConsumer postingsConsumer = termsConsumer.startTerm(term);
+        long totalTF = 0;
+        int docCount = 0;
+        for(Posting posting : postings) {
+          if (VERBOSE) {
+            System.out.println("    " + docCount + ": docID=" + posting.docID + " freq=" + posting.positions.size());
+          }
+          postingsConsumer.startDoc(posting.docID, posting.positions.size());
+          seenDocs.set(posting.docID);
+          if (doPos) {
+            totalTF += posting.positions.size();
+            for(Position pos : posting.positions) {
+              if (VERBOSE) {
+                System.out.println("      pos=" + pos.position);
+              }
+              postingsConsumer.addPosition(pos.position, doPayloads ? new BytesRef(pos.payload) : null, pos.startOffset, pos.endOffset);
+            }
+          } else if (doFreq) {
+            totalTF += posting.positions.size();
+          } else {
+            totalTF++;
+          }
+          docCount++;
+        }
+        termsConsumer.finishTerm(term, new TermStats(postings.size(), totalTF));
+        sumTotalTF += totalTF;
+        sumDF += postings.size();
+      }
+
+      termsConsumer.finish(sumTotalTF, sumDF, seenDocs.cardinality());
+    }
+
+    fieldsConsumer.close();
+
+    if (VERBOSE) {
+      System.out.println("TEST: after indexing: files=");
+      for(String file : dir.listAll()) {
+        System.out.println("  " + file + ": " + dir.fileLength(file) + " bytes");
+      }
+    }
+
+    currentFieldInfos = newFieldInfos;
+
+    SegmentReadState readState = new SegmentReadState(dir, segmentInfo, newFieldInfos, IOContext.DEFAULT, 1);
+
+    return Codec.getDefault().postingsFormat().fieldsProducer(readState);
+  }
+
+  private static class ThreadState {
+    // Only used with REUSE option:
+    public DocsEnum reuseDocsEnum;
+    public DocsAndPositionsEnum reuseDocsAndPositionsEnum;
+  }
+
+  private void verifyEnum(ThreadState threadState,
+                          String field,
+                          BytesRef term,
+                          TermsEnum termsEnum,
+
+                          // Maximum options (docs/freqs/positions/offsets) to test:
+                          IndexOptions maxIndexOptions,
+
+                          EnumSet<Option> options) throws IOException {
+        
+    if (VERBOSE) {
+      System.out.println("  verifyEnum: options=" + options + " maxIndexOptions=" + maxIndexOptions);
+    }
+
+    // 50% of the time time pass liveDocs:
+    Bits liveDocs;
+    Map<String,Map<BytesRef,List<Posting>>> fieldsToUse;
+    if (options.contains(Option.LIVE_DOCS) && random().nextBoolean()) {
+      liveDocs = globalLiveDocs;
+      fieldsToUse = fieldsLive;
+      if (VERBOSE) {
+        System.out.println("  use liveDocs");
+      }
+    } else {
+      liveDocs = null;
+      fieldsToUse = fields;
+      if (VERBOSE) {
+        System.out.println("  no liveDocs");
+      }
+    }
+
+    FieldInfo fieldInfo = currentFieldInfos.fieldInfo(field);
+
+    assertEquals(fields.get(field).get(term).size(), termsEnum.docFreq());
+
+    // NOTE: can be empty list if we are using liveDocs:
+    List<Posting> expected = fieldsToUse.get(field).get(term);
+    
+    boolean allowFreqs = fieldInfo.getIndexOptions().compareTo(IndexOptions.DOCS_AND_FREQS) >= 0 &&
+      maxIndexOptions.compareTo(IndexOptions.DOCS_AND_FREQS) >= 0;
+    boolean doCheckFreqs = allowFreqs && random().nextInt(3) <= 2;
+
+    boolean allowPositions = fieldInfo.getIndexOptions().compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) >= 0 &&
+      maxIndexOptions.compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) >= 0;
+    boolean doCheckPositions = allowPositions && random().nextInt(3) <= 2;
+
+    boolean doCheckPayloads = options.contains(Option.PAYLOADS) && allowPositions && fieldInfo.hasPayloads();
+
+    DocsEnum prevDocsEnum = null;
+
+    DocsEnum docsEnum;
+    DocsAndPositionsEnum docsAndPositionsEnum;
+
+    if (!doCheckPositions) {
+      if (allowPositions && random().nextInt(10) == 7) {
+        // 10% of the time, even though we will not check positions, pull a DocsAndPositions enum
+        if (VERBOSE) {
+          System.out.println("  get DocsAndPositionsEnum (but we won't check positions)");
+        }
+        
+        if (options.contains(Option.REUSE_ENUMS) && random().nextInt(10) < 9) {
+          prevDocsEnum = threadState.reuseDocsAndPositionsEnum;
+        }
+
+        threadState.reuseDocsAndPositionsEnum = termsEnum.docsAndPositions(liveDocs, (DocsAndPositionsEnum) prevDocsEnum, false);
+        docsEnum = threadState.reuseDocsAndPositionsEnum;
+        docsAndPositionsEnum = threadState.reuseDocsAndPositionsEnum;
+      } else {
+        if (VERBOSE) {
+          System.out.println("  get DocsEnum");
+        }
+        if (options.contains(Option.REUSE_ENUMS) && random().nextInt(10) < 9) {
+          prevDocsEnum = threadState.reuseDocsEnum;
+        }
+        threadState.reuseDocsEnum = termsEnum.docs(liveDocs, prevDocsEnum, doCheckFreqs);
+        docsEnum = threadState.reuseDocsEnum;
+        docsAndPositionsEnum = null;
+      }
+    } else {
+      if (VERBOSE) {
+        System.out.println("  get DocsAndPositionsEnum");
+      }
+      if (options.contains(Option.REUSE_ENUMS) && random().nextInt(10) < 9) {
+        prevDocsEnum = threadState.reuseDocsAndPositionsEnum;
+      }
+      threadState.reuseDocsAndPositionsEnum = termsEnum.docsAndPositions(liveDocs, (DocsAndPositionsEnum) prevDocsEnum, false);
+      docsEnum = threadState.reuseDocsAndPositionsEnum;
+      docsAndPositionsEnum = threadState.reuseDocsAndPositionsEnum;
+    }
+
+    assertNotNull(docsEnum);
+    int initialDocID = docsEnum.docID();
+    assertTrue(initialDocID == -1 || initialDocID == DocsEnum.NO_MORE_DOCS);
+
+    if (VERBOSE) {
+      if (prevDocsEnum == null) {
+        System.out.println("  got enum=" + docsEnum);
+      } else if (prevDocsEnum == docsEnum) {
+        System.out.println("  got reuse enum=" + docsEnum);
+      } else {
+        System.out.println("  got enum=" + docsEnum + " (reuse of " + prevDocsEnum + " failed)");
+      }
+    }
+
+    // 10% of the time don't consume all docs:
+    int stopAt;
+    if (options.contains(Option.PARTIAL_DOC_CONSUME) && expected.size() > 1 && random().nextInt(10) == 7) {
+      stopAt = random().nextInt(expected.size()-1);
+      if (VERBOSE) {
+        System.out.println("  will not consume all docs (" + stopAt + " vs " + expected.size() + ")");
+      }
+    } else {
+      stopAt = expected.size();
+      if (VERBOSE) {
+        System.out.println("  consume all docs");
+      }
+    }
+
+    double skipChance = random().nextDouble();
+    int numSkips = expected.size() < 3 ? 1 : _TestUtil.nextInt(random(), 1, Math.min(20, expected.size()/3));
+    int skipInc = expected.size()/numSkips;
+    int skipDocInc = (1+maxDocID)/numSkips;
+
+    // Sometimes do 100% skipping:
+    boolean doAllSkipping = options.contains(Option.SKIPPING) && random().nextInt(7) == 1;
+
+    double freqAskChance = random().nextDouble();
+
+    if (VERBOSE) {
+      if (options.contains(Option.SKIPPING)) {
+        System.out.println("  skipChance=" + skipChance + " numSkips=" + numSkips);
+      } else {
+        System.out.println("  no skipping");
+      }
+      if (doCheckFreqs) {
+        System.out.println("  freqAskChance=" + freqAskChance);
+      }
+    }
+
+    int nextPosting = 0;
+    while (nextPosting <= stopAt) {
+      if (nextPosting == stopAt) {
+        if (stopAt == expected.size()) {
+          assertEquals(DocsEnum.NO_MORE_DOCS, docsEnum.nextDoc());
+
+          // Common bug is to forget to set this.doc=NO_MORE_DOCS in the enum!:
+          assertEquals(DocsEnum.NO_MORE_DOCS, docsEnum.docID());
+        }
+        break;
+      }
+
+      Posting posting;
+      if (options.contains(Option.SKIPPING) && (doAllSkipping || random().nextDouble() <= skipChance)) {
+        int targetDocID = -1;
+        if (nextPosting < stopAt && random().nextBoolean()) {
+          // Pick target we know exists:
+          nextPosting = _TestUtil.nextInt(random(), nextPosting, nextPosting+skipInc);
+        } else {
+          // Pick random target (might not exist):
+          Posting target = new Posting();
+          target.docID = _TestUtil.nextInt(random(), expected.get(nextPosting).docID, expected.get(nextPosting).docID+skipDocInc);
+          targetDocID = target.docID;
+          int loc = Collections.binarySearch(expected.subList(nextPosting, expected.size()), target);
+          if (loc < 0) {
+            loc = -loc-1;
+          }
+          nextPosting = nextPosting + loc;
+        }
+
+        if (nextPosting >= stopAt) {
+          int target = random().nextBoolean() ? (maxDocID+1) : DocsEnum.NO_MORE_DOCS;
+          if (VERBOSE) {
+            System.out.println("  now advance to end (target=" + target + ")");
+          }
+          assertEquals(DocsEnum.NO_MORE_DOCS, docsEnum.advance(target));
+          break;
+        } else {
+          posting = expected.get(nextPosting++);
+          if (VERBOSE) {
+            if (targetDocID != -1) {
+              System.out.println("  now advance to random target=" + targetDocID + " (" + nextPosting + " of " + stopAt + ")");
+            } else {
+              System.out.println("  now advance to known-exists target=" + posting.docID + " (" + nextPosting + " of " + stopAt + ")");
+            }
+          }
+          int docID = docsEnum.advance(targetDocID != -1 ? targetDocID : posting.docID);
+          assertEquals(posting.docID, docID);
+        }
+      } else {
+        posting = expected.get(nextPosting++);
+        if (VERBOSE) {
+          System.out.println("  now nextDoc to " + posting.docID + " (" + nextPosting + " of " + stopAt + ")");
+        }
+        int docID = docsEnum.nextDoc();
+        assertEquals(posting.docID, docID);
+      }
+
+      if (doCheckFreqs && random().nextDouble() <= freqAskChance) {
+        if (VERBOSE) {
+          System.out.println("    now freq()=" + posting.positions.size());
+        }
+        int freq = docsEnum.freq();
+        assertEquals(posting.positions.size(), freq);
+      }
+
+      if (doCheckPositions) {
+        // nocommit sometimes don't consume all positions:
+        int freq = docsEnum.freq();
+        int numPosToConsume;
+        if (options.contains(Option.PARTIAL_POS_CONSUME)) {
+          numPosToConsume = random().nextInt(freq);
+        } else {
+          numPosToConsume = freq;
+        }
+
+        for(int i=0;i<numPosToConsume;i++) {
+          Position position = posting.positions.get(i);
+          if (VERBOSE) {
+            System.out.println("    now nextPosition to " + position.position);
+          }
+          assertEquals(position.position, docsAndPositionsEnum.nextPosition());
+
+          // nocommit offsets
+          if (doCheckPayloads) {
+            if (position.payload.length == 0) {
+              assertFalse(docsAndPositionsEnum.hasPayload());
+            } else {
+              assertTrue(docsAndPositionsEnum.hasPayload());
+
+              BytesRef payload = docsAndPositionsEnum.getPayload();
+              assertFalse(docsAndPositionsEnum.hasPayload());
+
+              assertNotNull(payload);
+              assertEquals(position.payload.length, payload.length);
+              for(int byteUpto=0;byteUpto<position.payload.length;byteUpto++) {
+                assertEquals(position.payload[byteUpto],
+                             payload.bytes[payload.offset+byteUpto]);
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  private void testTerms(final Fields fieldsSource, final EnumSet<Option> options, final IndexOptions maxIndexOptions) throws Exception {
+
+    if (options.contains(Option.THREADS)) {
+      int numThreads = _TestUtil.nextInt(random(), 2, 5);
+      Thread[] threads = new Thread[numThreads];
+      for(int threadUpto=0;threadUpto<numThreads;threadUpto++) {
+        threads[threadUpto] = new Thread() {
+            @Override
+            public void run() {
+              try {
+                testTermsOneThread(fieldsSource, options, maxIndexOptions);
+              } catch (Throwable t) {
+                throw new RuntimeException(t);
+              }
+            }
+          };
+        threads[threadUpto].start();
+      }
+      for(int threadUpto=0;threadUpto<numThreads;threadUpto++) {
+        threads[threadUpto].join();
+      }
+    } else {
+      testTermsOneThread(fieldsSource, options, maxIndexOptions);
+    }
+  }
+
+  private void testTermsOneThread(Fields fieldsSource, EnumSet<Option> options, IndexOptions maxIndexOptions) throws IOException {
+
+    ThreadState threadState = new ThreadState();
+
+    // Test random terms/fields:
+    DocsEnum docsEnum = null;
+    DocsAndPositionsEnum docsAndPositionsEnum = null;
+
+    List<TermState> termStates = new ArrayList<TermState>();
+    List<FieldAndTerm> termStateTerms = new ArrayList<FieldAndTerm>();
+    
+    Collections.shuffle(allTerms, random());
+    int upto = 0;
+    while (upto < allTerms.size()) {
+
+      boolean useTermState = termStates.size() != 0 && random().nextInt(5) == 1;
+      FieldAndTerm fieldAndTerm;
+      TermsEnum termsEnum;
+
+      TermState termState = null;
+
+      if (!useTermState) {
+        // Seek by random field+term:
+        fieldAndTerm = allTerms.get(upto++);
+        if (VERBOSE) {
+          System.out.println("\nTEST: seek to term=" + fieldAndTerm.field + ":" + fieldAndTerm.term.utf8ToString() );
+        }
+      } else {
+        // Seek by previous saved TermState
+        int idx = random().nextInt(termStates.size());
+        fieldAndTerm = termStateTerms.get(idx);
+        if (VERBOSE) {
+          System.out.println("\nTEST: seek using TermState to term=" + fieldAndTerm.field + ":" + fieldAndTerm.term.utf8ToString());
+        }
+        termState = termStates.get(idx);
+      }
+
+      Terms terms = fieldsSource.terms(fieldAndTerm.field);
+      assertNotNull(terms);
+      termsEnum = terms.iterator(null);
+
+      if (!useTermState) {
+        assertTrue(termsEnum.seekExact(fieldAndTerm.term, true));
+      } else {
+        termsEnum.seekExact(fieldAndTerm.term, termState);
+      }
+
+      boolean savedTermState = false;
+
+      if (options.contains(Option.TERM_STATE) && !useTermState && random().nextInt(5) == 1) {
+        // Save away this TermState:
+        termStates.add(termsEnum.termState());
+        termStateTerms.add(fieldAndTerm);
+        savedTermState = true;
+      }
+
+      verifyEnum(threadState,
+                 fieldAndTerm.field,
+                 fieldAndTerm.term,
+                 termsEnum,
+                 maxIndexOptions,
+                 options);
+
+      // Sometimes save term state after pulling the enum:
+      if (options.contains(Option.TERM_STATE) && !useTermState && !savedTermState && random().nextInt(5) == 1) {
+        // Save away this TermState:
+        termStates.add(termsEnum.termState());
+        termStateTerms.add(fieldAndTerm);
+        useTermState = true;
+      }
+
+      // 10% of the time make sure you can pull another enum
+      // from the same term:
+      if (random().nextInt(10) == 7) {
+        // Try same term again
+        if (VERBOSE) {
+          System.out.println("TEST: try enum again on same term");
+        }
+
+        verifyEnum(threadState,
+                   fieldAndTerm.field,
+                   fieldAndTerm.term,
+                   termsEnum,
+                   maxIndexOptions,
+                   options);
+      }
+    }
+  }
+
+  public void test() throws Exception {
+    Directory dir = newDirectory();
+
+    boolean indexPayloads = false;
+    // nocommit test thread safety of buildIndex too
+    FieldsProducer fieldsProducer = buildIndex(dir, IndexOptions.DOCS_AND_FREQS_AND_POSITIONS, indexPayloads);
+
+    //testTerms(fieldsProducer, EnumSet.noneOf(Option.class), IndexOptions.DOCS_AND_FREQS_AND_POSITIONS);
+    //testTerms(fieldsProducer, EnumSet.of(Option.LIVE_DOCS), IndexOptions.DOCS_AND_FREQS_AND_POSITIONS);
+    //testTerms(fieldsProducer, EnumSet.of(Option.TERM_STATE, Option.LIVE_DOCS, Option.PARTIAL_DOC_CONSUME, Option.PARTIAL_POS_CONSUME), IndexOptions.DOCS_AND_FREQS_AND_POSITIONS);
+
+    //testTerms(fieldsProducer, EnumSet.of(Option.SKIPPING), IndexOptions.DOCS_AND_FREQS_AND_POSITIONS);
+    testTerms(fieldsProducer, EnumSet.of(Option.THREADS, Option.TERM_STATE, Option.SKIPPING, Option.PARTIAL_DOC_CONSUME, Option.PARTIAL_POS_CONSUME), IndexOptions.DOCS_AND_FREQS_AND_POSITIONS);
+
+    fieldsProducer.close();
+    dir.close();
+  }
+}
Index: lucene/core/src/java/org/apache/lucene/codecs/pfor/ForUtil.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/pfor/ForUtil.java	(revision 1362013)
+++ lucene/core/src/java/org/apache/lucene/codecs/pfor/ForUtil.java	(working copy)
@@ -73,6 +73,9 @@
     // since this buffer is reused at upper level, rewind first
     intBuffer.rewind();
 
+    // nocommit assert header isn't "malformed", ie besides
+    // numBytes / bit-width there is nothing else!
+
     int numBits = ((header >> 8) & MASK[6]);
 
     decompressCore(intBuffer, data, numBits);
Index: lucene/core/src/java/org/apache/lucene/codecs/pfor/ForPostingsFormat.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/pfor/ForPostingsFormat.java	(revision 1362013)
+++ lucene/core/src/java/org/apache/lucene/codecs/pfor/ForPostingsFormat.java	(working copy)
@@ -17,6 +17,8 @@
  * limitations under the License.
  */
 
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
 import java.io.IOException;
 import java.util.Set;
 
@@ -33,12 +35,17 @@
 import org.apache.lucene.codecs.TermsIndexWriterBase;
 import org.apache.lucene.codecs.sep.SepPostingsReader;
 import org.apache.lucene.codecs.sep.SepPostingsWriter;
+import org.apache.lucene.codecs.fixed.FixedPostingsReader.BlockReader;
+import org.apache.lucene.codecs.fixed.FixedPostingsReader;
+import org.apache.lucene.codecs.fixed.FixedPostingsWriter;
 import org.apache.lucene.index.SegmentInfo;
 import org.apache.lucene.index.SegmentReadState;
 import org.apache.lucene.index.SegmentWriteState;
 import org.apache.lucene.store.IOContext;
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.IOUtils;
+import org.apache.lucene.store.IndexInput;
+import org.apache.lucene.store.IndexOutput;
 
 /**
  * Pass ForFactory to a PostingsWriter/ReaderBase, and get 
@@ -71,7 +78,20 @@
   @Override
   public FieldsConsumer fieldsConsumer(SegmentWriteState state) throws IOException {
     // TODO: implement a new PostingsWriterBase to improve skip-settings
-    PostingsWriterBase postingsWriter = new SepPostingsWriter(state, new ForFactory()); 
+    PostingsWriterBase postingsWriter = new FixedPostingsWriter(state, 128) {
+        private final byte[] encoded = new byte[128*4 + 4];
+        private final IntBuffer encodedBuffer = ByteBuffer.wrap(encoded).asIntBuffer();
+
+        @Override
+        protected void writeBlock(int[] buffer, IndexOutput out) throws IOException {
+          final int header = ForUtil.compress(buffer, encodedBuffer);
+          final int numBytes = ForUtil.getEncodedSize(header);
+          //System.out.println("    block has " + numBytes + " bytes");
+          out.writeVInt(header);
+          out.writeBytes(encoded, numBytes);
+        }
+      };
+
     boolean success = false;
     try {
       FieldsConsumer ret = new BlockTreeTermsWriter(state, 
@@ -89,13 +109,36 @@
 
   @Override
   public FieldsProducer fieldsProducer(SegmentReadState state) throws IOException {
-    PostingsReaderBase postingsReader = new SepPostingsReader(state.dir,
-                                                              state.fieldInfos,
-                                                              state.segmentInfo,
-                                                              state.context,
-                                                              new ForFactory(),
-                                                              state.segmentSuffix);
+    PostingsReaderBase postingsReader = new FixedPostingsReader(state.dir,
+                                                                state.fieldInfos,
+                                                                state.segmentInfo,
+                                                                state.context,
+                                                                state.segmentSuffix,
+                                                                128) {
+        @Override
+        protected BlockReader getBlockReader(final IndexInput in) {
+          return new BlockReader() {
+            private final byte[] encoded = new byte[128*4 + 4];
+            private final IntBuffer encodedBuffer = ByteBuffer.wrap(encoded).asIntBuffer();
 
+            @Override
+            public void readBlock(int[] buffer) throws IOException {
+              final int header = in.readVInt();
+              final int numBytes = ForUtil.getEncodedSize(header);
+              assert numBytes <= 128*4+4;
+              in.readBytes(encoded, 0, numBytes);
+              ForUtil.decompress(encodedBuffer, buffer, header);
+            }
+
+            @Override
+            public void skipBlock() throws IOException {
+              final int header = in.readVInt();
+              in.seek(in.getFilePointer() + ForUtil.getEncodedSize(header));
+            }
+          };
+        }
+      };
+
     boolean success = false;
     try {
       FieldsProducer ret = new BlockTreeTermsReader(state.dir,
Index: lucene/core/src/java/org/apache/lucene/codecs/pfor/PForFactory.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/pfor/PForFactory.java	(revision 1362013)
+++ lucene/core/src/java/org/apache/lucene/codecs/pfor/PForFactory.java	(working copy)
@@ -120,9 +120,8 @@
     @Override
     protected void flushBlock() throws IOException {
       final int header = PForUtil.compress(buffer,encodedBuffer);
-      final int numBytes = PForUtil.getEncodedSize(header);
       out.writeInt(header);
-      out.writeBytes(encoded, numBytes);
+      out.writeBytes(encoded, PForUtil.getEncodedSize(header));
     }
   }
 }
Index: lucene/core/src/java/org/apache/lucene/codecs/pfor/ForFactory.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/pfor/ForFactory.java	(revision 1362013)
+++ lucene/core/src/java/org/apache/lucene/codecs/pfor/ForFactory.java	(working copy)
@@ -95,8 +95,8 @@
         final int header = in.readInt();
         final int numBytes = ForUtil.getEncodedSize(header);
         assert numBytes <= ForPostingsFormat.DEFAULT_BLOCK_SIZE*4;
-        in.readBytes(encoded,0,numBytes);
-        ForUtil.decompress(encodedBuffer,buffer,header);
+        in.readBytes(encoded, 0, numBytes);
+        ForUtil.decompress(encodedBuffer, buffer, header);
       }
     }
 
@@ -118,10 +118,9 @@
 
     @Override
     protected void flushBlock() throws IOException {
-      final int header = ForUtil.compress(buffer,encodedBuffer);
-      final int numBytes = ForUtil.getEncodedSize(header);
+      final int header = ForUtil.compress(buffer, encodedBuffer);
       out.writeInt(header);
-      out.writeBytes(encoded, numBytes);
+      out.writeBytes(encoded, ForUtil.getEncodedSize(header));
     }
   }
 }
Index: lucene/core/src/java/org/apache/lucene/codecs/MultiLevelSkipListReader.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/MultiLevelSkipListReader.java	(revision 1362013)
+++ lucene/core/src/java/org/apache/lucene/codecs/MultiLevelSkipListReader.java	(working copy)
@@ -141,6 +141,7 @@
 
     // read next skip entry
     skipDoc[level] += readSkipData(level, skipStream[level]);
+    //System.out.println("  skipDoc[level=" + level + "]=" + skipDoc[level]);
     
     if (level != 0) {
       // read the child pointer if we are not on the leaf level
@@ -148,7 +149,6 @@
     }
     
     return true;
-
   }
   
   /** Seeks the skip entry on the given level */
@@ -234,6 +234,7 @@
   protected void setLastSkipData(int level) {
     lastDoc = skipDoc[level];
     lastChildPointer = childPointer[level];
+    //System.out.println("super.setLastSkipData level=" + level + " doc=" + lastDoc);
   }
 
   
Index: lucene/core/src/java/org/apache/lucene/codecs/MultiLevelSkipListWriter.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/MultiLevelSkipListWriter.java	(revision 1362013)
+++ lucene/core/src/java/org/apache/lucene/codecs/MultiLevelSkipListWriter.java	(working copy)
@@ -110,7 +110,7 @@
     for (numLevels = 0; (df % skipInterval) == 0 && numLevels < numberOfSkipLevels; df /= skipInterval) {
       numLevels++;
     }
-    
+
     long childPointer = 0;
     
     for (int level = 0; level < numLevels; level++) {
Index: lucene/core/src/java/org/apache/lucene/codecs/BlockTreeTermsWriter.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/BlockTreeTermsWriter.java	(revision 1362013)
+++ lucene/core/src/java/org/apache/lucene/codecs/BlockTreeTermsWriter.java	(working copy)
@@ -725,7 +725,7 @@
           // Write term stats, to separate byte[] blob:
           bytesWriter2.writeVInt(term.stats.docFreq);
           if (fieldInfo.getIndexOptions() != IndexOptions.DOCS_ONLY) {
-            assert term.stats.totalTermFreq >= term.stats.docFreq;
+            assert term.stats.totalTermFreq >= term.stats.docFreq: term.stats.totalTermFreq + " vs " + term.stats.docFreq;
             bytesWriter2.writeVLong(term.stats.totalTermFreq - term.stats.docFreq);
           }
         }
Index: lucene/core/src/java/org/apache/lucene/codecs/fixed/FixedSkipWriter.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/fixed/FixedSkipWriter.java	(revision 0)
+++ lucene/core/src/java/org/apache/lucene/codecs/fixed/FixedSkipWriter.java	(working copy)
@@ -0,0 +1,99 @@
+package org.apache.lucene.codecs.fixed;
+
+/*
+ * 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.io.IOException;
+import java.util.Arrays;
+
+import org.apache.lucene.store.IndexOutput;
+import org.apache.lucene.codecs.MultiLevelSkipListWriter;
+
+// nocommit do we need more frequent skips at level > 0?
+// 128*128 is immense?  may need to decouple
+// baseSkipInterval & theRestSkipInterval?
+
+public class FixedSkipWriter extends MultiLevelSkipListWriter {
+  private int[] lastSkipDoc;
+  private long[] lastSkipDocPointer;
+  private long[] lastSkipPosPointer;
+
+  private boolean DEBUG = FixedPostingsReader.DEBUG;
+  
+  private final IndexOutput docOut;
+  private final IndexOutput posOut;
+
+  private int curDoc;
+  private long curDocPointer;
+  private long curPosPointer;
+  private int curPosBufferUpto;
+
+  public FixedSkipWriter(int skipInterval, int maxSkipLevels, int docCount, IndexOutput docOut, IndexOutput posOut) {
+    super(skipInterval, maxSkipLevels, docCount);
+    this.docOut = docOut;
+    this.posOut = posOut;
+    
+    lastSkipDoc = new int[maxSkipLevels];
+    lastSkipDocPointer = new long[maxSkipLevels];
+    lastSkipPosPointer = new long[maxSkipLevels];
+  }
+
+  /**
+   * Sets the values for the current skip data. 
+   */
+  public void bufferSkip(int doc, int numDocs, long posFP, int posBufferUpto) throws IOException {
+    this.curDoc = doc;
+    this.curDocPointer = docOut.getFilePointer();
+    if (posOut != null) {
+      this.curPosPointer = posFP;
+      this.curPosBufferUpto = posBufferUpto;
+    }
+    bufferSkip(numDocs);
+  }
+
+  @Override
+  public void resetSkip() {
+    super.resetSkip();
+    Arrays.fill(lastSkipDoc, 0);
+    Arrays.fill(lastSkipDocPointer, docOut.getFilePointer());
+    if (posOut != null) {
+      Arrays.fill(lastSkipPosPointer, posOut.getFilePointer());
+    }
+  }
+  
+  @Override
+  protected void writeSkipData(int level, IndexOutput skipBuffer) throws IOException {
+    int delta = curDoc - lastSkipDoc[level];
+    if (DEBUG) {
+      System.out.println("writeSkipData level=" + level + " lastDoc=" + curDoc + " delta=" + delta + " curDocPointer=" + curDocPointer);
+    }
+    skipBuffer.writeVInt(delta);
+    lastSkipDoc[level] = curDoc;
+
+    skipBuffer.writeVInt((int) (curDocPointer - lastSkipDocPointer[level]));
+    lastSkipDocPointer[level] = curDocPointer;
+
+    if (posOut != null) {
+      if (DEBUG) {
+        System.out.println("  curPosPointer=" + curPosPointer + " curPosBufferUpto=" + curPosBufferUpto);
+      }
+      skipBuffer.writeVInt((int) (curPosPointer - lastSkipPosPointer[level]));
+      lastSkipPosPointer[level] = curPosPointer;
+      skipBuffer.writeVInt(curPosBufferUpto);
+    }
+  }
+}
Index: lucene/core/src/java/org/apache/lucene/codecs/fixed/FixedPostingsWriter.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/fixed/FixedPostingsWriter.java	(revision 0)
+++ lucene/core/src/java/org/apache/lucene/codecs/fixed/FixedPostingsWriter.java	(working copy)
@@ -0,0 +1,416 @@
+package org.apache.lucene.codecs.fixed;
+
+/*
+ * 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.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.lucene.codecs.PostingsWriterBase;
+import org.apache.lucene.codecs.TermStats;
+import org.apache.lucene.index.CorruptIndexException;
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.index.FieldInfo.IndexOptions;
+import org.apache.lucene.index.FieldInfo;
+import org.apache.lucene.index.IndexFileNames;
+import org.apache.lucene.index.SegmentWriteState;
+import org.apache.lucene.store.IndexOutput;
+import org.apache.lucene.store.RAMOutputStream;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.CodecUtil;
+import org.apache.lucene.util.IOUtils;
+
+// nocommit javadocs
+
+public abstract class FixedPostingsWriter extends PostingsWriterBase {
+
+  private boolean DEBUG = FixedPostingsReader.DEBUG;
+
+  public static final String DOC_EXTENSION = "doc";
+  public static final String POS_EXTENSION = "pos";
+
+  static final int maxSkipLevels = 10;
+
+  final static String TERMS_CODEC = "FixedPostingsWriterTerms";
+  final static String DOC_CODEC = "FixedPostingsWriterDoc";
+  final static String POS_CODEC = "FixedPostingsWriterPos";
+
+  // Increment version to change it:
+  final static int VERSION_START = 0;
+  final static int VERSION_CURRENT = VERSION_START;
+
+  final IndexOutput docOut;
+  final IndexOutput posOut;
+
+  static final int DEFAULT_BLOCK_SIZE = 128;
+
+  final int blockSize;
+
+  private IndexOutput termsOut;
+
+  // How current field indexes postings:
+  private boolean fieldHasFreqs;
+  private boolean fieldHasPositions;
+
+  // Holds starting file pointers for each term:
+  private long docTermStartFP;
+  private long posTermStartFP;
+
+  final int[] docDeltaBuffer;
+  final int[] freqBuffer;
+  private int docBufferUpto;
+
+  final int[] posDeltaBuffer;
+  private int posBufferUpto;
+
+  private int lastPayloadLength;
+  private int lastBlockDocID;
+  private boolean saveNextPosBlock;
+  private long lastBlockPosFP;
+  private int lastBlockPosBufferUpto;
+  private int lastDocID;
+  private int lastPosition;
+  private int docCount;
+
+  private final FixedSkipWriter skipWriter;
+  
+  public FixedPostingsWriter(SegmentWriteState state, int blockSize) throws IOException {
+    super();
+    this.blockSize = blockSize;
+
+    docOut = state.directory.createOutput(IndexFileNames.segmentFileName(state.segmentInfo.name, state.segmentSuffix, DOC_EXTENSION),
+                                          state.context);
+    IndexOutput posOut = null;
+    boolean success = false;
+    try {
+      CodecUtil.writeHeader(docOut, DOC_CODEC, VERSION_CURRENT);
+      if (state.fieldInfos.hasProx()) {
+        posDeltaBuffer = new int[blockSize];
+        posOut = state.directory.createOutput(IndexFileNames.segmentFileName(state.segmentInfo.name, state.segmentSuffix, POS_EXTENSION),
+                                              state.context);
+        CodecUtil.writeHeader(posOut, POS_CODEC, VERSION_CURRENT);
+      } else {
+        posDeltaBuffer = null;
+      }
+      this.posOut = posOut;
+      success = true;
+    } finally {
+      if (!success) {
+        IOUtils.closeWhileHandlingException(docOut, posOut);
+      }
+    }
+
+    docDeltaBuffer = new int[blockSize];
+    freqBuffer = new int[blockSize];
+
+    skipWriter = new FixedSkipWriter(blockSize,
+                                     maxSkipLevels, 
+                                     state.segmentInfo.getDocCount(),
+                                     docOut,
+                                     posOut);
+  }
+
+  protected abstract void writeBlock(int[] buffer, IndexOutput out) throws IOException;
+
+  @Override
+  public void start(IndexOutput termsOut) throws IOException {
+    this.termsOut = termsOut;
+    CodecUtil.writeHeader(termsOut, TERMS_CODEC, VERSION_CURRENT);
+    termsOut.writeVInt(blockSize);
+  }
+
+  @Override
+  public void setField(FieldInfo fieldInfo) {
+    IndexOptions indexOptions = fieldInfo.getIndexOptions();
+    
+    if (indexOptions.compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS) >= 0) {
+      throw new IllegalArgumentException("this postings format cannot index offsets: " + indexOptions);
+    }
+    fieldHasFreqs = indexOptions.compareTo(IndexOptions.DOCS_AND_FREQS) >= 0;
+    fieldHasPositions = indexOptions.compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) >= 0;
+  }
+
+  @Override
+  public void startTerm() {
+    docTermStartFP = docOut.getFilePointer();
+    if (fieldHasPositions) {
+      posTermStartFP = posOut.getFilePointer();
+    }
+    // force first payload to write its length
+    lastPayloadLength = -1;
+    lastBlockDocID = -1;
+    lastDocID = 0;
+    if (DEBUG) {
+      System.out.println("FPW.startTerm startFP=" + docTermStartFP);
+    }
+    skipWriter.resetSkip();
+  }
+
+  @Override
+  public void startDoc(int docID, int termDocFreq) throws IOException {
+    if (DEBUG) {
+      System.out.println("FPW.startDoc docID=" + docID);
+    }
+
+    // nocommit do this in finishDoc... but does it fail...?
+    // is it not always called...?
+    if (posOut != null && saveNextPosBlock) {
+      lastBlockPosFP = posOut.getFilePointer();
+      lastBlockPosBufferUpto = posBufferUpto;
+      saveNextPosBlock = false;
+      if (DEBUG) {
+        System.out.println("  now save lastBlockPosFP=" + lastBlockPosFP + " lastBlockPosBufferUpto=" + lastBlockPosBufferUpto);
+      }
+    }
+
+    final int docDelta = docID - lastDocID;
+    if (docID < 0 || (docCount > 0 && docDelta <= 0)) {
+      throw new CorruptIndexException("docs out of order (" + docID + " <= " + lastDocID + " ) (docOut: " + docOut + ")");
+    }
+    lastDocID = docID;
+
+    docDeltaBuffer[docBufferUpto] = docDelta;
+    if (DEBUG) {
+      System.out.println("  docDeltaBuffer[" + docBufferUpto + "]=" + docDelta);
+    }
+    if (fieldHasFreqs) {
+      freqBuffer[docBufferUpto] = termDocFreq;
+    }
+
+    docBufferUpto++;
+    docCount++;
+
+    if (docBufferUpto == blockSize) {
+      // nocommit maybe instead of buffering skip before
+      // writing a block based on last block's end data
+      // ... we could buffer after writing the block?  only
+      // iffiness with that approach is it could be a
+      // pointlness skip?  like we may stop adding docs
+      // right after that, then we have skip point AFTER
+      // last doc.  the thing is, in finishTerm we are
+      // already sometimes adding a skip point AFTER the
+      // last doc?
+      if (lastBlockDocID != -1) {
+        if (DEBUG) {
+          System.out.println("  bufferSkip at writeBlock: lastDocID=" + lastBlockDocID + " docCount=" + (docCount-blockSize));
+        }
+        skipWriter.bufferSkip(lastBlockDocID, docCount-blockSize, lastBlockPosFP, lastBlockPosBufferUpto);
+      }
+      lastBlockDocID = docID;
+      saveNextPosBlock = true;
+
+      if (DEBUG) {
+        System.out.println("  write docDelta block @ fp=" + docOut.getFilePointer());
+      }
+      writeBlock(docDeltaBuffer, docOut);
+      if (fieldHasFreqs) {
+        if (DEBUG) {
+          System.out.println("  write freq block @ fp=" + docOut.getFilePointer());
+        }
+        writeBlock(freqBuffer, docOut);
+      }
+      docBufferUpto = 0;
+    }
+
+    lastPosition = 0;
+  }
+
+  /** Add a new position & payload */
+  @Override
+  public void addPosition(int position, BytesRef payload, int startOffset, int endOffset) throws IOException {
+    if (DEBUG) {
+      System.out.println("FPW.addPosition pos=" + position + " posBufferUpto=" + posBufferUpto);
+    }
+    posDeltaBuffer[posBufferUpto++] = position - lastPosition;
+    lastPosition = position;
+    if (posBufferUpto == blockSize) {
+      if (DEBUG) {
+        System.out.println("  write pos bulk block @ fp=" + posOut.getFilePointer());
+      }
+      writeBlock(posDeltaBuffer, posOut);
+      posBufferUpto = 0;
+    }
+  }
+
+  @Override
+  public void finishDoc() {
+  }
+
+  private static class PendingTerm {
+    public final long docStartFP;
+    public final long posStartFP;
+    public final int skipOffset;
+    public final int lastPosBlockOffset;
+
+    public PendingTerm(long docStartFP, long posStartFP, int skipOffset, int lastPosBlockOffset) {
+      this.docStartFP = docStartFP;
+      this.posStartFP = posStartFP;
+      this.skipOffset = skipOffset;
+      this.lastPosBlockOffset = lastPosBlockOffset;
+    }
+  }
+
+  private final List<PendingTerm> pendingTerms = new ArrayList<PendingTerm>();
+
+  /** Called when we are done adding docs to this term */
+  @Override
+  public void finishTerm(TermStats stats) throws IOException {
+
+    assert stats.docFreq > 0;
+
+    // TODO: wasteful we are counting this (counting # docs
+    // for this term) in two places?
+    assert stats.docFreq == docCount: stats.docFreq + " vs " + docCount;
+
+    if (DEBUG) {
+      System.out.println("FPW.finishTerm docFreq=" + stats.docFreq);
+    }
+
+    // nocommit silly that skipper must write skip when we no
+    // postings come after it, but if we don't do this, skip
+    // reader incorrectly thinks it can read another level 0
+    // skip entry here!:
+    //if (docCount > blockSize && docBufferUpto > 0) {
+    if (docCount > blockSize) {
+      final int lastDocCount = blockSize*(docCount/blockSize);
+      if (DEBUG) {
+        System.out.println("  bufferSkip at finishTerm: lastDocID=" + lastBlockDocID + " docCount=" + lastDocCount);
+      }
+      skipWriter.bufferSkip(lastBlockDocID, lastDocCount, lastBlockPosFP, lastBlockPosBufferUpto);
+    }
+
+    if (DEBUG) {
+      if (docBufferUpto > 0) {
+        System.out.println("  write doc/freq vInt block (count=" + docBufferUpto + ") at fp=" + docOut.getFilePointer() + " docTermStartFP=" + docTermStartFP);
+      }
+    }
+
+    // vInt encode the remaining doc deltas and freqs:
+    for(int i=0;i<docBufferUpto;i++) {
+      final int docDelta = docDeltaBuffer[i];
+      final int freq = freqBuffer[i];
+      if (!fieldHasFreqs) {
+        docOut.writeVInt(docDelta);
+      } else if (freqBuffer[i] == 1) {
+        docOut.writeVInt((docDelta<<1)|1);
+      } else {
+        docOut.writeVInt(docDelta<<1);
+        docOut.writeVInt(freq);
+      }
+    }
+
+    final int lastPosBlockOffset;
+
+    if (fieldHasPositions) {
+      if (DEBUG) {
+        if (posBufferUpto > 0) {
+          System.out.println("  write pos vInt block (count=" + posBufferUpto + ") at fp=" + posOut.getFilePointer() + " posTermStartFP=" + posTermStartFP);
+        }
+      }
+
+      assert stats.totalTermFreq != -1;
+      if (stats.totalTermFreq > blockSize) {
+        lastPosBlockOffset = (int) (posOut.getFilePointer() - posTermStartFP);
+      } else {
+        lastPosBlockOffset = -1;
+      }
+      if (posBufferUpto > 0) {
+        posOut.writeVInt(posBufferUpto);
+        // vInt encode the remaining position deltas:
+        for(int i=0;i<posBufferUpto;i++) {
+          final int posDelta = posDeltaBuffer[i];
+          posOut.writeVInt(posDelta);
+        }
+      }
+      if (DEBUG) {
+        System.out.println("  totalTermFreq=" + stats.totalTermFreq + " lastPosBlockOffset=" + lastPosBlockOffset);
+      }
+    } else {
+      lastPosBlockOffset = -1;
+    }
+
+    int skipOffset;
+    if (docCount > blockSize) {
+      skipOffset = (int) (skipWriter.writeSkip(docOut)-docTermStartFP);
+      
+      if (DEBUG) {
+        System.out.println("skip packet " + (docOut.getFilePointer() - (docTermStartFP + skipOffset)) + " bytes");
+      }
+    } else {
+      skipOffset = -1;
+      if (DEBUG) {
+        System.out.println("no skip: docCount=" + docCount);
+      }
+    }
+
+    pendingTerms.add(new PendingTerm(docTermStartFP, posTermStartFP, skipOffset, lastPosBlockOffset));
+    docBufferUpto = 0;
+    posBufferUpto = 0;
+    lastDocID = 0;
+    docCount = 0;
+  }
+
+  private final RAMOutputStream bytesWriter = new RAMOutputStream();
+
+  @Override
+  public void flushTermsBlock(int start, int count) throws IOException {
+
+    if (count == 0) {
+      termsOut.writeByte((byte) 0);
+      return;
+    }
+
+    assert start <= pendingTerms.size();
+    assert count <= start;
+
+    final int limit = pendingTerms.size() - start + count;
+
+    long lastDocStartFP = 0;
+    long lastPosStartFP = 0;
+    for(int idx=limit-count; idx<limit; idx++) {
+      PendingTerm term = pendingTerms.get(idx);
+
+      bytesWriter.writeVLong(term.docStartFP - lastDocStartFP);
+      lastDocStartFP = term.docStartFP;
+
+      if (fieldHasPositions) {
+        bytesWriter.writeVLong(term.posStartFP - lastPosStartFP);
+        lastPosStartFP = term.posStartFP;
+        if (term.lastPosBlockOffset != -1) {
+          bytesWriter.writeVInt(term.lastPosBlockOffset);
+        }
+      }
+
+      if (term.skipOffset != -1) {
+        bytesWriter.writeVInt(term.skipOffset);
+      }
+    }
+
+    termsOut.writeVInt((int) bytesWriter.getFilePointer());
+    bytesWriter.writeTo(termsOut);
+    bytesWriter.reset();
+
+    // Remove the terms we just wrote:
+    pendingTerms.subList(limit-count, limit).clear();
+  }
+
+  @Override
+  public void close() throws IOException {
+    IOUtils.close(docOut, posOut);
+  }
+}
Index: lucene/core/src/java/org/apache/lucene/codecs/fixed/FixedPostingsReader.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/fixed/FixedPostingsReader.java	(revision 0)
+++ lucene/core/src/java/org/apache/lucene/codecs/fixed/FixedPostingsReader.java	(working copy)
@@ -0,0 +1,844 @@
+package org.apache.lucene.codecs.fixed;
+
+/*
+ * 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.io.IOException;
+
+import org.apache.lucene.codecs.BlockTermState;
+import org.apache.lucene.codecs.PostingsReaderBase;
+import org.apache.lucene.index.DocsAndPositionsEnum;
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.index.FieldInfo.IndexOptions;
+import org.apache.lucene.index.FieldInfo;
+import org.apache.lucene.index.FieldInfos;
+import org.apache.lucene.index.IndexFileNames;
+import org.apache.lucene.index.SegmentInfo;
+import org.apache.lucene.index.TermState;
+import org.apache.lucene.store.ByteArrayDataInput;
+import org.apache.lucene.store.DataInput;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.IOContext;
+import org.apache.lucene.store.IndexInput;
+import org.apache.lucene.util.ArrayUtil;
+import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.CodecUtil;
+import org.apache.lucene.util.IOUtils;
+
+// nocommit javadocs
+public abstract class FixedPostingsReader extends PostingsReaderBase {
+
+  private final IndexInput docIn;
+  private final IndexInput posIn;
+
+  public static boolean DEBUG = false;
+
+  // nocommit
+  final String segment;
+
+  // NOTE: not private to avoid access$NNN methods:
+  final int blockSize;
+
+  public FixedPostingsReader(Directory dir, FieldInfos fieldInfos, SegmentInfo segmentInfo, IOContext ioContext, String segmentSuffix, int blockSize) throws IOException {
+    boolean success = false;
+    segment = segmentInfo.name;
+    IndexInput docIn = null;
+    IndexInput posIn = null;
+    try {
+      docIn = dir.openInput(IndexFileNames.segmentFileName(segmentInfo.name, segmentSuffix, FixedPostingsWriter.DOC_EXTENSION),
+                            ioContext);
+      CodecUtil.checkHeader(docIn,
+                            FixedPostingsWriter.DOC_CODEC,
+                            FixedPostingsWriter.VERSION_START,
+                            FixedPostingsWriter.VERSION_START);
+
+      if (fieldInfos.hasProx()) {
+        posIn = dir.openInput(IndexFileNames.segmentFileName(segmentInfo.name, segmentSuffix, FixedPostingsWriter.POS_EXTENSION),
+                              ioContext);
+        CodecUtil.checkHeader(posIn,
+                              FixedPostingsWriter.POS_CODEC,
+                              FixedPostingsWriter.VERSION_START,
+                              FixedPostingsWriter.VERSION_START);
+      }
+
+      this.docIn = docIn;
+      this.posIn = posIn;
+      success = true;
+    } finally {
+      if (!success) {
+        IOUtils.closeWhileHandlingException(docIn, posIn);
+      }
+    }
+
+    this.blockSize = blockSize;
+  }
+
+  @Override
+  public void init(IndexInput termsIn) throws IOException {
+    // Make sure we are talking to the matching past writer
+    CodecUtil.checkHeader(termsIn,
+                          FixedPostingsWriter.TERMS_CODEC,
+                          FixedPostingsWriter.VERSION_START,
+                          FixedPostingsWriter.VERSION_START);
+    final int indexBlockSize = termsIn.readVInt();
+    if (indexBlockSize != blockSize) {
+      throw new IllegalStateException("index-time blockSize (" + indexBlockSize + ") != read-time blockSize (" + blockSize + ")");
+    }
+  }
+
+  // Must keep final because we do non-standard clone
+  private final static class FixedTermState extends BlockTermState {
+    long docStartFP;
+    long posStartFP;
+    int skipOffset;
+    int lastPosBlockOffset;
+
+    // Only used by the "primary" TermState -- clones don't
+    // copy this (basically they are "transient"):
+    ByteArrayDataInput bytesReader;  // TODO: should this NOT be in the TermState...?
+    byte[] bytes;
+
+    @Override
+    public FixedTermState clone() {
+      FixedTermState other = new FixedTermState();
+      other.copyFrom(this);
+      return other;
+    }
+
+    @Override
+    public void copyFrom(TermState _other) {
+      super.copyFrom(_other);
+      FixedTermState other = (FixedTermState) _other;
+      docStartFP = other.docStartFP;
+      posStartFP = other.posStartFP;
+      lastPosBlockOffset = other.lastPosBlockOffset;
+      skipOffset = other.skipOffset;
+
+      // Do not copy bytes, bytesReader (else TermState is
+      // very heavy, ie drags around the entire block's
+      // byte[]).  On seek back, if next() is in fact used
+      // (rare!), they will be re-read from disk.
+    }
+
+    @Override
+    public String toString() {
+      return super.toString() + " docStartFP=" + docStartFP + " posStartFP=" + posStartFP + " lastPosBlockOffset=" + lastPosBlockOffset;
+    }
+  }
+
+  @Override
+  public FixedTermState newTermState() {
+    return new FixedTermState();
+  }
+
+  @Override
+  public void close() throws IOException {
+    IOUtils.close(docIn, posIn);
+  }
+
+  /* Reads but does not decode the byte[] blob holding
+     metadata for the current terms block */
+  @Override
+  public void readTermsBlock(IndexInput termsIn, FieldInfo fieldInfo, BlockTermState _termState) throws IOException {
+    final FixedTermState termState = (FixedTermState) _termState;
+
+    final int numBytes = termsIn.readVInt();
+
+    if (termState.bytes == null) {
+      termState.bytes = new byte[ArrayUtil.oversize(numBytes, 1)];
+      termState.bytesReader = new ByteArrayDataInput();
+    } else if (termState.bytes.length < numBytes) {
+      termState.bytes = new byte[ArrayUtil.oversize(numBytes, 1)];
+    }
+
+    termsIn.readBytes(termState.bytes, 0, numBytes);
+    termState.bytesReader.reset(termState.bytes, 0, numBytes);
+  }
+
+  @Override
+  public void nextTerm(FieldInfo fieldInfo, BlockTermState _termState)
+    throws IOException {
+    final FixedTermState termState = (FixedTermState) _termState;
+    final boolean isFirstTerm = termState.termBlockOrd == 0;
+    final boolean fieldHasPositions = fieldInfo.getIndexOptions().compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) >= 0;
+
+    final DataInput in = termState.bytesReader;
+    if (isFirstTerm) {
+      termState.docStartFP = in.readVLong();
+      if (fieldHasPositions) {
+        termState.posStartFP = in.readVLong();
+        if (termState.totalTermFreq > blockSize) {
+          termState.lastPosBlockOffset = in.readVInt();
+        } else {
+          termState.lastPosBlockOffset = -1;
+        }
+      }
+    } else {
+      termState.docStartFP += in.readVLong();
+      if (fieldHasPositions) {
+        termState.posStartFP += in.readVLong();
+        if (termState.totalTermFreq > blockSize) {
+          termState.lastPosBlockOffset = in.readVInt();
+        } else {
+          termState.lastPosBlockOffset = -1;
+        }
+      }
+    }
+
+    if (termState.docFreq > blockSize) {
+      termState.skipOffset = in.readVInt();
+    } else {
+      termState.skipOffset = -1;
+    }
+  }
+    
+  @Override
+  public DocsEnum docs(FieldInfo fieldInfo, BlockTermState termState, Bits liveDocs, DocsEnum reuse, boolean needsFreqs) throws IOException {
+    FixedDocsEnum docsEnum;
+    if (reuse instanceof FixedDocsEnum) {
+      docsEnum = (FixedDocsEnum) reuse;
+      if (!docsEnum.canReuse(docIn, fieldInfo)) {
+        docsEnum = new FixedDocsEnum(fieldInfo);
+      }
+    } else {
+      docsEnum = new FixedDocsEnum(fieldInfo);
+    }
+    return docsEnum.reset(liveDocs, (FixedTermState) termState);
+  }
+
+  // TODO: specialize to liveDocs vs not, and freqs vs not
+  
+  @Override
+  public DocsAndPositionsEnum docsAndPositions(FieldInfo fieldInfo, BlockTermState termState, Bits liveDocs,
+                                               DocsAndPositionsEnum reuse, boolean needsOffsets)
+    throws IOException {
+    FixedDocsAndPositionsEnum docsAndPositionsEnum;
+    if (reuse instanceof FixedDocsAndPositionsEnum) {
+      docsAndPositionsEnum = (FixedDocsAndPositionsEnum) reuse;
+      if (!docsAndPositionsEnum.canReuse(docIn, fieldInfo)) {
+        docsAndPositionsEnum = new FixedDocsAndPositionsEnum(fieldInfo);
+      }
+    } else {
+      docsAndPositionsEnum = new FixedDocsAndPositionsEnum(fieldInfo);
+    }
+    return docsAndPositionsEnum.reset(liveDocs, (FixedTermState) termState);
+  }
+
+  // nocommit consolidate w/ sep's:
+
+  public interface BlockReader {
+    void readBlock(int[] buffer) throws IOException;
+    void skipBlock() throws IOException;
+  }
+
+  protected abstract BlockReader getBlockReader(IndexInput in);
+
+  final class FixedDocsEnum extends DocsEnum {
+    
+    private final int[] docDeltaBuffer = new int[blockSize];
+    private final int[] freqBuffer = new int[blockSize];
+
+    private int docBufferUpto;
+
+    private FixedSkipReader skipper;
+    private boolean skipped;
+
+    final IndexInput startDocIn;
+
+    final IndexInput docIn;
+    final boolean indexHasFreq;
+    final boolean indexHasPos;
+
+    private int docFreq;                              // number of docs in this posting list
+    private int docUpto;                              // how many docs we've read
+    private int doc;                                  // doc we last read
+    private int accum;                                // accumulator for doc deltas
+    private int freq;                                 // freq we last read
+
+    // Where this term's postings start in the .doc file:
+    private long docTermStartFP;
+
+    // Where this term's skip data starts (after
+    // docTermStartFP) in the .doc file (or -1 if there is
+    // no skip data for this term):
+    private int skipOffset;
+
+    private Bits liveDocs;
+
+    private final BlockReader docBlockReader;
+    
+    public FixedDocsEnum(FieldInfo fieldInfo) throws IOException {
+      this.startDocIn = FixedPostingsReader.this.docIn;
+      this.docIn = (IndexInput) startDocIn.clone();
+      indexHasFreq = fieldInfo.getIndexOptions().compareTo(IndexOptions.DOCS_AND_FREQS) >= 0;
+      indexHasPos = fieldInfo.getIndexOptions().compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) >= 0;
+      docBlockReader = getBlockReader(this.docIn);
+    }
+
+    public boolean canReuse(IndexInput docIn, FieldInfo fieldInfo) {
+      return docIn == startDocIn &&
+        indexHasFreq == (fieldInfo.getIndexOptions().compareTo(IndexOptions.DOCS_AND_FREQS) >= 0) &&
+        indexHasPos == (fieldInfo.getIndexOptions().compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) >= 0);
+    }
+    
+    public DocsEnum reset(Bits liveDocs, FixedTermState termState) throws IOException {
+      this.liveDocs = liveDocs;
+      if (DEBUG) {
+        System.out.println("  FPR.reset: seg=" + segment + " termState=" + termState);
+      }
+      docFreq = termState.docFreq;
+      docTermStartFP = termState.docStartFP;
+      docIn.seek(docTermStartFP);
+      skipOffset = termState.skipOffset;
+
+      doc = -1;
+      accum = 0;
+      docUpto = 0;
+      docBufferUpto = blockSize;
+      skipped = false;
+      return this;
+    }
+    
+    @Override
+    public final int freq() throws IOException {
+      return freq;
+    }
+
+    @Override
+    public final int docID() {
+      return doc;
+    }
+    
+    private void refillDocs() throws IOException {
+      final int left = docFreq - docUpto;
+      assert left > 0;
+
+      if (left >= blockSize) {
+        if (DEBUG) {
+          System.out.println("    fill doc block from fp=" + docIn.getFilePointer());
+        }
+        docBlockReader.readBlock(docDeltaBuffer);
+        if (indexHasFreq) {
+          if (DEBUG) {
+            System.out.println("    fill freq block from fp=" + docIn.getFilePointer());
+          }
+          docBlockReader.readBlock(freqBuffer);
+        }
+      } else {
+        // Read vInts:
+        if (DEBUG) {
+          System.out.println("    fill last vInt block from fp=" + docIn.getFilePointer());
+        }
+        for(int i=0;i<left;i++) {
+          final int code = docIn.readVInt();
+          if (indexHasFreq) {
+            docDeltaBuffer[i] = code >> 1;
+            if ((code & 1) != 0) {
+              freqBuffer[i] = 1;
+            } else {
+              freqBuffer[i] = docIn.readVInt();
+            }
+          } else {
+            docDeltaBuffer[i] = code;
+          }
+        }
+      }
+      docBufferUpto = 0;
+    }
+
+    @Override
+    public int nextDoc() throws IOException {
+
+      if (DEBUG) {
+        System.out.println("\nFPR.nextDoc");
+      }
+
+      while (true) {
+        if (DEBUG) {
+          System.out.println("  docUpto=" + docUpto + " (of df=" + docFreq + ") docBufferUpto=" + docBufferUpto);
+        }
+
+        if (docUpto == docFreq) {
+          if (DEBUG) {
+            System.out.println("  return doc=END");
+          }
+          return doc = NO_MORE_DOCS;
+        }
+
+        if (docBufferUpto == blockSize) {
+          refillDocs();
+        }
+
+        if (DEBUG) {
+          System.out.println("    accum=" + accum + " docDeltaBuffer[" + docBufferUpto + "]=" + docDeltaBuffer[docBufferUpto]);
+        }
+        accum += docDeltaBuffer[docBufferUpto];
+        docUpto++;
+
+        if (liveDocs == null || liveDocs.get(accum)) {
+          doc = accum;
+          freq = freqBuffer[docBufferUpto];
+          docBufferUpto++;
+          if (DEBUG) {
+            System.out.println("  return doc=" + doc + " freq=" + freq);
+          }
+          return doc;
+        }
+
+        if (DEBUG) {
+          System.out.println("  doc=" + accum + " is deleted; try next doc");
+        }
+
+        docBufferUpto++;
+      }
+    }
+    
+    @Override
+    public final int advance(int target) throws IOException {
+      // nocommit make frq block load lazy/skippable
+
+      // nocommit 2 is heuristic guess!!
+      // nocommit put cheating back!  does it help?
+      // nocommit use skipper!!!  it has next last doc id!!
+      //if (docFreq > blockSize && target - (blockSize - docBufferUpto) - 2*blockSize > accum) {
+      if (docFreq > blockSize && target - accum > blockSize) {
+
+        if (DEBUG) {
+          System.out.println("load skipper");
+        }
+
+        if (skipper == null) {
+          // Lazy init: first time this enum has ever been used for skipping
+          skipper = new FixedSkipReader((IndexInput) docIn.clone(),
+                                        FixedPostingsWriter.maxSkipLevels,
+                                        blockSize,
+                                        indexHasPos);
+        }
+
+        if (!skipped) {
+          assert skipOffset != -1;
+          // This is the first time this enum has skipped
+          // since reset() was called; load the skip data:
+          skipper.init(docTermStartFP+skipOffset, docTermStartFP, 0, docFreq);
+          skipped = true;
+        }
+
+        final int newDocUpto = skipper.skipTo(target); 
+
+        if (newDocUpto > docUpto) {
+          // Skipper moved
+
+          if (DEBUG) {
+            System.out.println("skipper moved to docUpto=" + newDocUpto + " vs current=" + docUpto + "; docID=" + skipper.getDoc() + " fp=" + skipper.getDocPointer());
+          }
+
+          assert newDocUpto % blockSize == (blockSize-1): "got " + newDocUpto;
+          docUpto = newDocUpto+1;
+
+          // Force block read next:
+          docBufferUpto = blockSize;
+          accum = skipper.getDoc();
+          docIn.seek(skipper.getDocPointer());
+        }
+      }
+
+      // Now scan:
+      while (nextDoc() != NO_MORE_DOCS) {
+        if (doc >= target) {
+          if (DEBUG) {
+            System.out.println("  advance return doc=" + doc);
+          }
+          return doc;
+        }
+      }
+
+      if (DEBUG) {
+        System.out.println("  advance return doc=END");
+      }
+
+      return NO_MORE_DOCS;
+    }
+  }
+
+
+  final class FixedDocsAndPositionsEnum extends DocsAndPositionsEnum {
+    
+    private final int[] docDeltaBuffer = new int[blockSize];
+    private final int[] freqBuffer = new int[blockSize];
+    private final int[] posDeltaBuffer = new int[blockSize];
+
+    private int docBufferUpto;
+    private int posBufferUpto;
+
+    private FixedSkipReader skipper;
+    private boolean skipped;
+
+    final IndexInput startDocIn;
+
+    final IndexInput docIn;
+    final IndexInput posIn;
+
+    private int docFreq;                              // number of docs in this posting list
+    private int docUpto;                              // how many docs we've read
+    private int doc;                                  // doc we last read
+    private int accum;                                // accumulator for doc deltas
+    private int freq;                                 // freq we last read
+    private int position;                             // current position
+
+    // how many positions "behind" we are; nextPosition must
+    // skip these to "catch up":
+    private int posPendingCount;
+
+    // Lazy pos seek: if != -1 then we must seek to this FP
+    // before reading positions:
+    private long posPendingFP;
+
+    // Where this term's postings start in the .doc file:
+    private long docTermStartFP;
+
+    // Where this term's postings start in the .pos file:
+    private long posTermStartFP;
+
+    // File pointer where the last (vInt encoded) pos delta
+    // block is.  We need this to know whether to bulk
+    // decode vs vInt decode the block:
+    private long lastPosBlockFP;
+
+    // Where this term's skip data starts (after
+    // docTermStartFP) in the .doc file (or -1 if there is
+    // no skip data for this term):
+    private int skipOffset;
+
+    private Bits liveDocs;
+    
+    private final BlockReader docBlockReader;
+    private final BlockReader posBlockReader;
+
+    public FixedDocsAndPositionsEnum(FieldInfo fieldInfo) throws IOException {
+      this.startDocIn = FixedPostingsReader.this.docIn;
+      this.docIn = (IndexInput) startDocIn.clone();
+      this.posIn = (IndexInput) FixedPostingsReader.this.posIn.clone();
+      docBlockReader = getBlockReader(this.docIn);
+      posBlockReader = getBlockReader(this.posIn);
+    }
+
+    public boolean canReuse(IndexInput docIn, FieldInfo fieldInfo) {
+      return docIn == startDocIn;
+    }
+    
+    public DocsAndPositionsEnum reset(Bits liveDocs, FixedTermState termState) throws IOException {
+      this.liveDocs = liveDocs;
+      if (DEBUG) {
+        System.out.println("  FPR.reset: termState=" + termState);
+      }
+      docFreq = termState.docFreq;
+      docTermStartFP = termState.docStartFP;
+      posTermStartFP = termState.posStartFP;
+      docIn.seek(docTermStartFP);
+      skipOffset = termState.skipOffset;
+      posPendingFP = posTermStartFP;
+      posPendingCount = 0;
+      if (termState.totalTermFreq < blockSize) {
+        lastPosBlockFP = posTermStartFP;
+      } else if (termState.totalTermFreq == blockSize) {
+        lastPosBlockFP = -1;
+      } else {
+        lastPosBlockFP = posTermStartFP + termState.lastPosBlockOffset;
+      }
+
+      doc = -1;
+      accum = 0;
+      docUpto = 0;
+      docBufferUpto = blockSize;
+      skipped = false;
+      return this;
+    }
+    
+    @Override
+    public final int freq() throws IOException {
+      return freq;
+    }
+
+    @Override
+    public final int docID() {
+      return doc;
+    }
+
+    private void refillDocs() throws IOException {
+      final int left = docFreq - docUpto;
+      assert left > 0;
+
+      if (left >= blockSize) {
+        if (DEBUG) {
+          System.out.println("    fill doc block from fp=" + docIn.getFilePointer());
+        }
+        docBlockReader.readBlock(docDeltaBuffer);
+        if (DEBUG) {
+          System.out.println("    fill freq block from fp=" + docIn.getFilePointer());
+        }
+        docBlockReader.readBlock(freqBuffer);
+      } else {
+        // Read vInts:
+        if (DEBUG) {
+          System.out.println("    fill last vInt block from fp=" + docIn.getFilePointer());
+        }
+        for(int i=0;i<left;i++) {
+          final int code = docIn.readVInt();
+          docDeltaBuffer[i] = code >> 1;
+          if ((code & 1) != 0) {
+            freqBuffer[i] = 1;
+          } else {
+            freqBuffer[i] = docIn.readVInt();
+          }
+        }
+      }
+      docBufferUpto = 0;
+    }
+    
+    private void refillPositions() throws IOException {
+      if (DEBUG) {
+        System.out.println("      refillPositions");
+      }
+      if (posIn.getFilePointer() == lastPosBlockFP) {
+        if (DEBUG) {
+          System.out.println("        vInt block @ fp=" + posIn.getFilePointer());
+        }
+        final int count = posIn.readVInt();
+        for(int i=0;i<count;i++) {
+          posDeltaBuffer[i] = posIn.readVInt();
+        }
+      } else {
+        if (DEBUG) {
+          System.out.println("        bulk block @ fp=" + posIn.getFilePointer());
+        }
+        posBlockReader.readBlock(posDeltaBuffer);
+      }
+    }
+
+    @Override
+    public int nextDoc() throws IOException {
+
+      if (DEBUG) {
+        System.out.println("  FPR.nextDoc");
+      }
+
+      while (true) {
+        if (DEBUG) {
+          System.out.println("    docUpto=" + docUpto + " (of df=" + docFreq + ") docBufferUpto=" + docBufferUpto);
+        }
+
+        if (docUpto == docFreq) {
+          return doc = NO_MORE_DOCS;
+        }
+
+        if (docBufferUpto == blockSize) {
+          refillDocs();
+        }
+
+        if (DEBUG) {
+          System.out.println("    accum=" + accum + " docDeltaBuffer[" + docBufferUpto + "]=" + docDeltaBuffer[docBufferUpto]);
+        }
+        accum += docDeltaBuffer[docBufferUpto];
+        freq = freqBuffer[docBufferUpto];
+        posPendingCount += freq;
+        docBufferUpto++;
+        docUpto++;
+
+        if (liveDocs == null || liveDocs.get(accum)) {
+          doc = accum;
+          if (DEBUG) {
+            System.out.println("    return doc=" + doc + " freq=" + freq + " posPendingCount=" + posPendingCount);
+          }
+          position = 0;
+          return doc;
+        }
+
+        if (DEBUG) {
+          System.out.println("    doc=" + accum + " is deleted; try next doc");
+        }
+      }
+    }
+    
+    @Override
+    public final int advance(int target) throws IOException {
+      // nocommit make frq block load lazy/skippable
+      if (DEBUG) {
+        System.out.println("  FPR.advance target=" + target);
+      }
+
+      // nocommit 2 is heuristic guess!!
+      // nocommit put cheating back!  does it help?
+      // nocommit use skipper!!!  it has next last doc id!!
+      //if (docFreq > blockSize && target - (blockSize - docBufferUpto) - 2*blockSize > accum) {
+      if (docFreq > blockSize && target - accum > blockSize) {
+
+        if (DEBUG) {
+          System.out.println("    try skipper");
+        }
+
+        if (skipper == null) {
+          // Lazy init: first time this enum has ever been used for skipping
+          if (DEBUG) {
+            System.out.println("    create skipper");
+          }
+          skipper = new FixedSkipReader((IndexInput) docIn.clone(),
+                                        FixedPostingsWriter.maxSkipLevels,
+                                        blockSize,
+                                        true);
+        }
+
+        if (!skipped) {
+          assert skipOffset != -1;
+          // This is the first time this enum has skipped
+          // since reset() was called; load the skip data:
+          if (DEBUG) {
+            System.out.println("    init skipper");
+          }
+          skipper.init(docTermStartFP+skipOffset, docTermStartFP, posTermStartFP, docFreq);
+          skipped = true;
+        }
+
+        final int newDocUpto = skipper.skipTo(target); 
+
+        if (newDocUpto > docUpto) {
+          // Skipper moved
+
+          if (DEBUG) {
+            System.out.println("    skipper moved to docUpto=" + newDocUpto + " vs current=" + docUpto + "; docID=" + skipper.getDoc() + " fp=" + skipper.getDocPointer() + " pos.fp=" + skipper.getPosPointer() + " pos.bufferUpto=" + skipper.getPosBufferUpto());
+          }
+
+          assert newDocUpto % blockSize == (blockSize-1): "got " + newDocUpto;
+          docUpto = newDocUpto+1;
+
+          // Force block read next:
+          docBufferUpto = blockSize;
+          accum = skipper.getDoc();
+          docIn.seek(skipper.getDocPointer());
+          posPendingFP = skipper.getPosPointer();
+          posPendingCount = skipper.getPosBufferUpto();
+        }
+      }
+
+      // Now scan:
+      while (nextDoc() != NO_MORE_DOCS) {
+        if (doc >= target) {
+          if (DEBUG) {
+            System.out.println("  advance return doc=" + doc);
+          }
+          return doc;
+        }
+      }
+
+      if (DEBUG) {
+        System.out.println("  advance return doc=END");
+      }
+
+      return NO_MORE_DOCS;
+    }
+
+    // nocommit in theory we could avoid loading frq block
+    // when not needed, ie, use skip data to load how far to
+    // seek the pos pointe ... instead of having to load frq
+    // blocks only to sum up how many positions to skip
+    private void skipPositions() throws IOException {
+      // Skip positions now:
+      int toSkip = posPendingCount - freq;
+      if (DEBUG) {
+        System.out.println("      FPR.skipPositions: toSkip=" + toSkip);
+      }
+
+      final int leftInBlock = blockSize - posBufferUpto;
+      if (toSkip < leftInBlock) {
+        if (DEBUG) {
+          System.out.println("        skip w/in block to posBufferUpto=" + posBufferUpto);
+        }
+        posBufferUpto += toSkip;
+      } else {
+        toSkip -= leftInBlock;
+        while(toSkip >= blockSize) {
+          if (DEBUG) {
+            System.out.println("        skip whole block @ fp=" + posIn.getFilePointer());
+          }
+          assert posIn.getFilePointer() != lastPosBlockFP;
+          posBlockReader.skipBlock();
+          toSkip -= blockSize;
+        }
+        refillPositions();
+        posBufferUpto = toSkip;
+        if (DEBUG) {
+          System.out.println("        skip w/in block to posBufferUpto=" + posBufferUpto);
+        }
+      }
+
+      position = 0;
+    }
+
+    @Override
+    public int nextPosition() throws IOException {
+      if (DEBUG) {
+        System.out.println("    FPR.nextPosition posPendingCount=" + posPendingCount + " posBufferUpto=" + posBufferUpto);
+      }
+      if (posPendingFP != -1) {
+        if (DEBUG) {
+          System.out.println("      seek to pendingFP=" + posPendingFP);
+        }
+        posIn.seek(posPendingFP);
+        posPendingFP = -1;
+
+        // Force buffer refill:
+        posBufferUpto = blockSize;
+      }
+
+      if (posPendingCount > freq) {
+        skipPositions();
+        posPendingCount = freq;
+      }
+
+      if (posBufferUpto == blockSize) {
+        refillPositions();
+        posBufferUpto = 0;
+      }
+      position += posDeltaBuffer[posBufferUpto++];
+      posPendingCount--;
+      if (DEBUG) {
+        System.out.println("      return pos=" + position);
+      }
+      return position;
+    }
+
+    @Override
+    public int startOffset() {
+      return -1;
+    }
+  
+    @Override
+    public int endOffset() {
+      return -1;
+    }
+  
+    @Override
+    public boolean hasPayload() {
+      return false;
+    }
+
+    @Override
+    public BytesRef getPayload() {
+      return null;
+    }
+  }
+}
Index: lucene/core/src/java/org/apache/lucene/codecs/fixed/FixedSkipReader.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/fixed/FixedSkipReader.java	(revision 0)
+++ lucene/core/src/java/org/apache/lucene/codecs/fixed/FixedSkipReader.java	(working copy)
@@ -0,0 +1,138 @@
+package org.apache.lucene.codecs.fixed;
+
+/*
+ * 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.io.IOException;
+import java.util.Arrays;
+
+import org.apache.lucene.codecs.MultiLevelSkipListReader;
+import org.apache.lucene.store.IndexInput;
+
+/**
+ * Implements the skip list reader for the 4.0 posting list format
+ * that stores positions and payloads.
+ * 
+ * @see Lucene40PostingsFormat
+ * @lucene.experimental
+ */
+public class FixedSkipReader extends MultiLevelSkipListReader {
+  private boolean DEBUG = FixedPostingsReader.DEBUG;
+
+  private long docPointer[];
+  private long posPointer[];
+  private int posBufferUpto[];
+
+  private long lastPosPointer;
+  private long lastDocPointer;
+  private int lastPosBufferUpto;
+
+  public FixedSkipReader(IndexInput skipStream, int maxSkipLevels, int skipInterval, boolean hasPos) {
+    super(skipStream, maxSkipLevels, skipInterval);
+    docPointer = new long[maxSkipLevels];
+    if (hasPos) {
+      posPointer = new long[maxSkipLevels];
+      posBufferUpto = new int[maxSkipLevels];
+    } else {
+      posPointer = null;
+    }
+  }
+
+  public void init(long skipPointer, long docBasePointer, long posBasePointer, int df) {
+    super.init(skipPointer, df);
+    lastDocPointer = docBasePointer;
+    lastPosPointer = posBasePointer;
+
+    Arrays.fill(docPointer, docBasePointer);
+    if (posPointer != null) {
+      Arrays.fill(posPointer, posBasePointer);
+    } else {
+      assert posBasePointer == 0;
+    }
+  }
+
+  /** Returns the doc pointer of the doc to which the last call of 
+   * {@link MultiLevelSkipListReader#skipTo(int)} has skipped.  */
+  public long getDocPointer() {
+    return lastDocPointer;
+  }
+
+  public long getPosPointer() {
+    return lastPosPointer;
+  }
+
+  public int getPosBufferUpto() {
+    return lastPosBufferUpto;
+  }
+
+  @Override
+  protected void seekChild(int level) throws IOException {
+    super.seekChild(level);
+    if (DEBUG) {
+      System.out.println("seekChild level=" + level);
+    }
+    docPointer[level] = lastDocPointer;
+    if (posPointer != null) {
+      posPointer[level] = lastPosPointer;
+      posBufferUpto[level] = lastPosBufferUpto;
+    }
+  }
+  
+  @Override
+  protected void setLastSkipData(int level) {
+    super.setLastSkipData(level);
+    lastDocPointer = docPointer[level];
+    if (DEBUG) {
+      System.out.println("setLastSkipData level=" + level);
+      System.out.println("  lastDocPointer=" + lastDocPointer);
+    }
+    if (posPointer != null) {
+      lastPosPointer = posPointer[level];
+      lastPosBufferUpto = posBufferUpto[level];
+      if (DEBUG) {
+        System.out.println("  lastPosPointer=" + lastPosPointer + " lastPosBUfferUpto=" + lastPosBufferUpto);
+      }
+    }
+  }
+
+  @Override
+  protected int readSkipData(int level, IndexInput skipStream) throws IOException {
+    if (DEBUG) {
+      System.out.println("readSkipData level=" + level);
+    }
+    int delta = skipStream.readVInt();
+    if (DEBUG) {
+      System.out.println("  delta=" + delta);
+    }
+    docPointer[level] += skipStream.readVInt();
+    if (DEBUG) {
+      System.out.println("  docFP=" + docPointer[level]);
+    }
+
+    if (posPointer != null) {
+      posPointer[level] += skipStream.readVInt();
+      if (DEBUG) {
+        System.out.println("  posFP=" + posPointer[level]);
+      }
+      posBufferUpto[level] = skipStream.readVInt();
+      if (DEBUG) {
+        System.out.println("  posBufferUpto=" + posBufferUpto[level]);
+      }
+    }
+    return delta;
+  }
+}
Index: lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40PostingsWriter.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40PostingsWriter.java	(revision 1362013)
+++ lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40PostingsWriter.java	(working copy)
@@ -185,8 +185,6 @@
   int lastDocID;
   int df;
   
-  /** Adds a new doc in this term.  If this returns null
-   *  then we just skip consuming positions/payloads. */
   @Override
   public void startDoc(int docID, int termDocFreq) throws IOException {
     // if (DEBUG) System.out.println("SPW:   startDoc seg=" + segment + " docID=" + docID + " tf=" + termDocFreq + " freqOut.fp=" + freqOut.getFilePointer());
Index: lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java	(revision 1362013)
+++ lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java	(working copy)
@@ -246,9 +246,8 @@
     collector.setScorer(bs);
     do {
       bucketTable.first = null;
-      
+
       while (current != null) {         // more queued 
-
         // check prohibited & required
         if ((current.bits & PROHIBITED_MASK) == 0) {
 
@@ -272,7 +271,7 @@
             collector.collect(current.doc);
           }
         }
-        
+
         current = current.next;         // pop the queue
       }
       
Index: lucene/core/src/java/org/apache/lucene/index/FieldInfo.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/FieldInfo.java	(revision 1362013)
+++ lucene/core/src/java/org/apache/lucene/index/FieldInfo.java	(working copy)
@@ -70,7 +70,7 @@
    * @lucene.experimental
    */
   public FieldInfo(String name, boolean indexed, int number, boolean storeTermVector, 
-            boolean omitNorms, boolean storePayloads, IndexOptions indexOptions, DocValues.Type docValues, DocValues.Type normsType, Map<String,String> attributes) {
+                   boolean omitNorms, boolean storePayloads, IndexOptions indexOptions, DocValues.Type docValues, DocValues.Type normsType, Map<String,String> attributes) {
     this.name = name;
     this.indexed = indexed;
     this.number = number;
