Package com.shop.util.ccdb2

Source Code of com.shop.util.ccdb2.CCDB2File

/*
* Copyright 2008-2009 SHOP.COM
*
* Licensed 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.
*/
package com.shop.util.ccdb2;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.FileNotFoundException;

/**
* Manages the DB file. This class is, essentially, a very quick disk space allocator.<br>
*
* @author Jordan Zimmerman
*/
class CCDB2File
{

  /**
   * @param driver the driver
   * @param filePath full path to the file to manage
   * @param nullByte value to store in newly allocated file space.
   * @throws IOException errors
   */
  CCDB2File(CCDB2Driver driver, String filePath, byte nullByte) throws IOException
  {
    long    allocationChunkSize = driver.getAllocationChunkSize();

    File     localFle = new File(filePath);
    fFilePool = new CCDB2FilePool(driver, localFle);
    fActualSize = localFle.length();
    fLogicalSize = fActualSize;
    fAllocationLock = new Object();
    fAllocationChunkSize = (allocationChunkSize > 0) ? allocationChunkSize : DEFAULT_ALLOCATION_CHUNK_SIZE;

    fNullBuffer = new byte[NULL_BUFFER_SIZE];
    for ( int i = 0; i < NULL_BUFFER_SIZE; ++i )
    {
      fNullBuffer[i] = nullByte;
    }
  }

  /**
   * Return the current number of users of this file
   *
   * @return user count
   */
  int getFilePoolSize()
  {
    return fFilePool.getTotalQty();
  }

  /**
   * Returns the chunk size being used. i.e. the minimum number of bytes allocated
   *
   * @return chunk size
   */
  long getAllocationChunkSize()
  {
    return fAllocationChunkSize;
  }

  /**
   * close the file
   */
  void close()
  {
    fFilePool.close();
  }

  /**
   * Return the file's size as client's would see it. i.e., if you allocate 1 byte, you will actually
   * be allocating {@link #getAllocationChunkSize()} bytes. However, this method will still return 1.
   *
   * @return the logical size
   */
  long getLogicalSize()
  {
    return fLogicalSize;
  }

  /**
   * Returns the actual size of the file on disk (including unused space in an over-allocated chunk).
   *
   * @return actualy size
   */
  long getActualSize()
  {
    return fActualSize;
  }

  /**
   * Return an IO object to use for the file. This call MUST be balanced by a call to {@link #releaseFile(CCDB2io)}. If you do not
   * call release() you will have a resource leak.
   *
   * @return the I/O
   * @throws IOException errors
   */
  CCDB2io getFile() throws IOException
  {
    final RandomAccessFile     f = fFilePool.get();
    return new CCDB2io()
    {
      @Override
      public void seek(long i) throws IOException
      {
        f.seek(i);
      }

      @Override
      public void writeByte(byte b) throws IOException
      {
        f.writeByte(b);
      }

      @Override
      public void writeInt(int i) throws IOException
      {
        f.writeInt(i);
      }

      @Override
      public void writeBoolean(boolean b) throws IOException
      {
        f.writeBoolean(b);
      }

      @Override
      public void writeLong(long l) throws IOException
      {
        f.writeLong(l);
      }

      @Override
      public byte readByte() throws IOException
      {
        return f.readByte();
      }

      @Override
      public int readInt() throws IOException
      {
        return f.readInt();
      }

      @Override
      public boolean readBoolean() throws IOException
      {
        return f.readBoolean();
      }

      @Override
      public long readLong() throws IOException
      {
        return f.readLong();
      }

      @Override
      public void write(byte[] bytes, int length) throws IOException
      {
        f.write(bytes, 0, length);
      }

      @Override
      public void write(byte[] b) throws IOException
      {
        f.write(b);
      }

      @Override
      public void readFully(byte[] b) throws IOException
      {
        f.readFully(b);
      }

      @Override
      public RandomAccessFile getUnderlyingFile()
      {
        return f;
      }
    };
  }

  /**
   * Release an IO object returned from {@link #getFile()}
   *
   * @param file the file
   */
  void releaseFile(CCDB2io file)
  {
    fFilePool.release(file.getUnderlyingFile());
  }

  /**
   * Used internally when loading the file to strip off excess
   *
   * @param size size to truncate at
   */
  void setLogicalSize(long size)
  {
    synchronized(fAllocationLock)
    {
      fLogicalSize = size;
    }
  }

  /**
   * Allocates space in the file and returns an address to that space.
   *
   * @param amount the amount to allocate
   * @return the address
   * @throws FileNotFoundException no such file
   * @throws CCDB2SetFileLengthException ran out of disk space or a similar error
   */
  long      allocate(int amount) throws CCDB2SetFileLengthException, FileNotFoundException
  {
    long        offset;
    RandomAccessFile  file = null;
    try
    {
      synchronized(fAllocationLock)
      {
        offset = fLogicalSize;

        long newLogicalSize = offset + amount;
        if ( newLogicalSize > fActualSize )
        {
          long         needed = newLogicalSize - fActualSize;
          long         addAmount = quantize(needed, fAllocationChunkSize);

          file = fFilePool.get();
          long         fileLength;
          try
          {
            nullNewFile(file, fActualSize, addAmount);
            fileLength = file.length();
          }
          catch ( IOException e )
          {
            throw new CCDB2SetFileLengthException(e.getMessage());
          }

          fActualSize += addAmount;
          assert fActualSize == fileLength;
        }
        fLogicalSize = newLogicalSize;
      }
    }
    finally
    {
      if ( file != null )
      {
        fFilePool.release(file);
      }
    }

    return offset;
  }

  private static long quantize(long value, long quantizeSize)
  {
    return ((value / quantizeSize) + 1) * quantizeSize;
  }

  private void nullNewFile(RandomAccessFile file, long offset, long size) throws IOException
  {
    file.seek(offset);
    while ( size > 0 )
    {
      if ( size < fNullBuffer.length )
      {
        int intSize = (int)size;
        file.write(fNullBuffer, 0, intSize);
        size -= intSize;
      }
      else
      {
        file.write(fNullBuffer);
        size -= fNullBuffer.length;
      }
    }

    if ( size < 0 )
    {
      throw new IOException("size went negative in nullNewFile(): " + size);
    }
  }

  private static final long   DEFAULT_ALLOCATION_CHUNK_SIZE = 0x1000000// 16 MB

  private static final int  NULL_BUFFER_SIZE = 0x100000;

  private final Object    fAllocationLock;
  private final byte[]    fNullBuffer;
  private final long       fAllocationChunkSize;

  private  CCDB2FilePool    fFilePool;
  private volatile long    fLogicalSize;
  private volatile long    fActualSize;
}
TOP

Related Classes of com.shop.util.ccdb2.CCDB2File

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.