Index: lucene/src/java/org/apache/lucene/util/fst/Builder.java
===================================================================
--- lucene/src/java/org/apache/lucene/util/fst/Builder.java	(revision 1154245)
+++ lucene/src/java/org/apache/lucene/util/fst/Builder.java	(working copy)
@@ -160,7 +160,7 @@
   }
 
   private CompiledNode compileNode(UnCompiledNode<T> n, int tailLength) throws IOException {
-    final int address;
+    final long address;
     if (dedupHash != null && (doShareNonSingletonNodes || n.numArcs <= 1) && tailLength <= shareMaxTailLength) {
       if (n.numArcs == 0) {
         address = fst.addNode(n);
@@ -496,7 +496,7 @@
   }
 
   static final class CompiledNode implements Node {
-    int address;
+    long address;
     public boolean isCompiled() {
       return true;
     }
Index: lucene/src/java/org/apache/lucene/util/fst/FST.java
===================================================================
--- lucene/src/java/org/apache/lucene/util/fst/FST.java	(revision 1154245)
+++ lucene/src/java/org/apache/lucene/util/fst/FST.java	(working copy)
@@ -37,15 +37,12 @@
  *  <p> The format is similar to what's used by Morfologik
  *  (http://sourceforge.net/projects/morfologik).
  *
- *  <p><b>NOTE</b>: the FST cannot be larger than ~2.1 GB
- *  because it uses int to address the byte[].
- *
  * @lucene.experimental
  */
 public class FST<T> {
   public static enum INPUT_TYPE {BYTE1, BYTE2, BYTE4};
   public final INPUT_TYPE inputType;
-
+  
   private final static int BIT_FINAL_ARC = 1 << 0;
   private final static int BIT_LAST_ARC = 1 << 1;
   private final static int BIT_TARGET_NEXT = 1 << 2;
@@ -95,16 +92,15 @@
   // if non-null, this FST accepts the empty string and
   // produces this output
   T emptyOutput;
-  private byte[] emptyOutputBytes;
+  private RewritablePagedBytes emptyOutputBytes;
 
-  private byte[] bytes;
-  int byteUpto = 0;
+  private RewritablePagedBytes bytes; 
+  
+  private long startNode = -1;
 
-  private int startNode = -1;
-
   public final Outputs<T> outputs;
 
-  private int lastFrozenNode;
+  private long lastFrozenNode;
 
   private final T NO_OUTPUT;
 
@@ -123,14 +119,14 @@
     public int label;
     public T output;
 
-    int target;
+    long target;
 
     byte flags;
     public T nextFinalOutput;
-    int nextArc;
+    long nextArc;
 
     // This is non-zero if current arcs are fixed array:
-    int posArcsStart;
+    long posArcsStart;
     int bytesPerArc;
     int arcIdx;
     int numArcs;
@@ -177,7 +173,7 @@
   public FST(INPUT_TYPE inputType, Outputs<T> outputs) {
     this.inputType = inputType;
     this.outputs = outputs;
-    bytes = new byte[128];
+    bytes = new RewritablePagedBytes(128);
     NO_OUTPUT = outputs.getNoOutput();
     
     writer = new BytesWriter();
@@ -192,10 +188,8 @@
     CodecUtil.checkHeader(in, FILE_FORMAT_NAME, VERSION_INT_NUM_BYTES_PER_ARC, VERSION_INT_NUM_BYTES_PER_ARC);
     if (in.readByte() == 1) {
       // accepts empty string
-      int numBytes = in.readVInt();
-      // messy
-      bytes = new byte[numBytes];
-      in.readBytes(bytes, 0, numBytes);
+      long numBytes = in.readVLong();
+      bytes = new RewritablePagedBytes(in, numBytes);
       emptyOutput = outputs.read(getBytesReader(numBytes-1));
     } else {
       emptyOutput = null;
@@ -214,13 +208,13 @@
     default:
       throw new IllegalStateException("invalid input type " + t);
     }
-    startNode = in.readVInt();
+    startNode = in.readVLong();
     nodeCount = in.readVInt();
     arcCount = in.readVInt();
     arcWithOutputCount = in.readVInt();
 
-    bytes = new byte[in.readVInt()];
-    in.readBytes(bytes, 0, bytes.length);
+    long numBytes = in.readVLong();
+    bytes = new RewritablePagedBytes(in, numBytes);
     NO_OUTPUT = outputs.getNoOutput();
 
     cacheRootArcs();
@@ -231,19 +225,19 @@
   }
 
   /** Returns bytes used to represent the FST */
-  public int sizeInBytes() {
-    return bytes.length;
+  public long sizeInBytes() {
+    return bytes.length();
   }
 
-  void finish(int startNode) throws IOException {
+  void finish(long startNode) throws IOException {
     if (startNode == FINAL_END_NODE && emptyOutput != null) {
       startNode = 0;
     }
     if (this.startNode != -1) {
       throw new IllegalStateException("already finished");
     }
-    byte[] finalBytes = new byte[writer.posWrite];
-    System.arraycopy(bytes, 0, finalBytes, 0, writer.posWrite);
+    RewritablePagedBytes finalBytes = new RewritablePagedBytes(writer.posWrite);
+    bytes.readBytes(0, finalBytes, 0, writer.posWrite);
     bytes = finalBytes;
     this.startNode = startNode;
 
@@ -283,20 +277,20 @@
 
     // TODO: this is messy -- replace with sillyBytesWriter; maybe make
     // bytes private
-    final int posSave = writer.posWrite;
+    final long posSave = writer.posWrite;
     outputs.write(emptyOutput, writer);
-    emptyOutputBytes = new byte[writer.posWrite-posSave];
+    emptyOutputBytes = new RewritablePagedBytes(writer.posWrite-posSave);
 
     // reverse
-    final int stopAt = (writer.posWrite - posSave)/2;
-    int upto = 0;
+    final long stopAt = (writer.posWrite - posSave)/2;
+    long upto = 0;
     while(upto < stopAt) {
-      final byte b = bytes[posSave + upto];
-      bytes[posSave+upto] = bytes[writer.posWrite-upto-1];
-      bytes[writer.posWrite-upto-1] = b;
+      final byte b = bytes.readByte(posSave + upto);
+      bytes.writeByte(bytes.readByte(writer.posWrite-upto-1), posSave+upto);
+      bytes.writeByte(b, writer.posWrite-upto-1);
       upto++;
     }
-    System.arraycopy(bytes, posSave, emptyOutputBytes, 0, writer.posWrite-posSave);
+    bytes.readBytes(posSave, emptyOutputBytes, 0, writer.posWrite-posSave);  
     writer.posWrite = posSave;
   }
 
@@ -309,8 +303,8 @@
     // to the root node, instead of special casing here:
     if (emptyOutput != null) {
       out.writeByte((byte) 1);
-      out.writeVInt(emptyOutputBytes.length);
-      out.writeBytes(emptyOutputBytes, 0, emptyOutputBytes.length);
+      out.writeVLong(emptyOutputBytes.length());
+      emptyOutputBytes.writeToOutput(out);
     } else {
       out.writeByte((byte) 0);
     }
@@ -323,12 +317,12 @@
       t = 2;
     }
     out.writeByte(t);
-    out.writeVInt(startNode);
+    out.writeVLong(startNode);
     out.writeVInt(nodeCount);
     out.writeVInt(arcCount);
     out.writeVInt(arcWithOutputCount);
-    out.writeVInt(bytes.length);
-    out.writeBytes(bytes, 0, bytes.length);
+    out.writeVLong(bytes.length());
+    bytes.writeToOutput(out);
   }
 
   private void writeLabel(int v) throws IOException {
@@ -363,7 +357,7 @@
 
   // serializes new node by appending its bytes to the end
   // of the current byte[]
-  int addNode(Builder.UnCompiledNode<T> node) throws IOException {
+  long addNode(Builder.UnCompiledNode<T> node) throws IOException {
     //System.out.println("FST.addNode pos=" + posWrite + " numArcs=" + node.numArcs);
     if (node.numArcs == 0) {
       if (node.isFinal) {
@@ -373,11 +367,11 @@
       }
     }
 
-    int startAddress = writer.posWrite;
+    long startAddress = writer.posWrite;
     //System.out.println("  startAddr=" + startAddress);
 
     final boolean doFixedArray = shouldExpand(node);
-    final int fixedArrayStart;
+    final long fixedArrayStart;
     if (doFixedArray) {
       if (bytesPerArc.length < node.numArcs) {
         bytesPerArc = new int[ArrayUtil.oversize(node.numArcs, 1)];
@@ -400,7 +394,7 @@
     
     final int lastArc = node.numArcs-1;
 
-    int lastArcStart = writer.posWrite;
+    long lastArcStart = writer.posWrite;
     int maxBytesPerArc = 0;
     for(int arcIdx=0;arcIdx<node.numArcs;arcIdx++) {
       final Builder.Arc<T> arc = node.arcs[arcIdx];
@@ -449,14 +443,14 @@
 
       if (targetHasArcs && (doFixedArray || lastFrozenNode != target.address)) {
         assert target.address > 0;
-        writer.writeInt(target.address);
+        writer.writeVLong(target.address);
       }
 
       // just write the arcs "like normal" on first pass,
       // but record how many bytes each one took, and max
       // byte size:
       if (doFixedArray) {
-        bytesPerArc[arcIdx] = writer.posWrite - lastArcStart;
+        bytesPerArc[arcIdx] = (int) (writer.posWrite - lastArcStart);
         lastArcStart = writer.posWrite;
         maxBytesPerArc = Math.max(maxBytesPerArc, bytesPerArc[arcIdx]);
         //System.out.println("    bytes=" + bytesPerArc[arcIdx]);
@@ -472,25 +466,25 @@
       assert maxBytesPerArc > 0;
       // 2nd pass just "expands" all arcs to take up a fixed
       // byte size
-      final int sizeNeeded = fixedArrayStart + node.numArcs * maxBytesPerArc;
-      bytes = ArrayUtil.grow(bytes, sizeNeeded);
+      
       // TODO: we could make this a vInt instead
-      bytes[fixedArrayStart-4] = (byte) (maxBytesPerArc >> 24);
-      bytes[fixedArrayStart-3] = (byte) (maxBytesPerArc >> 16);
-      bytes[fixedArrayStart-2] = (byte) (maxBytesPerArc >> 8);
-      bytes[fixedArrayStart-1] = (byte) maxBytesPerArc;
+      bytes.writeByte((byte) (maxBytesPerArc >> 24), fixedArrayStart-4);
+      bytes.writeByte((byte) (maxBytesPerArc >> 16), fixedArrayStart-3);
+      bytes.writeByte((byte) (maxBytesPerArc >> 8), fixedArrayStart-2);
+      bytes.writeByte((byte) maxBytesPerArc, fixedArrayStart-1);
 
       // expand the arcs in place, backwards
-      int srcPos = writer.posWrite;
-      int destPos = fixedArrayStart + node.numArcs*maxBytesPerArc;
+      long srcPos = writer.posWrite;
+      long destPos = fixedArrayStart + node.numArcs*maxBytesPerArc;
       writer.posWrite = destPos;
+      bytes.ensureCapacity(writer.posWrite);
       for(int arcIdx=node.numArcs-1;arcIdx>=0;arcIdx--) {
         //System.out.println("  repack arcIdx=" + arcIdx + " srcPos=" + srcPos + " destPos=" + destPos);
         destPos -= maxBytesPerArc;
         srcPos -= bytesPerArc[arcIdx];
         if (srcPos != destPos) {
           assert destPos > srcPos;
-          System.arraycopy(bytes, srcPos, bytes, destPos, bytesPerArc[arcIdx]);
+          bytes.copyBytes(srcPos, destPos, bytesPerArc[arcIdx]);
         }
       }
     }
@@ -498,16 +492,15 @@
     // reverse bytes in-place; we do this so that the
     // "BIT_TARGET_NEXT" opto can work, ie, it reads the
     // node just before the current one
-    final int endAddress = lastFrozenNode = writer.posWrite - 1;
+    final long endAddress = lastFrozenNode = writer.posWrite - 1;
 
-    int left = startAddress;
-    int right = endAddress;
+    long left = startAddress;
+    long right = endAddress;
     while (left < right) {
-      final byte b = bytes[left];
-      bytes[left++] = bytes[right];
-      bytes[right--] = b;
+      final byte b = bytes.readByte(left);
+      bytes.writeByte(bytes.readByte(right), left++);
+      bytes.writeByte(b, right--);
     }
-
     return endAddress;
   }
 
@@ -570,7 +563,7 @@
           if (arc.flag(BIT_STOP_NODE)) {
           } else if (arc.flag(BIT_TARGET_NEXT)) {
           } else {
-            in.pos -= 4;
+          	in.readVLong(); //skip over the target arc position
           }
           arc.flags = in.readByte();
         }
@@ -610,7 +603,7 @@
   }
 
   // Not private because NodeHash needs access:
-  Arc<T> readFirstRealArc(int address, Arc<T> arc) throws IOException {
+  Arc<T> readFirstRealArc(long address, Arc<T> arc) throws IOException {
 
     final BytesReader in = getBytesReader(address);
 
@@ -670,12 +663,12 @@
     if (arc.label == END_LABEL) {
       //System.out.println("    nextArc fake " + arc.nextArc);
       in = getBytesReader(arc.nextArc);
-      byte flags = bytes[in.pos];
+      byte flags = bytes.readByte(in.pos);
       if (flag(flags, BIT_ARCS_AS_FIXED_ARRAY)) {
         //System.out.println("    nextArc fake array");
         in.pos--;
         in.readVInt();
-        in.readInt();
+        in.readInt(); 
       }
     } else {
       if (arc.bytesPerArc != 0) {
@@ -738,7 +731,7 @@
       }
       arc.target = in.pos;
     } else {
-      arc.target = in.readInt();
+      arc.target = in.readVLong();
       arc.nextArc = in.pos;
     }
 
@@ -850,7 +843,7 @@
       }
 
       if (!flag(flags, BIT_STOP_NODE) && !flag(flags, BIT_TARGET_NEXT)) {
-        in.readInt();
+      	in.readVLong();  
       }
 
       if (flag(flags, BIT_LAST_ARC)) {
@@ -899,7 +892,7 @@
 
   // Non-static: writes to FST's byte[]
   class BytesWriter extends DataOutput {
-    int posWrite;
+    long posWrite;
 
     public BytesWriter() {
       // pad: ensure no node gets address 0 which is reserved to mean
@@ -909,44 +902,38 @@
 
     @Override
     public void writeByte(byte b) {
-      if (bytes.length == posWrite) {
-        bytes = ArrayUtil.grow(bytes);
-      }
-      assert posWrite < bytes.length: "posWrite=" + posWrite + " bytes.length=" + bytes.length;
-      bytes[posWrite++] = b;
+      bytes.writeByte(b, posWrite++);
     }
 
     @Override
     public void writeBytes(byte[] b, int offset, int length) {
-      final int size = posWrite + length;
-      bytes = ArrayUtil.grow(bytes, size);
-      System.arraycopy(b, offset, bytes, posWrite, length);
+      bytes.writeBytes(posWrite, b, offset, length);
       posWrite += length;
     }
   }
 
-  final BytesReader getBytesReader(int pos) {
+  final BytesReader getBytesReader(long pos) {
     // TODO: maybe re-use via ThreadLocal?
     return new BytesReader(pos);
   }
 
   // Non-static: reads byte[] from FST
   final class BytesReader extends DataInput {
-    int pos;
+    long pos;
 
-    public BytesReader(int pos) {
+    public BytesReader(long pos) {
       this.pos = pos;
     }
 
     @Override
     public byte readByte() {
-      return bytes[pos--];
+      return bytes.readByte(pos--);
     }
 
     @Override
     public void readBytes(byte[] b, int offset, int len) {
       for(int i=0;i<len;i++) {
-        b[offset+i] = bytes[pos--];
+        b[offset+i] = bytes.readByte(pos--);
       }
     }
   }
Index: lucene/src/java/org/apache/lucene/util/fst/NodeHash.java
===================================================================
--- lucene/src/java/org/apache/lucene/util/fst/NodeHash.java	(revision 1154245)
+++ lucene/src/java/org/apache/lucene/util/fst/NodeHash.java	(working copy)
@@ -22,19 +22,19 @@
 // Used to dedup states (lookup already-frozen states)
 final class NodeHash<T> {
 
-  private int[] table;
+  private long[] table;
   private int count;
   private int mask;
   private final FST<T> fst;
   private final FST.Arc<T> scratchArc = new FST.Arc<T>();
 
   public NodeHash(FST<T> fst) {
-    table = new int[16];
+    table = new long[16];
     mask = 15;
     this.fst = fst;
   }
 
-  private boolean nodesEqual(Builder.UnCompiledNode<T> node, int address) throws IOException {
+  private boolean nodesEqual(Builder.UnCompiledNode<T> node, long address) throws IOException {
     final FST<T>.BytesReader in = fst.getBytesReader(0);
     fst.readFirstRealArc(address, scratchArc);
     if (scratchArc.bytesPerArc != 0 && node.numArcs != scratchArc.numArcs) {
@@ -64,7 +64,7 @@
   }
 
   // hash code for an unfrozen node.  This must be identical
-  // to the un-frozen case (below)!!
+  // to the frozen case (below)!!
   private int hash(Builder.UnCompiledNode<T> node) {
     final int PRIME = 31;
     //System.out.println("hash unfrozen");
@@ -72,35 +72,36 @@
     // TODO: maybe if number of arcs is high we can safely subsample?
     for(int arcIdx=0;arcIdx<node.numArcs;arcIdx++) {
       final Builder.Arc<T> arc = node.arcs[arcIdx];
-      //System.out.println("  label=" + arc.label + " target=" + ((Builder.CompiledNode) arc.target).address + " h=" + h + " output=" + fst.outputs.outputToString(arc.output) + " isFinal?=" + arc.isFinal);
       h = PRIME * h + arc.label;
-      h = PRIME * h + ((Builder.CompiledNode) arc.target).address;
+      h = PRIME * h + ((int) ((Builder.CompiledNode) arc.target).address);
       h = PRIME * h + arc.output.hashCode();
       h = PRIME * h + arc.nextFinalOutput.hashCode();
       if (arc.isFinal) {
         h += 17;
       }
+      //System.out.println("  label=" + arc.label + " target=" + ((Builder.CompiledNode) arc.target).address + " h=" + h + " output=" + fst.outputs.outputToString(arc.output) + " isFinal?=" + arc.isFinal);
+      
     }
     //System.out.println("  ret " + (h&Integer.MAX_VALUE));
     return h & Integer.MAX_VALUE;
   }
 
   // hash code for a frozen node
-  private int hash(int node) throws IOException {
+  private int hash(long node) throws IOException {
     final int PRIME = 31;
     final FST<T>.BytesReader in = fst.getBytesReader(0);
-    //System.out.println("hash frozen");
+    //System.out.println("hash frozen: " + node);
     int h = 0;
     fst.readFirstRealArc(node, scratchArc);
     while(true) {
-      //System.out.println("  label=" + scratchArc.label + " target=" + scratchArc.target + " h=" + h + " output=" + fst.outputs.outputToString(scratchArc.output) + " next?=" + scratchArc.flag(4) + " final?=" + scratchArc.isFinal());
       h = PRIME * h + scratchArc.label;
-      h = PRIME * h + scratchArc.target;
+      h = PRIME * h + (int) scratchArc.target;
       h = PRIME * h + scratchArc.output.hashCode();
       h = PRIME * h + scratchArc.nextFinalOutput.hashCode();
       if (scratchArc.isFinal()) {
         h += 17;
       }
+      //System.out.println("  label=" + scratchArc.label + " target=" + scratchArc.target + " h=" + h + " output=" + fst.outputs.outputToString(scratchArc.output) + " next?=" + scratchArc.flag(4) + " final?=" + scratchArc.isFinal());
       if (scratchArc.isLast()) {
         break;
       }
@@ -110,16 +111,16 @@
     return h & Integer.MAX_VALUE;
   }
 
-  public int add(Builder.UnCompiledNode<T> node) throws IOException {
+  public long add(Builder.UnCompiledNode<T> node) throws IOException {
     // System.out.println("hash: add count=" + count + " vs " + table.length);
     final int h = hash(node);
     int pos = h & mask;
     int c = 0;
     while(true) {
-      final int v = table[pos];
+      final long v = table[pos];
       if (v == 0) {
         // freeze & add
-        final int address = fst.addNode(node);
+        final long address = fst.addNode(node);
         //System.out.println("  now freeze addr=" + address);
         assert hash(address) == h : "frozenHash=" + hash(address) + " vs h=" + h;
         count++;
@@ -139,7 +140,7 @@
   }
 
   // called only by rehash
-  private void addNew(int address) throws IOException {
+  private void addNew(long address) throws IOException {
     int pos = hash(address) & mask;
     int c = 0;
     while(true) {
@@ -154,11 +155,11 @@
   }
 
   private void rehash() throws IOException {
-    final int[] oldTable = table;
-    table = new int[2*table.length];
+    final long[] oldTable = table;
+    table = new long[2*table.length];
     mask = table.length-1;
     for(int idx=0;idx<oldTable.length;idx++) {
-      final int address = oldTable[idx];
+      final long address = oldTable[idx];
       if (address != 0) {
         addNew(address);
       }
Index: lucene/src/java/org/apache/lucene/util/fst/RewritablePagedBytes.java
===================================================================
--- lucene/src/java/org/apache/lucene/util/fst/RewritablePagedBytes.java	(revision 0)
+++ lucene/src/java/org/apache/lucene/util/fst/RewritablePagedBytes.java	(revision 0)
@@ -0,0 +1,189 @@
+package org.apache.lucene.util.fst;
+
+import java.io.IOException;
+
+import org.apache.lucene.store.DataInput;
+import org.apache.lucene.store.DataOutput;
+import org.apache.lucene.util.ArrayUtil;
+
+/**
+ * TODO:  see if we can just use org.apache.lucene.util.PagedBytes
+ *        It seems like you cannot write to the same byte position twice, 
+ *        which is required here...
+ */
+public class RewritablePagedBytes {
+	private byte[][] bytes;
+	//private static final int bits = 4; for testing multiple pages on a workstation
+	private static final int bits = 30;  //TODO:  is there a way to make pageSize=Integer.MAX_VALUE and not have to divide?
+	private static final int pageSize = 1<<bits; 
+	private static final int mask = pageSize-1;
+	private long length;
+	
+	public RewritablePagedBytes() {
+		init(100);
+	}
+	
+	public RewritablePagedBytes(long initialCapacity) {
+		init(initialCapacity);
+	}
+	
+	public RewritablePagedBytes(DataInput in, long numBytes) throws IOException {
+		init(numBytes);
+		if(numBytes>0) {
+			for(byte[] b : bytes) {
+				int numToRead = (int)Math.min(RewritablePagedBytes.getPageSize(), numBytes);
+	    	in.readBytes(b, 0, numToRead);
+	    	numBytes -= numToRead;
+	    }
+		}    
+	}
+	
+	private void init(long initialCapacity) {
+		if(initialCapacity==0) {
+			initialCapacity=1;
+		}
+		bytes = new byte[(int) Math.ceil((double) initialCapacity / pageSize)][];
+		for(int i=0 ; i<bytes.length-1 ; i++) {
+			bytes[i] = new byte[pageSize];
+		}
+		int size = (int) (initialCapacity % pageSize);
+		if(size==0) {
+			size = pageSize;
+		}
+		bytes[bytes.length-1] = new byte[size];
+		length = 0;
+	}
+	
+	public void writeByte(byte b, long pos) {
+		final long minLength = pos+1;
+		final int page = (int) (pos >> bits);
+    final int elementIndex = (int) (pos & mask);
+		ensureCapacity(page, elementIndex, minLength);
+		bytes[page][elementIndex] = b;
+	}
+		
+	public void ensureCapacity(long pos) {
+		final long minLength = pos+1;
+		final int page = (int) (pos >> bits);
+    final int elementIndex = (int) (pos & mask);
+		ensureCapacity(page, elementIndex, minLength);
+	}
+	
+	private void ensureCapacity(int page, int elementIndex, long minLength) {
+		if(page >= bytes.length) { //not enough pages...add more		
+			
+			//make last inner array full-sized
+			final int lastArrIndex = bytes.length-1;
+			if(bytes[lastArrIndex].length<pageSize) {
+				byte[] newByteArr = new byte[pageSize];
+				System.arraycopy(bytes[lastArrIndex], 0, newByteArr, 0, bytes[lastArrIndex].length);
+				bytes[lastArrIndex] = newByteArr;
+			}
+			
+			//expand outer array
+			byte[][] newBytes = new byte[page+1][];
+			System.arraycopy(bytes, 0, newBytes, 0, bytes.length);
+			
+			//Add new intermediate pages
+			for(int i=bytes.length ; i<newBytes.length ; i++) {
+				newBytes[i] = new byte[pageSize];
+			}
+			
+			//Add final new page just big enough
+			newBytes[page] = new byte[elementIndex+1];
+			bytes = newBytes;
+		} else {		
+			//Ensure final page is at least big enough
+			bytes[page] = ArrayUtil.grow(bytes[page], elementIndex + 1);
+		}		
+		length = Math.max(length, minLength);		
+	}
+		
+	public void writeBytes(long pos, byte[] incoming, int incomingOffset, int incomingLength) {
+		ensureCapacity(pos + incomingLength - 1);
+		int page = (int) (pos >> bits);
+    final int elementIndex = (int) (pos & mask);
+		int currentLength = Math.min(pageSize - elementIndex, incomingLength);
+		System.arraycopy(incoming, incomingOffset, bytes[page], elementIndex, currentLength);
+		incomingLength -= currentLength;
+		while(incomingLength>0) {
+			page++;
+			incomingOffset += currentLength;
+			currentLength = Math.min(pageSize, incomingLength);
+			System.arraycopy(incoming, incomingOffset, bytes[page], 0, currentLength);
+			incomingLength -= currentLength;
+		}
+  }
+	
+	public byte readByte(long pos) {		
+		final int page = (int) (pos >> bits);
+    final int elementIndex = (int) (pos & mask);
+		return bytes[page][elementIndex];
+	}
+	
+	public void readBytes(long pos, byte[] reuse, int reuseOffset, int lengthToGet) {
+		if(lengthToGet==0) {
+			return;
+		}
+		int page = (int) (pos >> bits);
+    final int elementIndex = (int) (pos & mask);
+		int currentLength = Math.min(pageSize - elementIndex, lengthToGet);
+		System.arraycopy(bytes[page], elementIndex, reuse, reuseOffset, currentLength);
+		lengthToGet -= currentLength;
+		while(lengthToGet>0) {
+			page++;
+			reuseOffset += currentLength;
+			currentLength = Math.min(pageSize, lengthToGet);
+			System.arraycopy(bytes[page], 0, reuse, reuseOffset, currentLength);
+			lengthToGet -= currentLength;
+		}
+	}
+	
+	public void readBytes(long pos, RewritablePagedBytes reuse, long reuseOffset, long lengthToGet) {
+		if(lengthToGet==0) {
+			return;
+		}
+		int page = (int) (pos >> bits);
+    final int elementIndex = (int) (pos & mask);
+		int currentLength = (int) Math.min(pageSize - elementIndex, lengthToGet);
+		reuse.writeBytes(reuseOffset, bytes[page], elementIndex, currentLength);
+		lengthToGet -= currentLength;
+		while(lengthToGet>0) {
+			page++;
+			reuseOffset += currentLength;
+			currentLength = (int) Math.min(pageSize, lengthToGet);
+			reuse.writeBytes(reuseOffset, bytes[page], 0, currentLength);
+			lengthToGet -= currentLength;
+		}
+	}
+	
+	public void copyBytes(long srcPos, long destPos, int numBytes) {
+		if(numBytes==0) {
+			return;
+		}
+		byte[] temp = new byte[numBytes];
+		readBytes(srcPos, temp, 0, numBytes);
+		writeBytes(destPos, temp, 0, numBytes);		
+	}
+	
+	public long length() {
+		return length;
+	}	
+		
+	public void writeToOutput(DataOutput out) throws IOException {
+		if(length>0) {
+			for(byte[] b : bytes) {
+	    	out.writeBytes(b, 0, Math.min(b.length, pageSize));
+	    }
+		}
+	}
+	
+	/**
+	 * Page size should be as large as possible (as partial pages are allowed)
+	 * but to test having multiple pages on a smaller machine, set lower...
+	 * @lucene.internal
+	 */
+	public static int getPageSize() {
+		return pageSize;
+	}
+}
Index: lucene/src/java/org/apache/lucene/util/fst/Util.java
===================================================================
--- lucene/src/java/org/apache/lucene/util/fst/Util.java	(revision 1154245)
+++ lucene/src/java/org/apache/lucene/util/fst/Util.java	(working copy)
@@ -177,6 +177,8 @@
    * 
    * <p>
    * Note: larger FSTs (a few thousand nodes) won't even render, don't bother.
+   *       This code casts all "long" addresses down to "int"s, so FSTs larger 
+   *       than 2.1gb are entirely unsupported.
    * 
    * @param sameRank
    *          If <code>true</code>, the resulting <code>dot</code> file will try
@@ -209,7 +211,7 @@
 
     // A bitset of already seen states (target offset).
     final BitSet seen = new BitSet();
-    seen.set(startArc.target);
+    seen.set((int)startArc.target);
 
     // Shape for states.
     final String stateShape = "circle";
@@ -223,7 +225,7 @@
     }
 
     emitDotState(out, "initial", "point", "white", "");
-    emitDotState(out, Integer.toString(startArc.target), stateShape, 
+    emitDotState(out, Integer.toString((int)startArc.target), stateShape, 
         fst.isExpandedTarget(startArc) ? expandedNodeColor : null, 
         "");
     out.write("  initial -> " + startArc.target + "\n");
@@ -243,19 +245,19 @@
         
         if (fst.targetHasArcs(arc)) {
           // scan all arcs
-          final int node = arc.target;
+          final int node = (int)arc.target;
           fst.readFirstTargetArc(arc, arc);
           
           while (true) {
             // Emit the unseen state and add it to the queue for the next level.
-            if (arc.target >= 0 && !seen.get(arc.target)) {
+            if (arc.target >= 0 && !seen.get((int)arc.target)) {
               final boolean isExpanded = fst.isExpandedTarget(arc);
-              emitDotState(out, Integer.toString(arc.target), stateShape, 
+              emitDotState(out, Integer.toString((int)arc.target), stateShape, 
                   isExpanded ?  expandedNodeColor : null, 
-                  labelStates ? Integer.toString(arc.target) : ""); 
-              seen.set(arc.target);
+                  labelStates ? Integer.toString((int)arc.target) : ""); 
+              seen.set((int)arc.target);
               nextLevelQueue.add(new FST.Arc<T>().copyFrom(arc));
-              sameLevelStates.add(arc.target);
+              sameLevelStates.add((int)arc.target);
             }
 
             String outs;
Index: lucene/src/test/org/apache/lucene/util/fst/TestRewritablePagedBytes.java
===================================================================
--- lucene/src/test/org/apache/lucene/util/fst/TestRewritablePagedBytes.java	(revision 0)
+++ lucene/src/test/org/apache/lucene/util/fst/TestRewritablePagedBytes.java	(revision 0)
@@ -0,0 +1,77 @@
+package org.apache.lucene.util.fst;
+
+import junit.framework.Assert;
+
+import org.apache.lucene.util.LuceneTestCase;
+import org.junit.Test;
+
+
+public class TestRewritablePagedBytes extends LuceneTestCase {
+
+	@Test
+	public void testRewritablePagedBytes() throws Exception {
+		int size = 1000;
+				
+		//Check to see if it fills an initally-undersized PagedBytes with 1-at-a-time writes
+		RewritablePagedBytes bytes = new RewritablePagedBytes(3);		
+		{
+			byte[] controlBytes = new byte[size];
+			random.nextBytes(controlBytes);
+			for(int i=0 ; i<size ; i++) {
+				bytes.writeByte(controlBytes[i], i);
+			}
+			compareBytes(bytes, controlBytes);
+		}
+		
+		//Check to see if it fills an initally-undersized PagedBytes with a full array copy
+		bytes = new RewritablePagedBytes(3);
+		{
+			byte[] controlBytes = new byte[size>>1];
+			random.nextBytes(controlBytes);
+			bytes.writeBytes(0, controlBytes, 0, controlBytes.length);
+			compareBytes(bytes, controlBytes);
+		}
+		
+		//Check to see if it fills an initally-undersized PagedBytes with a partial array copy
+		bytes = new RewritablePagedBytes(3);
+		{
+			byte[] controlBytes = new byte[size>>1];
+			byte[] controlBytes1 = new byte[controlBytes.length-2];
+			random.nextBytes(controlBytes);
+			System.arraycopy(controlBytes, 1, controlBytes1, 0, controlBytes.length-2);			
+			bytes.writeBytes(0, controlBytes, 1, controlBytes.length-2);
+			compareBytes(bytes, controlBytes1);
+		}
+		
+		bytes = new RewritablePagedBytes(3);
+		{
+			byte[] controlBytes = new byte[(int)size];
+			random.nextBytes(controlBytes);
+			for(int i=0 ; i<size ; i++) {
+				bytes.writeByte(controlBytes[i], i);
+			}
+			byte[] readBytes = new byte[controlBytes.length];
+			for(int i=0 ; i<readBytes.length ; i++) {
+				readBytes[i] = bytes.readByte(i);
+			}
+			for(int i=0 ; i<readBytes.length ; i++) {
+				Assert.assertTrue(controlBytes[i]==readBytes[i]);
+			}
+			Assert.assertTrue(controlBytes.length==bytes.length());
+		}
+	}
+	
+	private void compareBytes(RewritablePagedBytes bytes, byte[] controlBytes) {
+		byte[] readBytes = new byte[controlBytes.length];
+		bytes.readBytes(0, readBytes, 0, controlBytes.length);
+		for(int i=0 ; i<controlBytes.length ; i++) {
+			Assert.assertTrue(readBytes[i]==controlBytes[i]);
+		}
+		
+		RewritablePagedBytes pbm = new RewritablePagedBytes(1);
+		bytes.readBytes(0, pbm, 0, controlBytes.length);
+		for(int i=0 ; i<controlBytes.length ; i++) {
+			Assert.assertTrue(pbm.readByte(i)==controlBytes[i]);
+		}
+	}
+}
