/FileExtraction/Java_unseen/dlitz_resin/modules/resin/src/com/caucho/db/sql/SelectResult.java
/*
 * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.db.sql;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.SQLException;

import com.caucho.db.blob.BlobInputStream;
import com.caucho.db.block.BlockStore;
import com.caucho.db.table.TableIterator;
import com.caucho.db.table.Column.ColumnType;
import com.caucho.sql.SQLExceptionWrapper;
import com.caucho.util.CharBuffer;
import com.caucho.util.FreeList;
import com.caucho.util.IntArray;
import com.caucho.util.L10N;
import com.caucho.util.QDate;
import com.caucho.vfs.TempBuffer;

public class SelectResult {
  private static final L10N L = new L10N(SelectResult.class);

  private static final FreeList<SelectResult> _freeList
    = new FreeList<SelectResult>(32);

  private static final int SIZE = TempBuffer.SIZE;

  private static QDate _date = new QDate();

  private CharBuffer _cb = new CharBuffer();
  private byte []_blob = new byte[128];

  private Expr []_exprs;
  private BlockStore []_stores = new BlockStore[32];

  private TableIterator []_rows = new TableIterator[16];

  private Order _order;
  private IntArray _orderIndex;

  private TempBuffer []_tempBuffers = new TempBuffer[128];
  private byte [][]_buffers = new byte[128][];
  private int _length;
  private int _rowCount;

  private int _row;

  private int _offset;
  private int _rowOffset;
  private int _columnOffset;
  private int _column;

  private boolean _wasNull;

  private SelectResult()
  {
  }

  public static SelectResult create(Expr []exprs, Order order)
  {
    SelectResult rs = _freeList.allocate();

    if (rs == null)
      rs = new SelectResult();

    rs.init(exprs, order);

    return rs;
  }

  /**
   * Initialize the iterator.
   */
  TableIterator []initRows(FromItem []fromItems)
  {
    if (_rows.length < fromItems.length)
      _rows = new TableIterator[fromItems.length];

    for (int i = 0; i < fromItems.length; i++) {
      if (_rows[i] == null)
        _rows[i] = new TableIterator();
      _rows[i].init(fromItems[i].getTable());
    }

    return _rows;
  }

  /**
   * Initialize based on the exprs.
   */
  private void init(Expr []exprs, Order order)
  {
    _exprs = exprs;
    _order = order;

    if (order != null)
      _orderIndex = new IntArray();

    if (_stores.length < _exprs.length) {
      _stores = new BlockStore[exprs.length];
    }

    for (int i = 0; i < exprs.length; i++)
      _stores[i] = exprs[i].getTable();

    _length = 0;
    _rowCount = 0;
  }

  void initRead()
    throws SQLException
  {
    if (_order != null)
      _order.sort(this, _orderIndex);

    _row = -1;
    _offset = 0;
    _column = 0;
    _rowOffset = 0;
    _columnOffset = 0;
  }

  /**
   * Moves to the next row, returning true if the row has data.
   */
  public boolean next()
    throws SQLException
  {
    if (++_row < _rowCount) {
      if (_orderIndex != null) {
        _offset = _orderIndex.get(_row);
      }
      else if (_row != 0) {
        _offset = _columnOffset;
        skipColumns(_exprs.length - _column);
      }

      _column = 0;
      _rowOffset = _offset;
      _columnOffset = _rowOffset;

      return true;
    }
    else
      return false;
  }

  /**
   * Returns the expressions.
   */
  public Expr []getExprs()
  {
    return _exprs;
  }

  /**
   * Returns the column index with the given name.
   */
  public int findColumnIndex(String name)
    throws SQLException
  {
    for (int i = 0; i < _exprs.length; i++) {
      if (_exprs[i].getName().equals(name))
        return i + 1;
    }

    throw new SQLException(L.l("column `{0}' does not exist.", name));
  }

  /**
   * Returns the string value of the given index.
   */
  public String getString(int index)
    throws SQLException
  {
    _wasNull = false;

    setColumn(index);

    int type = read();
    
    if (type < 0)
      return null;
    
    ColumnType cType = ColumnType.values()[type];

    switch (cType) {
    case NONE:
      _wasNull = true;
      return null;

    case SHORT:
      {
        int value = (short) ((read() << 8) + (read()));

        return String.valueOf(value);
      }

    case INT:
      {
        int value = ((read() << 24) +
                     (read() << 16) +
                     (read() << 8) +
                     (read()));

        return String.valueOf(value);
      }

    case LONG:
      {
        long value = (((long) read() << 56) +
                      ((long) read() << 48) +
                      ((long) read() << 40) +
                      ((long) read() << 32) +
                      ((long) read() << 24) +
                      ((long) read() << 16) +
                      ((long) read() << 8) +
                      ((long) read()));

        return String.valueOf(value);
      }

    case DOUBLE:
      {
        long value = (((long) read() << 56) +
                      ((long) read() << 48) +
                      ((long) read() << 40) +
                      ((long) read() << 32) +
                      ((long) read() << 24) +
                      ((long) read() << 16) +
                      ((long) read() << 8) +
                      ((long) read()));

        return String.valueOf(Double.longBitsToDouble(value));
      }

    case DATE:
      {
        long value = (((long) read() << 56) +
                      ((long) read() << 48) +
                      ((long) read() << 40) +
                      ((long) read() << 32) +
                      ((long) read() << 24) +
                      ((long) read() << 16) +
                      ((long) read() << 8) +
                      ((long) read()));

        return QDate.formatISO8601(value);
      }

    case VARCHAR:
      return readString();

    case BLOB:
      return readBlobString();

    case BINARY:
      {
        int len = read();

        char []chars = new char[len];

        for (int i = 0; i < len; i++) {
          chars[i] = (char) (read() & 0xff);
        }

        return new String(chars);
      }


    default:
      throw new RuntimeException("unknown column type:" + type + " column:" + index);
    }
  }

  /**
   * Returns the string value of the given index.
   */
  public byte [] getBytes(int index)
    throws SQLException
  {
    _wasNull = false;

    setColumn(index);

    int type = read();
    
    if (type < 0)
      return null;
    
    ColumnType cType = ColumnType.values()[type];

    switch (cType) {
    case NONE:
      _wasNull = true;
      return null;

    case BINARY:
      {
        int len = read();

        byte []bytes = new byte[len];

        read(bytes, 0, len);

        return bytes;
      }

    case BLOB:
      return readBlobBytes();

    default:
      throw new RuntimeException("unknown column type:" + type + " column:" + index);
    }
  }

  /**
   * Returns the integer value of the column.
   */
  public int getInt(int index)
    throws SQLException
  {
    _wasNull = false;
    setColumn(index);

    int type = read();
    
    if (type < 0)
      return 0;
    
    ColumnType cType = ColumnType.values()[type];
    
    switch (cType) {
    case NONE:
      _wasNull = true;
      return 0;

    case SHORT:
      {
        int value = (short) ((read() << 8)
                             + (read()));

        return value;
      }

    case INT:
      {
        int value = ((read() << 24) +
                     (read() << 16) +
                     (read() << 8) +
                     (read()));

        return value;
      }

    case LONG:
    case DATE:
      {
        long value = (((long) read() << 56) +
                      ((long) read() << 48) +
                      ((long) read() << 40) +
                      ((long) read() << 32) +
                      ((long) read() << 24) +
                      ((long) read() << 16) +
                      ((long) read() << 8) +
                      ((long) read()));

        return (int) value;
      }

    case DOUBLE:
      {
        long value = (((long) read() << 56) +
                      ((long) read() << 48) +
                      ((long) read() << 40) +
                      ((long) read() << 32) +
                      ((long) read() << 24) +
                      ((long) read() << 16) +
                      ((long) read() << 8) +
                      ((long) read()));

        return (int) Double.longBitsToDouble(value);
      }

    case VARCHAR:
      return Integer.parseInt(readString());

    case BLOB:
      return Integer.parseInt(readBlobString());

    default:
      return 0;
    }
  }

  /**
   * Returns the long value of the column.
   */
  public long getLong(int index)
    throws SQLException
  {
    _wasNull = false;
    setColumn(index);

    int type = read();

    if (type < 0)
      return 0;
    
    ColumnType cType = ColumnType.values()[type];

    switch (cType) {
    case NONE:
      _wasNull = true;
      return 0;

    case SHORT:
      {
        int value = (short) ((read() << 8)
                             + (read()));

        return value;
      }

    case INT:
      {
        int value = ((read() << 24) +
                     (read() << 16) +
                     (read() << 8) +
                     (read()));

        return value;
      }

    case LONG:
    case DATE:
      {
        long value = (((long) read() << 56) +
                      ((long) read() << 48) +
                      ((long) read() << 40) +
                      ((long) read() << 32) +
                      ((long) read() << 24) +
                      ((long) read() << 16) +
                      ((long) read() << 8) +
                      ((long) read()));

        return value;
      }

    case DOUBLE:
      {
        long value = (((long) read() << 56) +
                      ((long) read() << 48) +
                      ((long) read() << 40) +
                      ((long) read() << 32) +
                      ((long) read() << 24) +
                      ((long) read() << 16) +
                      ((long) read() << 8) +
                      ((long) read()));

        return (long) Double.longBitsToDouble(value);
      }

    case VARCHAR:
      return Long.parseLong(readString());

    case BLOB:
      return Long.parseLong(readBlobString());

    default:
      return 0;
    }
  }

  /**
   * Returns a double value from this column.
   */
  public double getDouble(int index)
    throws SQLException
  {
    _wasNull = false;
    setColumn(index);

    int type = read();

    if (type < 0)
      return 0;
    
    ColumnType cType = ColumnType.values()[type];
    
    switch (cType) {
    case NONE:
      _wasNull = true;
      return 0;

    case SHORT:
      {
        int value = (short) ((read() << 8)
                             + (read()));

        return value;
      }

    case INT:
      {
        int value = ((read() << 24) +
                     (read() << 16) +
                     (read() << 8) +
                     (read()));

        return value;
      }

    case LONG:
    case DATE:
      {
        long value = (((long) read() << 56) +
                      ((long) read() << 48) +
                      ((long) read() << 40) +
                      ((long) read() << 32) +
                      ((long) read() << 24) +
                      ((long) read() << 16) +
                      ((long) read() << 8) +
                      ((long) read()));

        return value;
      }

    case DOUBLE:
      {
        long value = (((long) read() << 56) +
                      ((long) read() << 48) +
                      ((long) read() << 40) +
                      ((long) read() << 32) +
                      ((long) read() << 24) +
                      ((long) read() << 16) +
                      ((long) read() << 8) +
                      ((long) read()));

        return Double.longBitsToDouble(value);
      }

    case VARCHAR:
      return Double.parseDouble(readString());

    case BLOB:
      return Double.parseDouble(readBlobString());

    default:
      return 0;
    }
  }

  public long getDate(int index)
    throws SQLException
  {
    _wasNull = false;
    setColumn(index);

    int type = read();

    if (type < 0)
      return 0;
    
    ColumnType cType = ColumnType.values()[type];
    
    switch (cType) {
    case NONE:
      _wasNull = true;
      return 0;

    case LONG:
    case DATE:
      {
        long value = (((long) read() << 56) +
                      ((long) read() << 48) +
                      ((long) read() << 40) +
                      ((long) read() << 32) +
                      ((long) read() << 24) +
                      ((long) read() << 16) +
                      ((long) read() << 8) +
                      ((long) read()));

        return value;
      }

    case INT:
      {
        long value = (((long) read() << 24) +
                      ((long) read() << 16) +
                      ((long) read() << 8) +
                      ((long) read()));

        return value;
      }

    case VARCHAR:
      {
        String value = readString();

        synchronized (_date) {
          try {
            return _date.parseDate(value);
          } catch (Exception e) {
            throw new SQLExceptionWrapper(e);
          }
        }
      }

    case BLOB:
      {
        String value = readBlobString();

        synchronized (_date) {
          try {
            return _date.parseDate(value);
          } catch (Exception e) {
            throw new SQLExceptionWrapper(e);
          }
        }
      }

    default:
      throw new SQLException("unknown type: " + type);
    }
  }

  /**
   * Returns the blob value of the given index.
   */
  public Blob getBlob(int index)
    throws SQLException
  {
    _wasNull = false;

    setColumn(index);

    int type = read();

    if (type < 0)
      return null;
    
    ColumnType cType = ColumnType.values()[type];

    switch (cType) {
    case NONE:
      _wasNull = true;
      return null;

    case BLOB:
      return getBlob();

    default:
      throw new RuntimeException("column can't be retrieved as a blob:" + type + " column:" + index);
    }
  }

  /**
   * Returns the clob value of the given index.
   */
  public Clob getClob(int index)
    throws SQLException
  {
    _wasNull = false;

    setColumn(index);

    int type = read();

    if (type < 0)
      return null;
    
    ColumnType cType = ColumnType.values()[type];

    switch (cType) {
    case NONE:
      _wasNull = true;
      return null;

    case BLOB:
      return getClob();

    default:
      throw new RuntimeException("column can't be retrieved as a clob:" + type + " column:" + index);
    }
  }

  /**
   * Returns true if the last column read was null.
   */
  public boolean wasNull()
  {
    return _wasNull;
  }

  /**
   * Returns the string value for the result set.
   */
  private String readString()
    throws SQLException
  {
    int length = ((read() << 24)
                  + (read() << 16)
                  + (read() << 8)
                  + (read()));

    int len = length >> 1;

    CharBuffer cb = _cb;
    cb.ensureCapacity(len);
    char []cBuf = cb.getBuffer();
    int cLen = 0;

    for (; len > 0; len--) {
      int ch1 = read();
      int ch2 = read();

      cBuf[cLen++] = (char) (((ch1 & 0xff) << 8) + (ch2 & 0xff));
    }

    return new String(cBuf, 0, cLen);
  }

  /**
   * Returns the blob value for the result set.
   */
  private Blob getBlob()
    throws SQLException
  {
    BlobImpl blob = new BlobImpl();

    blob.setStore(_stores[_column]);

    byte []inode = blob.getInode();

    read(inode, 0, 128);

    return blob;
  }

  /**
   * Returns the clob value for the result set.
   */
  private Clob getClob()
    throws SQLException
  {
    ClobImpl clob = new ClobImpl();

    clob.setStore(_stores[_column]);

    byte []inode = clob.getInode();

    read(inode, 0, 128);

    return clob;
  }

  /**
   * Returns the string value for the result set.
   */
  private String readBlobString()
    throws SQLException
  {
    read(_blob, 0, 128);

    CharBuffer cb = _cb;
    cb.clear();

    BlobInputStream is = null;
    try {
      is = new BlobInputStream(_stores[_column], _blob, 0);

      int ch;
      while ((ch = is.read()) >= 0) {
        if (ch < 0x80)
          cb.append((char) ch);
      }
    } catch (IOException e) {
      throw new SQLExceptionWrapper(e);
    }

    return cb.toString();
  }

  /**
   * Returns the string value for the result set.
   */
  private byte []readBlobBytes()
    throws SQLException
  {
    read(_blob, 0, 128);

    ByteArrayOutputStream bos = new ByteArrayOutputStream();

    BlobInputStream is = null;
    try {
      is = new BlobInputStream(_stores[_column], _blob, 0);

      int ch;
      while ((ch = is.read()) >= 0) {
        bos.write(ch);
      }
    } catch (IOException e) {
      throw new SQLExceptionWrapper(e);
    }

    return bos.toByteArray();
  }

  /**
   * Set the column in the current row.
   */
  private void setColumn(int column)
  {
    if (column < _column) {
      _offset = _rowOffset;
      skipColumns(column);
    }
    else {
      _offset = _columnOffset;
      skipColumns(column - _column);
    }

    _column = column;
    _columnOffset = _offset;
  }

  /**
   * Set the column in the current row.
   */
  void setRow(int rowOffset)
  {
    _rowOffset = rowOffset;
    _offset = rowOffset;
    _column = 0;
    _columnOffset = rowOffset;
  }

  /**
   * Skips the specified number of columns.
   */
  private void skipColumns(int count)
  {
    for (; count > 0; count--) {

      int type = read();

      if (type < 0)
        return;
      
      ColumnType cType = ColumnType.values()[type];

      int sublen;

      switch (cType) {
      case NONE:
        break;

      case VARCHAR:
        int l0 = read();
        int l1 = read();
        int l2 = read();
        int l3 = read();

        sublen = ((l0 << 24) +
                  (l1 << 16) +
                  (l2 << 8) +
                  (l3));

        _offset += sublen;
        break;

      case BINARY:
        sublen = read();

        _offset += sublen;
        break;

      case SHORT:
        _offset += 2;
        break;
      case INT:
        _offset += 4;
        break;
      case LONG:
      case DOUBLE:
      case DATE:
        _offset += 8;
        break;

      case BLOB:
        _offset += 128;
        break;

      default:
        throw new RuntimeException("Unknown column type: " + type);
      }
    }
  }

  /**
   * Starts a row
   */
  public void startRow()
  {
    if (_orderIndex != null)
      _orderIndex.add(_length);

    _rowCount++;
  }

  /**
   * Writes a null.
   */
  public void writeNull()
  {
    write(ColumnType.NONE.ordinal());
  }

  /**
   * Writes a string.
   */
  public void writeString(String s)
  {
    write(ColumnType.VARCHAR.ordinal());
    int stringLength = s.length();
    int length = 2 * stringLength;
    write(length >> 24);
    write(length >> 16);
    write(length >> 8);
    write(length);

    for (int i = 0; i < stringLength; i++) {
      char ch = s.charAt(i);

      write(ch << 8);
      write(ch);
    }
  }

  /**
   * Writes a string.
   */
  public void writeString(byte []buffer, int offset, int stringLength)
  {
    int rLength = _length;

    int rOffset = rLength % SIZE;
    int rBlockId = rLength / SIZE;

    if (_buffers[rBlockId] == null) {
      TempBuffer tempBuffer = TempBuffer.allocate();
      _tempBuffers[rBlockId] = tempBuffer;
      _buffers[rBlockId] = tempBuffer.getBuffer();
    }

    byte []rBuffer = _buffers[rBlockId];
    rBuffer[rOffset] = (byte) ColumnType.VARCHAR.ordinal();

    int length = 2 * stringLength;

    if (rOffset + 5 < rBuffer.length) {
      rBuffer[rOffset + 1] = (byte) (length >> 24);
      rBuffer[rOffset + 2] = (byte) (length >> 16);
      rBuffer[rOffset + 3] = (byte) (length >> 8);
      rBuffer[rOffset + 4] = (byte) length;

      if (rOffset + 5 + length < SIZE) {
        System.arraycopy(buffer, offset, rBuffer, rOffset + 5, length);

        _length = rLength + 5 + length;
      }
      else {
        _length = rLength + 5;
        write(buffer, offset, length);
      }
    }
    else {
      _length = rLength + 1;

      write(length >> 24);
      write(length >> 16);
      write(length >> 8);
      write(length);

      write(buffer, offset, length);
    }
  }

  /**
   * Writes a binary.
   */
  public void writeBinary(byte []buffer, int offset, int length)
  {
    write(ColumnType.BINARY.ordinal());
    write(length);
    write(buffer, offset, length);
  }

  /**
   * Writes a string.
   */
  public void writeBlock(int code, byte []buffer, int offset, int length)
  {
    write(code);
    write(buffer, offset, length);
  }

  /**
   * Writes a double.
   */
  public void writeDouble(double dValue)
  {
    write(ColumnType.DOUBLE.ordinal());

    long value = Double.doubleToLongBits(dValue);

    write((int) (value >> 56));
    write((int) (value >> 48));
    write((int) (value >> 40));
    write((int) (value >> 32));
    write((int) (value >> 24));
    write((int) (value >> 16));
    write((int) (value >> 8));
    write((int) value);
  }

  /**
   * Writes a long.
   */
  public void writeLong(long value)
  {
    write(ColumnType.LONG.ordinal());
    write((int) (value >> 56));
    write((int) (value >> 48));
    write((int) (value >> 40));
    write((int) (value >> 32));
    write((int) (value >> 24));
    write((int) (value >> 16));
    write((int) (value >> 8));
    write((int) value);
  }

  /**
   * Writes a date.
   */
  public void writeDate(long value)
  {
    write(ColumnType.DATE.ordinal());
    write((int) (value >> 56));
    write((int) (value >> 48));
    write((int) (value >> 40));
    write((int) (value >> 32));
    write((int) (value >> 24));
    write((int) (value >> 16));
    write((int) (value >> 8));
    write((int) value);
  }

  /**
   * Writes an long.
   */
  public void writeInt(int value)
  {
    write(ColumnType.INT.ordinal());
    write(value >> 24);
    write(value >> 16);
    write(value >> 8);
    write(value);
  }

  /**
   * Writes a short
   */
  public void writeShort(int value)
  {
    write(ColumnType.SHORT.ordinal());
    write(value >> 8);
    write(value);
  }

  /**
   * Writes a blob.
   */
  public void writeBlob(byte []buffer, int offset)
  {
    write(ColumnType.BLOB.ordinal());

    write(buffer, offset, 128);
  }

  /**
   * Reads the next byte.
   */
  private int read()
  {
    int offset = _offset;

    if (_length <= offset)
      return -1;

    _offset = offset + 1;

    byte []buf = _buffers[offset / SIZE];

    return buf[offset % SIZE] & 0xff;
  }

  /**
   * Reads the next byte.
   */
  private int read(byte []buffer, int bufOffset, int bufLength)
  {
    int offset = _offset;
    int length = _length;
    byte [][]buffers = _buffers;

    for (int i = bufLength; i > 0; i--) {
      if (length <= offset) {
        _offset = offset;
        return -1;
      }

      byte []buf = buffers[offset / SIZE];

      buffer[bufOffset] = buf[offset % SIZE];

      offset++;
      bufOffset++;
    }

    _offset = offset;

    return bufLength;
  }

  /**
   * Writes the next byte.
   */
  public void write(int value)
  {
    int length = _length;
    int rOffset = length % SIZE;
    int blockId = length / SIZE;

    while (_buffers.length <= blockId) {
      byte [][]newBuffers = new byte[2 * _buffers.length][];

      System.arraycopy(_buffers, 0, newBuffers, 0, _buffers.length);
      _buffers = newBuffers;

      TempBuffer []newTempBuffers = new TempBuffer[newBuffers.length];
      System.arraycopy(_tempBuffers, 0, newTempBuffers, 0,
                       _tempBuffers.length);
      _tempBuffers = newTempBuffers;
    }

    byte []buffer = _buffers[blockId];

    if (buffer == null) {
      TempBuffer tempBuffer = TempBuffer.allocate();
      _tempBuffers[blockId] = tempBuffer;
      _buffers[blockId] = tempBuffer.getBuffer();

      buffer = _buffers[blockId];
    }

    buffer[rOffset] = (byte) value;

    _length = length + 1;
  }

  /**
   * Writes a buffer
   */
  public void write(byte []buffer, int offset, int length)
  {
    int rLength = _length;

    while (length > 0) {
      int rOffset = rLength % SIZE;

      int rBufferId = rLength / SIZE;

      if (rOffset == 0) {
        TempBuffer tempBuffer = TempBuffer.allocate();
        if (_tempBuffers.length <= rBufferId) {
          int len = _tempBuffers.length;

          TempBuffer []newTempBuffers = new TempBuffer[len + 32];
          System.arraycopy(_tempBuffers, 0, newTempBuffers, 0, len);
          _tempBuffers = newTempBuffers;

          byte [][]newBuffers = new byte[len + 32][];
          System.arraycopy(_buffers, 0, newBuffers, 0, len);
          _buffers = newBuffers;

        }

        _tempBuffers[rBufferId] = tempBuffer;
        _buffers[rBufferId] = tempBuffer.getBuffer();
      }

      byte []rBuffer = _buffers[rBufferId];

      int sublen = rBuffer.length - rOffset;

      if (length < sublen)
        sublen = length;

      System.arraycopy(buffer, offset, rBuffer, rOffset, sublen);

      length -= sublen;
      offset += sublen;
      rLength += sublen;
    }

    _length = rLength;
  }

  public void close()
  {
    for (int i = 0; i < _buffers.length; i++) {
      TempBuffer buffer = _tempBuffers[i];

      if (buffer != null)
        TempBuffer.free(buffer);

      _tempBuffers[i] = null;
      _buffers[i] = null;
    }
    
    for (int i = _stores.length - 1; i >= 0; i--) {
      _stores[i] = null;
    }

    _order = null;
    _orderIndex = null;

    _freeList.free(this);
  }
}
