0001: /*
0002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
0003: *
0004: * This file is part of Resin(R) Open Source
0005: *
0006: * Each copy or derived work must preserve the copyright notice and this
0007: * notice unmodified.
0008: *
0009: * Resin Open Source is free software; you can redistribute it and/or modify
0010: * it under the terms of the GNU General Public License as published by
0011: * the Free Software Foundation; either version 2 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * Resin Open Source is distributed in the hope that it will be useful,
0015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
0017: * of NON-INFRINGEMENT. See the GNU General Public License for more
0018: * details.
0019: *
0020: * You should have received a copy of the GNU General Public License
0021: * along with Resin Open Source; if not, write to the
0022: *
0023: * Free Software Foundation, Inc.
0024: * 59 Temple Place, Suite 330
0025: * Boston, MA 02111-1307 USA
0026: *
0027: * @author Scott Ferguson
0028: */
0029:
0030: package com.caucho.db.store;
0031:
0032: import com.caucho.db.Database;
0033: import com.caucho.lifecycle.Lifecycle;
0034: import com.caucho.log.Log;
0035: import com.caucho.sql.SQLExceptionWrapper;
0036: import com.caucho.util.L10N;
0037: import com.caucho.vfs.Path;
0038: import com.caucho.vfs.RandomAccessStream;
0039:
0040: import java.io.IOException;
0041: import java.lang.ref.SoftReference;
0042: import java.sql.SQLException;
0043: import java.util.logging.Level;
0044: import java.util.logging.Logger;
0045:
0046: /**
0047: * The store manages the block-based persistent store file. Each table
0048: * will have its own store file, table.db.
0049: *
0050: * The store is block-based, where each block is 64k. Block allocation
0051: * is tracked by a free block, block 0. Each block is represented as a
0052: * two-byte value. The first byte is the allocation code: free, row,
0053: * or used. The second byte is a fragment allocation mask.
0054: *
0055: * Since 64k stores 32k entries, the allocation block can handle
0056: * a 2G database size. If the database is larger, another free block
0057: * occurs at block 32k handling another 2G.
0058: *
0059: * The blocks are marked as free (00), row (01), used (10) or fragment(11).
0060: * Row-blocks are table rows, so a table iterator will only look at
0061: * the row blocks. Used blocks are for special blocks like the
0062: * free list. Fragments are for blobs.
0063: *
0064: * Each store has a unique id in the database. The store id is merged with
0065: * the block number in the store to create a unique block id. There are
0066: * 64k allowed stores (and therefore 64k tables), leaving 64 - 16 = 48 bits
0067: * for the blocks in a table, i.e. 2 ^ 48 blocks = 256T blocks.
0068: *
0069: * block index: the block number in the file.
0070: *
0071: * address: the address of a byte within the store, treating the file as a
0072: * flat file.
0073: *
0074: * block id: the unique id of the block in the database.
0075: *
0076: * <h3>Blobs and fragments</h3>
0077: *
0078: * Fragments are stored in 8k chunks with a single byte prefix indicating
0079: * its use.
0080: *
0081: * <h3>Transactions</h3>
0082: *
0083: * Fragments are not associated with transactions. The rollback is
0084: * associated with a transaction.
0085: */
0086: public class Store {
0087: private final static Logger log = Log.open(Store.class);
0088: private final static L10N L = new L10N(Store.class);
0089:
0090: public final static int BLOCK_BITS = 16;
0091: public final static int BLOCK_SIZE = 1 << BLOCK_BITS;
0092: public final static long BLOCK_INDEX_MASK = BLOCK_SIZE - 1;
0093: public final static long BLOCK_MASK = ~BLOCK_INDEX_MASK;
0094: public final static long BLOCK_OFFSET_MASK = BLOCK_SIZE - 1;
0095:
0096: private final static int ALLOC_BYTES_PER_BLOCK = 2;
0097:
0098: private final static int ALLOC_CHUNK_SIZE = 1024 * ALLOC_BYTES_PER_BLOCK;
0099:
0100: public final static int ALLOC_FREE = 0x00;
0101: public final static int ALLOC_ROW = 0x01;
0102: public final static int ALLOC_USED = 0x02;
0103: public final static int ALLOC_FRAGMENT = 0x03;
0104: public final static int ALLOC_INDEX = 0x04;
0105: public final static int ALLOC_MINI_FRAG = 0x05;
0106: public final static int ALLOC_MASK = 0x0f;
0107:
0108: public final static int FRAGMENT_SIZE = 8 * 1024;
0109: public final static int FRAGMENT_PER_BLOCK = (int) (BLOCK_SIZE / FRAGMENT_SIZE);
0110:
0111: public final static int MINI_FRAG_SIZE = 256;
0112: public final static int MINI_FRAG_PER_BLOCK = (int) ((BLOCK_SIZE - 64) / MINI_FRAG_SIZE);
0113: public final static int MINI_FRAG_ALLOC_OFFSET = MINI_FRAG_PER_BLOCK
0114: * MINI_FRAG_SIZE;
0115:
0116: public final static long DATA_START = BLOCK_SIZE;
0117:
0118: public final static int STORE_CREATE_END = 1024;
0119:
0120: protected final Database _database;
0121: protected final BlockManager _blockManager;
0122:
0123: private final String _name;
0124:
0125: private int _id;
0126:
0127: private Path _path;
0128:
0129: // If true, dirty blocks are written at the end of the transaction.
0130: // Otherwise, they are buffered
0131: private boolean _isFlushDirtyBlocksOnCommit = true;
0132:
0133: private long _fileSize;
0134: private long _blockCount;
0135:
0136: private final Object _allocationLock = new Object();
0137: private byte[] _allocationTable;
0138:
0139: private final Object _allocationWriteLock = new Object();
0140: private int _allocDirtyMin = Integer.MAX_VALUE;
0141: private int _allocDirtyMax;
0142:
0143: private final Object _fragmentLock = new Object();
0144: private final Object _miniFragLock = new Object();
0145:
0146: private final Object _statLock = new Object();
0147: // number of fragments currently used
0148: private long _fragmentUseCount;
0149:
0150: // number of minifragments currently used
0151: private long _miniFragmentUseCount;
0152:
0153: private Object _fileLock = new Object();
0154: private SoftReference<RandomAccessWrapper> _cachedRowFile;
0155:
0156: private Lock _rowLock;
0157:
0158: private final Lifecycle _lifecycle = new Lifecycle();
0159:
0160: public Store(Database database, String name, Lock tableLock) {
0161: this (database, name, tableLock, database.getPath().lookup(
0162: name + ".db"));
0163: }
0164:
0165: /**
0166: * Creates a new store.
0167: *
0168: * @param database the owning database.
0169: * @param name the store name
0170: * @param lock the table lock
0171: * @param path the path to the files
0172: */
0173: public Store(Database database, String name, Lock rowLock, Path path) {
0174: _database = database;
0175: _blockManager = _database.getBlockManager();
0176:
0177: _name = name;
0178: _path = path;
0179:
0180: if (path == null)
0181: throw new NullPointerException();
0182:
0183: _id = _blockManager.allocateStoreId();
0184:
0185: if (rowLock == null)
0186: rowLock = new Lock("row-lock:" + _name + ":" + _id);
0187:
0188: _rowLock = rowLock;
0189: }
0190:
0191: /**
0192: * Creates an independent store.
0193: */
0194: public static Store create(Path path) throws IOException,
0195: SQLException {
0196: Database db = new Database();
0197: db.init();
0198:
0199: Store store = new Store(db, "temp", null, path);
0200:
0201: if (path.canRead())
0202: store.init();
0203: else
0204: store.create();
0205:
0206: return store;
0207: }
0208:
0209: /**
0210: * If true, dirty blocks are written at commit time.
0211: */
0212: public void setFlushDirtyBlocksOnCommit(boolean flushOnCommit) {
0213: _isFlushDirtyBlocksOnCommit = flushOnCommit;
0214: }
0215:
0216: /**
0217: * If true, dirty blocks are written at commit time.
0218: */
0219: public boolean isFlushDirtyBlocksOnCommit() {
0220: return _isFlushDirtyBlocksOnCommit;
0221: }
0222:
0223: /**
0224: * Returns the store's name.
0225: */
0226: public String getName() {
0227: return _name;
0228: }
0229:
0230: /**
0231: * Returns the store's id.
0232: */
0233: public int getId() {
0234: return _id;
0235: }
0236:
0237: /**
0238: * Returns the table's lock.
0239: */
0240: public Lock getLock() {
0241: return _rowLock;
0242: }
0243:
0244: /**
0245: * Returns the block manager.
0246: */
0247: public BlockManager getBlockManager() {
0248: return _blockManager;
0249: }
0250:
0251: /**
0252: * Returns the file size.
0253: */
0254: public long getFileSize() {
0255: return _fileSize;
0256: }
0257:
0258: /**
0259: * Returns the block count.
0260: */
0261: public long getBlockCount() {
0262: return _blockCount;
0263: }
0264:
0265: /**
0266: * Converts from the block index to the address for database
0267: * storage.
0268: */
0269: private static long blockIndexToAddr(long blockIndex) {
0270: return blockIndex << BLOCK_BITS;
0271: }
0272:
0273: /**
0274: * Converts from the block index to the unique block id.
0275: */
0276: private final long blockIndexToBlockId(long blockIndex) {
0277: return (blockIndex << BLOCK_BITS) + _id;
0278: }
0279:
0280: /**
0281: * Converts from the block index to the address for database
0282: * storage.
0283: */
0284: private static long blockIdToIndex(long blockId) {
0285: return blockId >> BLOCK_BITS;
0286: }
0287:
0288: /**
0289: * Converts from the block index to the unique block id.
0290: */
0291: public final long addressToBlockId(long address) {
0292: return (address & BLOCK_MASK) + _id;
0293: }
0294:
0295: /**
0296: * Converts from the block index to the unique block id.
0297: */
0298: public static long blockIdToAddress(long blockId) {
0299: return (blockId & BLOCK_MASK);
0300: }
0301:
0302: /**
0303: * Converts from the block index to the unique block id.
0304: */
0305: public static long blockIdToAddress(long blockId, int offset) {
0306: return (blockId & BLOCK_MASK) + offset;
0307: }
0308:
0309: /**
0310: * Returns the current number of fragments used.
0311: */
0312: public long getTotalFragmentSize() {
0313: return _fragmentUseCount * FRAGMENT_SIZE;
0314: }
0315:
0316: /**
0317: * Creates the store.
0318: */
0319: public void create() throws IOException, SQLException {
0320: if (!_lifecycle.toActive())
0321: return;
0322:
0323: log.finer(this + " create");
0324:
0325: _path.getParent().mkdirs();
0326:
0327: if (_path.exists())
0328: throw new SQLException(
0329: L
0330: .l(
0331: "Table '{0}' already exists. CREATE can not override an existing table.",
0332: _name));
0333:
0334: _allocationTable = new byte[ALLOC_CHUNK_SIZE];
0335:
0336: // allocates the allocation table itself
0337: setAllocation(0, ALLOC_USED);
0338: // allocates the header information
0339: setAllocation(1, ALLOC_USED);
0340:
0341: byte[] buffer = new byte[BLOCK_SIZE];
0342: writeBlock(0, buffer, 0, BLOCK_SIZE);
0343: writeBlock(BLOCK_SIZE, buffer, 0, BLOCK_SIZE);
0344:
0345: writeBlock(0, _allocationTable, 0, _allocationTable.length);
0346:
0347: _blockCount = 2;
0348: }
0349:
0350: public void init() throws IOException {
0351: if (!_lifecycle.toActive())
0352: return;
0353:
0354: log.finer(this + " init");
0355:
0356: RandomAccessWrapper wrapper = openRowFile();
0357:
0358: try {
0359: RandomAccessStream file = wrapper.getFile();
0360:
0361: _fileSize = file.getLength();
0362: _blockCount = ((_fileSize + BLOCK_SIZE - 1) / BLOCK_SIZE);
0363:
0364: int allocCount = (int) (_blockCount * ALLOC_BYTES_PER_BLOCK);
0365:
0366: allocCount += ALLOC_CHUNK_SIZE - allocCount
0367: % ALLOC_CHUNK_SIZE;
0368:
0369: _allocationTable = new byte[allocCount];
0370:
0371: for (int i = 0; i < allocCount; i += BLOCK_SIZE) {
0372: int len = allocCount - i;
0373:
0374: if (BLOCK_SIZE < len)
0375: len = BLOCK_SIZE;
0376:
0377: readBlock(
0378: (long) i / ALLOC_BYTES_PER_BLOCK * BLOCK_SIZE,
0379: _allocationTable, i, len);
0380: }
0381: } finally {
0382: wrapper.close();
0383: }
0384: }
0385:
0386: public void remove() throws SQLException {
0387: try {
0388: _path.remove();
0389: } catch (IOException e) {
0390: throw new SQLExceptionWrapper(e);
0391: }
0392: }
0393:
0394: /**
0395: * Returns the first block id which contains a row.
0396: *
0397: * @return the block id of the first row block
0398: */
0399: public long firstRow(long blockId) throws IOException {
0400: return firstBlock(blockId, ALLOC_ROW);
0401: }
0402:
0403: /**
0404: * Returns the first block id which contains a fragment.
0405: *
0406: * @return the block id of the first row block
0407: */
0408: public long firstFragment(long blockId) throws IOException {
0409: return firstBlock(blockId, ALLOC_FRAGMENT);
0410: }
0411:
0412: /**
0413: * Returns the first block id which contains a row.
0414: *
0415: * @return the block id of the first row block
0416: */
0417: public long firstBlock(long blockId, int type) throws IOException {
0418: if (blockId <= BLOCK_SIZE)
0419: blockId = BLOCK_SIZE;
0420:
0421: long blockIndex = blockId >> BLOCK_BITS;
0422:
0423: synchronized (_allocationLock) {
0424: for (; blockIndex < _blockCount; blockIndex++) {
0425: if (getAllocation(blockIndex) == type)
0426: return blockIndexToBlockId(blockIndex);
0427: }
0428: }
0429:
0430: return -1;
0431: }
0432:
0433: /**
0434: * Returns the matching block.
0435: */
0436: public final Block readBlock(long blockAddress) throws IOException {
0437: long blockId = addressToBlockId(blockAddress);
0438:
0439: Block block = _blockManager.getBlock(this , blockId);
0440:
0441: try {
0442: block.read();
0443:
0444: return block;
0445: } catch (IOException e) {
0446: block.free();
0447:
0448: throw e;
0449: } catch (RuntimeException e) {
0450: block.free();
0451:
0452: throw e;
0453: }
0454: }
0455:
0456: /**
0457: * Allocates a new block for a row.
0458: *
0459: * @return the block id of the allocated block.
0460: */
0461: public Block allocateRow() throws IOException {
0462: return allocateBlock(ALLOC_ROW);
0463: }
0464:
0465: /**
0466: * Return true if the block is a row block.
0467: */
0468: public boolean isRowBlock(long blockAddress) {
0469: return getAllocation(blockAddress / BLOCK_SIZE) == ALLOC_ROW;
0470: }
0471:
0472: /**
0473: * Allocates a new block for a non-row.
0474: *
0475: * @return the block id of the allocated block.
0476: */
0477: public Block allocateBlock() throws IOException {
0478: return allocateBlock(ALLOC_USED);
0479: }
0480:
0481: /**
0482: * Allocates a new block for a fragment
0483: *
0484: * @return the block id of the allocated block.
0485: */
0486: private Block allocateFragmentBlock() throws IOException {
0487: return allocateBlock(ALLOC_FRAGMENT);
0488: }
0489:
0490: /**
0491: * Allocates a new block for a mini-fragment
0492: *
0493: * @return the block id of the allocated block.
0494: */
0495: private Block allocateMiniFragmentBlock() throws IOException {
0496: return allocateBlock(ALLOC_MINI_FRAG);
0497: }
0498:
0499: /**
0500: * Allocates a new block for an index
0501: *
0502: * @return the block id of the allocated block.
0503: */
0504: public Block allocateIndexBlock() throws IOException {
0505: return allocateBlock(ALLOC_INDEX);
0506: }
0507:
0508: /**
0509: * Return true if the block is an index block.
0510: */
0511: public boolean isIndexBlock(long blockAddress) {
0512: return getAllocation(blockAddress / BLOCK_SIZE) == ALLOC_INDEX;
0513: }
0514:
0515: /**
0516: * Allocates a new block.
0517: *
0518: * @return the block id of the allocated block.
0519: */
0520: private Block allocateBlock(int code) throws IOException {
0521: long blockIndex;
0522: boolean isFileExtended = false;
0523:
0524: synchronized (_allocationLock) {
0525: long end = _blockCount;
0526:
0527: if (_allocationTable.length < ALLOC_BYTES_PER_BLOCK * end)
0528: end = _allocationTable.length / ALLOC_BYTES_PER_BLOCK;
0529:
0530: for (blockIndex = 0; blockIndex < end; blockIndex++) {
0531: if (getAllocation(blockIndex) == ALLOC_FREE)
0532: break;
0533: }
0534:
0535: if (_allocationTable.length <= ALLOC_BYTES_PER_BLOCK
0536: * blockIndex) {
0537: // expand the allocation table
0538: byte[] newTable = new byte[_allocationTable.length
0539: + ALLOC_CHUNK_SIZE];
0540: System.arraycopy(_allocationTable, 0, newTable, 0,
0541: _allocationTable.length);
0542: _allocationTable = newTable;
0543:
0544: // if the allocation table is over 32k, allocate the block for the
0545: // extension (each allocation block of 32k allocates 2G)
0546: if (blockIndex % (BLOCK_SIZE / ALLOC_BYTES_PER_BLOCK) == 0) {
0547: setAllocation(blockIndex, ALLOC_USED);
0548: blockIndex++;
0549: }
0550: }
0551:
0552: // mark USED before actual code so it's properly initialized
0553: setAllocation(blockIndex, ALLOC_USED);
0554:
0555: if (log.isLoggable(Level.FINE))
0556: log.fine(this + " allocating block " + blockIndex + " "
0557: + codeToName(code));
0558:
0559: if (_blockCount <= blockIndex) {
0560: isFileExtended = true;
0561: _blockCount = blockIndex + 1;
0562: }
0563: }
0564:
0565: long blockId = blockIndexToBlockId(blockIndex);
0566:
0567: Block block = _blockManager.getBlock(this , blockId);
0568:
0569: byte[] buffer = block.getBuffer();
0570:
0571: for (int i = BLOCK_SIZE - 1; i >= 0; i--)
0572: buffer[i] = 0;
0573:
0574: block.setDirty(0, BLOCK_SIZE);
0575:
0576: // if extending file, write the contents now
0577: if (isFileExtended) {
0578: try {
0579: block.write();
0580: } catch (IOException e) {
0581: log.log(Level.WARNING, e.toString(), e);
0582: }
0583: }
0584:
0585: synchronized (_allocationLock) {
0586: setAllocation(blockIndex, code);
0587: }
0588:
0589: saveAllocation();
0590:
0591: return block;
0592: }
0593:
0594: /**
0595: * Check that an allocated block is valid.
0596: */
0597: protected void validateBlockId(long blockId)
0598: throws IllegalArgumentException, IllegalStateException {
0599: RuntimeException e = null;
0600:
0601: if (isClosed())
0602: e = new IllegalStateException(L.l("store {0} is closing.",
0603: this ));
0604: else if (getId() <= 0)
0605: e = new IllegalStateException(L.l("invalid store {0}.",
0606: this ));
0607: else if (getId() != (blockId & BLOCK_INDEX_MASK)) {
0608: e = new IllegalArgumentException(L.l(
0609: "block {0} must match store {1}.", blockId
0610: & BLOCK_INDEX_MASK, this ));
0611: }
0612:
0613: if (e != null)
0614: throw e;
0615: }
0616:
0617: /**
0618: * Check that an allocated block is valid.
0619: */
0620: protected void assertStoreActive() throws IllegalStateException {
0621: RuntimeException e = null;
0622:
0623: if (isClosed())
0624: e = new IllegalStateException(L.l("store {0} is closing.",
0625: this ));
0626: else if (getId() <= 0)
0627: e = new IllegalStateException(L.l("invalid store {0}.",
0628: this ));
0629:
0630: if (e != null)
0631: throw e;
0632: }
0633:
0634: /**
0635: * Frees a block.
0636: *
0637: * @return the block id of the allocated block.
0638: */
0639: protected void freeBlock(long blockId) throws IOException {
0640: if (blockId == 0)
0641: return;
0642:
0643: synchronized (_allocationLock) {
0644: setAllocation(blockIdToIndex(blockId), ALLOC_FREE);
0645: }
0646:
0647: saveAllocation();
0648: }
0649:
0650: /**
0651: * Sets the allocation for a block.
0652: */
0653: private final int getAllocation(long blockIndex) {
0654: int allocOffset = (int) (ALLOC_BYTES_PER_BLOCK * blockIndex);
0655:
0656: return _allocationTable[allocOffset] & ALLOC_MASK;
0657: }
0658:
0659: /**
0660: * Sets the allocation for a block.
0661: */
0662: private void setAllocation(long blockIndex, int code) {
0663: int allocOffset = (int) (ALLOC_BYTES_PER_BLOCK * blockIndex);
0664:
0665: for (int i = 1; i < ALLOC_BYTES_PER_BLOCK; i++)
0666: _allocationTable[allocOffset + i] = 0;
0667:
0668: _allocationTable[allocOffset] = (byte) code;
0669:
0670: setAllocDirty(allocOffset, allocOffset + ALLOC_BYTES_PER_BLOCK);
0671: }
0672:
0673: /**
0674: * Sets the dirty range for the allocation table.
0675: */
0676: private void setAllocDirty(int min, int max) {
0677: if (min < _allocDirtyMin)
0678: _allocDirtyMin = min;
0679:
0680: if (_allocDirtyMax < max)
0681: _allocDirtyMax = max;
0682: }
0683:
0684: /**
0685: * Sets the allocation for a block.
0686: */
0687: void saveAllocation() throws IOException {
0688: // cache doesn't actually need to write this data
0689: if (!_isFlushDirtyBlocksOnCommit)
0690: return;
0691:
0692: synchronized (_allocationWriteLock) {
0693: int dirtyMin;
0694: int dirtyMax;
0695:
0696: synchronized (_allocationLock) {
0697: dirtyMin = _allocDirtyMin;
0698: _allocDirtyMin = Integer.MAX_VALUE;
0699:
0700: dirtyMax = _allocDirtyMax;
0701: _allocDirtyMax = 0;
0702: }
0703:
0704: // Write each dirty block to disk. The physical blocks are
0705: // broken up each BLOCK_SIZE / ALLOC_BYTES_PER_BLOCK.
0706: for (; dirtyMin < dirtyMax; dirtyMin = (dirtyMin + BLOCK_SIZE)
0707: - dirtyMin % BLOCK_SIZE) {
0708: int block = dirtyMin
0709: / (BLOCK_SIZE / ALLOC_BYTES_PER_BLOCK);
0710:
0711: int offset = dirtyMin % BLOCK_SIZE;
0712: int length;
0713:
0714: if (dirtyMin / BLOCK_SIZE != dirtyMax / BLOCK_SIZE)
0715: length = BLOCK_SIZE - offset;
0716: else
0717: length = dirtyMax - dirtyMin;
0718:
0719: writeBlock((long) block * BLOCK_SIZE + offset,
0720: _allocationTable, offset, length);
0721: }
0722: }
0723: }
0724:
0725: /**
0726: * Reads a fragment.
0727: *
0728: * @param fragmentAddress the address of the fragment
0729: * @param fragmentOffset the offset inside the fragment to start reading
0730: * @param buffer the result buffer
0731: * @param offset offset into the result buffer
0732: * @param length the number of bytes to read
0733: *
0734: * @return the number of bytes read
0735: */
0736: public int readFragment(long fragmentAddress, int fragmentOffset,
0737: byte[] buffer, int offset, int length) throws IOException {
0738: if (FRAGMENT_SIZE - fragmentOffset < length) {
0739: // server/13df
0740: throw new IllegalArgumentException(L.l(
0741: "read offset {0} length {1} too long",
0742: fragmentOffset, length));
0743: }
0744:
0745: Block block = readBlock(addressToBlockId(fragmentAddress));
0746:
0747: try {
0748: int blockOffset = getFragmentOffset(fragmentAddress);
0749:
0750: byte[] blockBuffer = block.getBuffer();
0751:
0752: synchronized (blockBuffer) {
0753: System.arraycopy(blockBuffer, blockOffset
0754: + fragmentOffset, buffer, offset, length);
0755: }
0756:
0757: return length;
0758: } finally {
0759: block.free();
0760: }
0761: }
0762:
0763: /**
0764: * Reads a fragment for a clob.
0765: *
0766: * @param fragmentAddress the address of the fragment
0767: * @param fragmentOffset the offset inside the fragment to start reading
0768: * @param buffer the result buffer
0769: * @param offset offset into the result buffer
0770: * @param length the length of the fragment in characters
0771: *
0772: * @return the number of characters read
0773: */
0774: public int readFragment(long fragmentAddress, int fragmentOffset,
0775: char[] buffer, int offset, int length) throws IOException {
0776: if (FRAGMENT_SIZE - fragmentOffset < 2 * length) {
0777: // server/13df
0778: throw new IllegalArgumentException(L.l(
0779: "read offset {0} length {1} too long",
0780: fragmentOffset, length));
0781: }
0782:
0783: Block block = readBlock(addressToBlockId(fragmentAddress));
0784:
0785: try {
0786: int blockOffset = getFragmentOffset(fragmentAddress);
0787: blockOffset += fragmentOffset;
0788:
0789: byte[] blockBuffer = block.getBuffer();
0790:
0791: synchronized (blockBuffer) {
0792: for (int i = 0; i < length; i++) {
0793: int ch1 = blockBuffer[blockOffset] & 0xff;
0794: int ch2 = blockBuffer[blockOffset + 1] & 0xff;
0795:
0796: buffer[offset + i] = (char) ((ch1 << 8) + ch2);
0797:
0798: blockOffset += 2;
0799: }
0800: }
0801:
0802: return length;
0803: } finally {
0804: block.free();
0805: }
0806: }
0807:
0808: /**
0809: * Reads a long value from a fragment.
0810: *
0811: * @return the long value
0812: */
0813: public long readFragmentLong(long fragmentAddress,
0814: int fragmentOffset) throws IOException {
0815: Block block = readBlock(addressToBlockId(fragmentAddress));
0816:
0817: try {
0818: int blockOffset = getFragmentOffset(fragmentAddress);
0819:
0820: byte[] blockBuffer = block.getBuffer();
0821:
0822: synchronized (blockBuffer) {
0823: return readLong(blockBuffer, blockOffset
0824: + fragmentOffset);
0825: }
0826: } finally {
0827: block.free();
0828: }
0829: }
0830:
0831: /**
0832: * Reads a block.
0833: *
0834: * @param blockAddress the address of the block
0835: * @param blockOffset the offset inside the block to start reading
0836: * @param buffer the result buffer
0837: * @param offset offset into the result buffer
0838: * @param length the number of bytes to read
0839: *
0840: * @return the number of bytes read
0841: */
0842: public int readBlock(long blockAddress, int blockOffset,
0843: byte[] buffer, int offset, int length) throws IOException {
0844: if (BLOCK_SIZE - blockOffset < length) {
0845: // server/13df
0846: throw new IllegalArgumentException(L.l(
0847: "read offset {0} length {1} too long", blockOffset,
0848: length));
0849: }
0850:
0851: Block block = readBlock(addressToBlockId(blockAddress));
0852:
0853: try {
0854: byte[] blockBuffer = block.getBuffer();
0855:
0856: synchronized (blockBuffer) {
0857: System.arraycopy(blockBuffer, blockOffset, buffer,
0858: offset, length);
0859: }
0860:
0861: return length;
0862: } finally {
0863: block.free();
0864: }
0865: }
0866:
0867: /**
0868: * Reads a block for a clob.
0869: *
0870: * @param blockAddress the address of the block
0871: * @param blockOffset the offset inside the block to start reading
0872: * @param buffer the result buffer
0873: * @param offset offset into the result buffer
0874: * @param length the length of the block in characters
0875: *
0876: * @return the number of characters read
0877: */
0878: public int readBlock(long blockAddress, int blockOffset,
0879: char[] buffer, int offset, int length) throws IOException {
0880: if (BLOCK_SIZE - blockOffset < 2 * length) {
0881: // server/13df
0882: throw new IllegalArgumentException(L.l(
0883: "read offset {0} length {1} too long", blockOffset,
0884: length));
0885: }
0886:
0887: Block block = readBlock(addressToBlockId(blockAddress));
0888:
0889: try {
0890: byte[] blockBuffer = block.getBuffer();
0891:
0892: synchronized (blockBuffer) {
0893: for (int i = 0; i < length; i++) {
0894: int ch1 = blockBuffer[blockOffset] & 0xff;
0895: int ch2 = blockBuffer[blockOffset + 1] & 0xff;
0896:
0897: buffer[offset + i] = (char) ((ch1 << 8) + ch2);
0898:
0899: blockOffset += 2;
0900: }
0901: }
0902:
0903: return length;
0904: } finally {
0905: block.free();
0906: }
0907: }
0908:
0909: /**
0910: * Reads a long value from a block.
0911: *
0912: * @return the long value
0913: */
0914: public long readBlockLong(long blockAddress, int offset)
0915: throws IOException {
0916: Block block = readBlock(addressToBlockId(blockAddress));
0917:
0918: try {
0919: byte[] blockBuffer = block.getBuffer();
0920:
0921: synchronized (blockBuffer) {
0922: return readLong(blockBuffer, offset);
0923: }
0924: } finally {
0925: block.free();
0926: }
0927: }
0928:
0929: /**
0930: * Allocates a new fragment.
0931: *
0932: * @return the fragment address
0933: */
0934: public long allocateFragment(StoreTransaction xa)
0935: throws IOException {
0936: while (true) {
0937: synchronized (_allocationLock) {
0938: byte[] allocationTable = _allocationTable;
0939:
0940: for (int i = 0; i < allocationTable.length; i += ALLOC_BYTES_PER_BLOCK) {
0941: int fragMask = allocationTable[i + 1] & 0xff;
0942:
0943: if (allocationTable[i] == ALLOC_FRAGMENT
0944: && fragMask != 0xff) {
0945: for (int j = 0; j < FRAGMENT_PER_BLOCK; j++) {
0946: if ((fragMask & (1 << j)) == 0) {
0947: allocationTable[i + 1] = (byte) (fragMask | (1 << j));
0948:
0949: setAllocDirty(i + 1, i + 2);
0950:
0951: _fragmentUseCount++;
0952:
0953: long fragmentAddress = BLOCK_SIZE
0954: * ((long) i / ALLOC_BYTES_PER_BLOCK)
0955: + j;
0956:
0957: //System.out.println((fragmentAddress / BLOCK_SIZE) + ":" + j + " ALLOCATE");
0958: return fragmentAddress;
0959: }
0960: }
0961: }
0962: }
0963: }
0964:
0965: // if no fragment, allocate a new one.
0966:
0967: Block block = allocateFragmentBlock();
0968: block.free();
0969: }
0970: }
0971:
0972: /**
0973: * Deletes a fragment.
0974: */
0975: public void deleteFragment(StoreTransaction xa, long fragmentAddress)
0976: throws IOException {
0977: synchronized (_allocationLock) {
0978: int i = (int) (ALLOC_BYTES_PER_BLOCK * (fragmentAddress / BLOCK_SIZE));
0979: int j = (int) (fragmentAddress & 0xff);
0980:
0981: int fragMask = _allocationTable[i + 1] & 0xff;
0982: //System.out.println((fragmentAddress / BLOCK_SIZE) + ":" + j + " DELETE");
0983:
0984: if (_allocationTable[i] != ALLOC_FRAGMENT)
0985: System.out.println("BAD ENTRY: " + fragMask);
0986:
0987: if (j >= 8)
0988: System.out.println("BAD J: " + fragMask);
0989:
0990: if ((fragMask & (1 << j)) == 0) {
0991: log.fine("BAD J-MASK: " + fragMask + " " + j);
0992: }
0993:
0994: _allocationTable[i + 1] = (byte) (fragMask & ~(1 << j));
0995:
0996: _fragmentUseCount--;
0997:
0998: setAllocDirty(i + 1, i + 2);
0999: }
1000: }
1001:
1002: /**
1003: * Writes a fragment.
1004: *
1005: * @param xa the owning transaction
1006: * @param fragmentAddress the fragment to write
1007: * @param fragmentOffset the offset into the fragment
1008: * @param buffer the write buffer
1009: * @param offset offset into the write buffer
1010: * @param length the number of bytes to write
1011: *
1012: * @return the fragment id
1013: */
1014: public void writeFragment(StoreTransaction xa,
1015: long fragmentAddress, int fragmentOffset, byte[] buffer,
1016: int offset, int length) throws IOException {
1017: if (FRAGMENT_SIZE - fragmentOffset < length)
1018: throw new IllegalArgumentException(L.l(
1019: "write offset {0} length {1} too long",
1020: fragmentOffset, length));
1021:
1022: Block block = xa.readBlock(this ,
1023: addressToBlockId(fragmentAddress));
1024:
1025: try {
1026: xa.addUpdateFragmentBlock(block);
1027:
1028: int blockOffset = getFragmentOffset(fragmentAddress);
1029:
1030: byte[] blockBuffer = block.getBuffer();
1031:
1032: blockOffset += fragmentOffset;
1033:
1034: synchronized (blockBuffer) {
1035: System.arraycopy(buffer, offset, blockBuffer,
1036: blockOffset, length);
1037:
1038: block.setDirty(blockOffset, blockOffset + length);
1039: }
1040: } finally {
1041: block.free();
1042: }
1043: }
1044:
1045: /**
1046: * Writes a character based
1047: *
1048: * @param fragmentAddress the fragment to write
1049: * @param fragmentOffset the offset into the fragment
1050: * @param buffer the write buffer
1051: * @param offset offset into the write buffer
1052: * @param length the number of bytes to write
1053: */
1054: public void writeFragment(StoreTransaction xa,
1055: long fragmentAddress, int fragmentOffset, char[] buffer,
1056: int offset, int length) throws IOException {
1057: if (FRAGMENT_SIZE - fragmentOffset < length)
1058: throw new IllegalArgumentException(L.l(
1059: "write offset {0} length {1} too long",
1060: fragmentOffset, length));
1061:
1062: Block block = xa.readBlock(this ,
1063: addressToBlockId(fragmentAddress));
1064:
1065: try {
1066: block = xa.createAutoCommitWriteBlock(block);
1067:
1068: int blockOffset = getFragmentOffset(fragmentAddress);
1069:
1070: byte[] blockBuffer = block.getBuffer();
1071:
1072: blockOffset += fragmentOffset;
1073:
1074: synchronized (blockBuffer) {
1075: int blockTail = blockOffset;
1076:
1077: for (int i = 0; i < length; i++) {
1078: char ch = buffer[offset + i];
1079:
1080: blockBuffer[blockTail] = (byte) (ch >> 8);
1081: blockBuffer[blockTail + 1] = (byte) (ch);
1082:
1083: blockTail += 2;
1084: }
1085:
1086: block.setDirty(blockOffset, blockTail);
1087: }
1088: } finally {
1089: block.free();
1090: }
1091: }
1092:
1093: /**
1094: * Writes a long value to a fragment.
1095: *
1096: * @return the long value
1097: */
1098: public void writeFragmentLong(StoreTransaction xa,
1099: long fragmentAddress, int fragmentOffset, long value)
1100: throws IOException {
1101: Block block = xa.readBlock(this ,
1102: addressToBlockId(fragmentAddress));
1103:
1104: try {
1105: xa.addUpdateBlock(block);
1106:
1107: int blockOffset = getFragmentOffset(fragmentAddress);
1108:
1109: byte[] blockBuffer = block.getBuffer();
1110: int offset = blockOffset + fragmentOffset;
1111:
1112: synchronized (blockBuffer) {
1113: writeLong(blockBuffer, offset, value);
1114:
1115: block.setDirty(offset, offset + 8);
1116: }
1117: } finally {
1118: block.free();
1119: }
1120: }
1121:
1122: /**
1123: * Writes a blockfragment.
1124: *
1125: * @param xa the owning transaction
1126: * @param blockAddress the block to write
1127: * @param blockOffset the offset into the block
1128: * @param buffer the write buffer
1129: * @param offset offset into the write buffer
1130: * @param length the number of bytes to write
1131: *
1132: * @return the fragment id
1133: */
1134: public void writeBlock(StoreTransaction xa, long blockAddress,
1135: int blockOffset, byte[] buffer, int offset, int length)
1136: throws IOException {
1137: if (BLOCK_SIZE - blockOffset < length)
1138: throw new IllegalArgumentException(L.l(
1139: "write offset {0} length {1} too long",
1140: blockOffset, length));
1141:
1142: Block block = xa
1143: .readBlock(this , addressToBlockId(blockAddress));
1144:
1145: try {
1146: xa.addUpdateBlock(block);
1147:
1148: byte[] blockBuffer = block.getBuffer();
1149:
1150: synchronized (blockBuffer) {
1151: System.arraycopy(buffer, offset, blockBuffer,
1152: blockOffset, length);
1153:
1154: block.setDirty(blockOffset, blockOffset + length);
1155: }
1156: } finally {
1157: block.free();
1158: }
1159: }
1160:
1161: /**
1162: * Writes a character based block
1163: *
1164: * @param blockAddress the fragment to write
1165: * @param blockOffset the offset into the fragment
1166: * @param buffer the write buffer
1167: * @param offset offset into the write buffer
1168: * @param length the number of bytes to write
1169: */
1170: public void writeBlock(StoreTransaction xa, long blockAddress,
1171: int blockOffset, char[] buffer, int offset, int length)
1172: throws IOException {
1173: if (BLOCK_SIZE - blockOffset < length)
1174: throw new IllegalArgumentException(L.l(
1175: "write offset {0} length {1} too long",
1176: blockOffset, length));
1177:
1178: Block block = xa
1179: .readBlock(this , addressToBlockId(blockAddress));
1180:
1181: try {
1182: block = xa.createAutoCommitWriteBlock(block);
1183:
1184: byte[] blockBuffer = block.getBuffer();
1185:
1186: synchronized (blockBuffer) {
1187: int blockTail = blockOffset;
1188:
1189: for (int i = 0; i < length; i++) {
1190: char ch = buffer[offset + i];
1191:
1192: blockBuffer[blockTail] = (byte) (ch >> 8);
1193: blockBuffer[blockTail + 1] = (byte) (ch);
1194:
1195: blockTail += 2;
1196: }
1197:
1198: block.setDirty(blockOffset, blockTail);
1199: }
1200: } finally {
1201: block.free();
1202: }
1203: }
1204:
1205: /**
1206: * Writes a long value to a block
1207: *
1208: * @return the long value
1209: */
1210: public void writeBlockLong(StoreTransaction xa, long blockAddress,
1211: int offset, long value) throws IOException {
1212: Block block = xa
1213: .readBlock(this , addressToBlockId(blockAddress));
1214:
1215: try {
1216: xa.addUpdateBlock(block);
1217:
1218: byte[] blockBuffer = block.getBuffer();
1219:
1220: synchronized (blockBuffer) {
1221: writeLong(blockBuffer, offset, value);
1222:
1223: block.setDirty(offset, offset + 8);
1224: }
1225: } finally {
1226: block.free();
1227: }
1228: }
1229:
1230: /**
1231: * Returns the fragment offset for an id.
1232: */
1233: private int getFragmentOffset(long fragmentAddress) {
1234: int id = (int) (fragmentAddress & BLOCK_OFFSET_MASK);
1235:
1236: return (int) (FRAGMENT_SIZE * id);
1237: }
1238:
1239: /**
1240: * Reads a fragment.
1241: *
1242: * @param fragmentAddress the address of the fragment
1243: * @param fragmentOffset the offset inside the fragment to start reading
1244: * @param buffer the result buffer
1245: * @param offset offset into the result buffer
1246: * @param length the number of bytes to read
1247: *
1248: * @return the number of bytes read
1249: */
1250: public int readMiniFragment(long fragmentAddress,
1251: int fragmentOffset, byte[] buffer, int offset, int length)
1252: throws IOException {
1253: if (MINI_FRAG_SIZE - fragmentOffset < length) {
1254: throw new IllegalArgumentException(L.l(
1255: "read offset {0} length {1} too long",
1256: fragmentOffset, length));
1257: }
1258:
1259: Block block = readBlock(addressToBlockId(fragmentAddress));
1260:
1261: try {
1262: int blockOffset = getMiniFragmentOffset(fragmentAddress);
1263:
1264: byte[] blockBuffer = block.getBuffer();
1265:
1266: synchronized (blockBuffer) {
1267: System.arraycopy(blockBuffer, blockOffset
1268: + fragmentOffset, buffer, offset, length);
1269: }
1270:
1271: return length;
1272: } finally {
1273: block.free();
1274: }
1275: }
1276:
1277: /**
1278: * Reads a miniFragment for a clob.
1279: *
1280: * @param fragmentAddress the address of the fragment
1281: * @param fragmentOffset the offset inside the fragment to start reading
1282: * @param buffer the result buffer
1283: * @param offset offset into the result buffer
1284: * @param length the length of the fragment in characters
1285: *
1286: * @return the number of characters read
1287: */
1288: public int readMiniFragment(long fragmentAddress,
1289: int fragmentOffset, char[] buffer, int offset, int length)
1290: throws IOException {
1291: if (MINI_FRAG_SIZE - fragmentOffset < 2 * length) {
1292: throw new IllegalArgumentException(L.l(
1293: "read offset {0} length {1} too long",
1294: fragmentOffset, length));
1295: }
1296:
1297: Block block = readBlock(addressToBlockId(fragmentAddress));
1298:
1299: try {
1300: int blockOffset = getMiniFragmentOffset(fragmentAddress);
1301: blockOffset += fragmentOffset;
1302:
1303: byte[] blockBuffer = block.getBuffer();
1304:
1305: synchronized (blockBuffer) {
1306: for (int i = 0; i < length; i++) {
1307: int ch1 = blockBuffer[blockOffset] & 0xff;
1308: int ch2 = blockBuffer[blockOffset + 1] & 0xff;
1309:
1310: buffer[offset + i] = (char) ((ch1 << 8) + ch2);
1311:
1312: blockOffset += 2;
1313: }
1314: }
1315:
1316: return length;
1317: } finally {
1318: block.free();
1319: }
1320: }
1321:
1322: /**
1323: * Reads a long value from a miniFragment.
1324: *
1325: * @return the long value
1326: */
1327: public long readMiniFragmentLong(long fragmentAddress,
1328: int fragmentOffset) throws IOException {
1329: Block block = readBlock(addressToBlockId(fragmentAddress));
1330:
1331: try {
1332: int blockOffset = getMiniFragmentOffset(fragmentAddress);
1333:
1334: byte[] blockBuffer = block.getBuffer();
1335:
1336: synchronized (blockBuffer) {
1337: return readLong(blockBuffer, blockOffset
1338: + fragmentOffset);
1339: }
1340: } finally {
1341: block.free();
1342: }
1343: }
1344:
1345: /**
1346: * Allocates a new miniFragment.
1347: *
1348: * @return the fragment address
1349: */
1350: public long allocateMiniFragment(StoreTransaction xa)
1351: throws IOException {
1352: while (true) {
1353: long blockAddr = allocateMiniFragmentBlock(xa);
1354:
1355: Block block = readBlock(blockAddr);
1356: int fragOffset = -1;
1357:
1358: try {
1359: byte[] blockBuffer = block.getBuffer();
1360:
1361: synchronized (blockBuffer) {
1362: for (int i = 0; i < MINI_FRAG_PER_BLOCK; i++) {
1363: int offset = i / 8 + MINI_FRAG_ALLOC_OFFSET;
1364: int mask = 1 << (i % 8);
1365:
1366: if ((blockBuffer[offset] & mask) == 0) {
1367: fragOffset = i;
1368: blockBuffer[offset] |= mask;
1369: block.setDirty(offset, offset + 1);
1370: break;
1371: }
1372: }
1373:
1374: // fragment allocated underneath us
1375: if (fragOffset < 0)
1376: continue;
1377:
1378: boolean hasFree = false;
1379: for (int i = 0; i < MINI_FRAG_PER_BLOCK; i++) {
1380: int offset = i / 8 + MINI_FRAG_ALLOC_OFFSET;
1381: int mask = 1 << (i % 8);
1382:
1383: if ((blockBuffer[offset] & mask) == 0) {
1384: hasFree = true;
1385: break;
1386: }
1387: }
1388:
1389: if (hasFree) {
1390: int i = (int) (ALLOC_BYTES_PER_BLOCK * (blockAddr / BLOCK_SIZE));
1391:
1392: synchronized (_allocationLock) {
1393: _allocationTable[i + 1] = 0;
1394: setAllocDirty(i + 1, i + 2);
1395: }
1396: }
1397:
1398: return blockAddr + fragOffset;
1399: }
1400: } finally {
1401: block.free();
1402: }
1403: }
1404: }
1405:
1406: /**
1407: * Allocates a new miniFragment.
1408: *
1409: * @return the fragment address
1410: */
1411: private long allocateMiniFragmentBlock(StoreTransaction xa)
1412: throws IOException {
1413: while (true) {
1414: synchronized (_allocationLock) {
1415: byte[] allocationTable = _allocationTable;
1416:
1417: for (int i = 0; i < allocationTable.length; i += ALLOC_BYTES_PER_BLOCK) {
1418: int fragMask = allocationTable[i + 1] & 0xff;
1419:
1420: if (allocationTable[i] == ALLOC_MINI_FRAG
1421: && fragMask != 0xff) {
1422: allocationTable[i + 1] = (byte) 0xff;
1423:
1424: setAllocDirty(i + 1, i + 2);
1425:
1426: _miniFragmentUseCount++;
1427:
1428: long fragmentAddress = BLOCK_SIZE
1429: * ((long) i / ALLOC_BYTES_PER_BLOCK);
1430:
1431: return fragmentAddress;
1432: }
1433: }
1434: }
1435:
1436: // if no fragment, allocate a new one.
1437:
1438: Block block = allocateMiniFragmentBlock();
1439: block.free();
1440: }
1441: }
1442:
1443: /**
1444: * Deletes a miniFragment.
1445: */
1446: public void deleteMiniFragment(StoreTransaction xa,
1447: long fragmentAddress) throws IOException {
1448: Block block = readBlock(fragmentAddress);
1449:
1450: try {
1451: int fragIndex = (int) (fragmentAddress & BLOCK_OFFSET_MASK);
1452: int offset = fragIndex / 8 + MINI_FRAG_ALLOC_OFFSET;
1453: int mask = 1 << (fragIndex % 8);
1454: byte[] blockBuffer = block.getBuffer();
1455:
1456: synchronized (blockBuffer) {
1457: blockBuffer[offset] &= ~mask;
1458: block.setDirty(offset, offset + 1);
1459:
1460: int i = (int) (ALLOC_BYTES_PER_BLOCK * (fragmentAddress / BLOCK_SIZE));
1461: int j = (int) (fragmentAddress & 0xff);
1462:
1463: synchronized (_allocationLock) {
1464: int fragMask = _allocationTable[i + 1] & 0xff;
1465: //System.out.println((fragmentAddress / BLOCK_SIZE) + ":" + j + " DELETE");
1466:
1467: if (_allocationTable[i] != ALLOC_MINI_FRAG)
1468: System.out.println("BAD ENTRY: " + fragMask);
1469:
1470: _allocationTable[i + 1] = 0;
1471:
1472: _miniFragmentUseCount--;
1473:
1474: setAllocDirty(i + 1, i + 2);
1475: }
1476: }
1477: } finally {
1478: block.free();
1479: }
1480: }
1481:
1482: /**
1483: * Writes a miniFragment.
1484: *
1485: * @param xa the owning transaction
1486: * @param fragmentAddress the fragment to write
1487: * @param fragmentOffset the offset into the fragment
1488: * @param buffer the write buffer
1489: * @param offset offset into the write buffer
1490: * @param length the number of bytes to write
1491: *
1492: * @return the fragment id
1493: */
1494: public void writeMiniFragment(StoreTransaction xa,
1495: long fragmentAddress, int fragmentOffset, byte[] buffer,
1496: int offset, int length) throws IOException {
1497: if (MINI_FRAG_SIZE - fragmentOffset < length)
1498: throw new IllegalArgumentException(L.l(
1499: "write offset {0} length {1} too long",
1500: fragmentOffset, length));
1501:
1502: Block block = xa.readBlock(this ,
1503: addressToBlockId(fragmentAddress));
1504:
1505: try {
1506: xa.addUpdateBlock(block);
1507:
1508: int blockOffset = getMiniFragmentOffset(fragmentAddress);
1509:
1510: byte[] blockBuffer = block.getBuffer();
1511:
1512: blockOffset += fragmentOffset;
1513:
1514: synchronized (blockBuffer) {
1515: System.arraycopy(buffer, offset, blockBuffer,
1516: blockOffset, length);
1517:
1518: block.setDirty(blockOffset, blockOffset + length);
1519: }
1520: } finally {
1521: block.free();
1522: }
1523: }
1524:
1525: /**
1526: * Writes a character based
1527: *
1528: * @param miniFragmentAddress the fragment to write
1529: * @param fragmentOffset the offset into the fragment
1530: * @param buffer the write buffer
1531: * @param offset offset into the write buffer
1532: * @param length the number of bytes to write
1533: */
1534: public void writeMiniFragment(StoreTransaction xa,
1535: long fragmentAddress, int fragmentOffset, char[] buffer,
1536: int offset, int length) throws IOException {
1537: if (MINI_FRAG_SIZE - fragmentOffset < length)
1538: throw new IllegalArgumentException(L.l(
1539: "write offset {0} length {1} too long",
1540: fragmentOffset, length));
1541:
1542: Block block = xa.readBlock(this ,
1543: addressToBlockId(fragmentAddress));
1544:
1545: try {
1546: block = xa.createAutoCommitWriteBlock(block);
1547:
1548: int blockOffset = getMiniFragmentOffset(fragmentAddress);
1549:
1550: byte[] blockBuffer = block.getBuffer();
1551:
1552: blockOffset += fragmentOffset;
1553:
1554: synchronized (blockBuffer) {
1555: int blockTail = blockOffset;
1556:
1557: for (int i = 0; i < length; i++) {
1558: char ch = buffer[offset + i];
1559:
1560: blockBuffer[blockTail] = (byte) (ch >> 8);
1561: blockBuffer[blockTail + 1] = (byte) (ch);
1562:
1563: blockTail += 2;
1564: }
1565:
1566: block.setDirty(blockOffset, blockTail);
1567: }
1568: } finally {
1569: block.free();
1570: }
1571: }
1572:
1573: /**
1574: * Writes a long value to a miniFragment.
1575: *
1576: * @return the long value
1577: */
1578: public void writeMiniFragmentLong(StoreTransaction xa,
1579: long fragmentAddress, int fragmentOffset, long value)
1580: throws IOException {
1581: Block block = xa.readBlock(this ,
1582: addressToBlockId(fragmentAddress));
1583:
1584: try {
1585: xa.addUpdateBlock(block);
1586:
1587: int blockOffset = getMiniFragmentOffset(fragmentAddress);
1588:
1589: byte[] blockBuffer = block.getBuffer();
1590: int offset = blockOffset + fragmentOffset;
1591:
1592: synchronized (blockBuffer) {
1593: writeLong(blockBuffer, offset, value);
1594:
1595: block.setDirty(offset, offset + 8);
1596: }
1597: } finally {
1598: block.free();
1599: }
1600: }
1601:
1602: /**
1603: * Returns the miniFragment offset for an id.
1604: */
1605: private int getMiniFragmentOffset(long fragmentAddress) {
1606: int id = (int) (fragmentAddress & BLOCK_OFFSET_MASK);
1607:
1608: return (int) (MINI_FRAG_SIZE * id);
1609: }
1610:
1611: /**
1612: * Reads a block into the buffer.
1613: */
1614: public void readBlock(long blockId, byte[] buffer, int offset,
1615: int length) throws IOException {
1616: synchronized (_fileLock) {
1617: RandomAccessWrapper wrapper = openRowFile();
1618: RandomAccessStream is = wrapper.getFile();
1619:
1620: long blockAddress = blockId & BLOCK_MASK;
1621:
1622: try {
1623: if (blockAddress < 0
1624: || _fileSize < blockAddress + length) {
1625: throw new IllegalStateException(
1626: L
1627: .l(
1628: "block at {0} is invalid for file {1} (length {2})",
1629: Long
1630: .toHexString(blockAddress),
1631: _path,
1632: Long.toHexString(_fileSize)));
1633: }
1634:
1635: //System.out.println("READ: " + (blockAddress >> 16) + ":" + (blockAddress & 0xffff));
1636: int readLen = is.read(blockAddress, buffer, offset,
1637: length);
1638:
1639: if (readLen < 0) {
1640: for (int i = 0; i < BLOCK_SIZE; i++)
1641: buffer[i] = 0;
1642: }
1643:
1644: freeRowFile(wrapper);
1645: wrapper = null;
1646: } finally {
1647: if (wrapper != null)
1648: wrapper.close();
1649: }
1650: }
1651: }
1652:
1653: /**
1654: * Saves the buffer to the database.
1655: */
1656: public void writeBlock(long blockAddress, byte[] buffer,
1657: int offset, int length) throws IOException {
1658: synchronized (_fileLock) {
1659: RandomAccessWrapper wrapper = openRowFile();
1660: RandomAccessStream os = wrapper.getFile();
1661:
1662: try {
1663: os.write(blockAddress, buffer, offset, length);
1664:
1665: freeRowFile(wrapper);
1666: wrapper = null;
1667:
1668: if (_fileSize < blockAddress + length) {
1669: _fileSize = blockAddress + length;
1670: }
1671:
1672: } finally {
1673: if (wrapper != null)
1674: wrapper.close();
1675: }
1676: }
1677: }
1678:
1679: /**
1680: * Opens the underlying file to the database.
1681: */
1682: private RandomAccessWrapper openRowFile() throws IOException {
1683: RandomAccessStream file = null;
1684: RandomAccessWrapper wrapper = null;
1685:
1686: synchronized (this ) {
1687: SoftReference<RandomAccessWrapper> ref = _cachedRowFile;
1688: _cachedRowFile = null;
1689:
1690: if (ref != null) {
1691: wrapper = ref.get();
1692: }
1693: }
1694:
1695: if (wrapper != null)
1696: file = wrapper.getFile();
1697:
1698: if (file == null) {
1699: file = _path.openRandomAccess();
1700:
1701: wrapper = new RandomAccessWrapper(file);
1702: }
1703:
1704: return wrapper;
1705: }
1706:
1707: private void freeRowFile(RandomAccessWrapper wrapper)
1708: throws IOException {
1709: synchronized (this ) {
1710: if (_cachedRowFile == null) {
1711: _cachedRowFile = new SoftReference<RandomAccessWrapper>(
1712: wrapper);
1713: return;
1714: }
1715: }
1716:
1717: wrapper.close();
1718: }
1719:
1720: /**
1721: * Writes the short.
1722: */
1723: private static void writeShort(byte[] buffer, int offset, int v) {
1724: buffer[offset + 0] = (byte) (v >> 8);
1725: buffer[offset + 1] = (byte) (v);
1726: }
1727:
1728: /**
1729: * Reads a short.
1730: */
1731: private static int readShort(byte[] buffer, int offset) {
1732: return (((buffer[offset + 0] & 0xff) << 8) | ((buffer[offset + 1] & 0xff)));
1733: }
1734:
1735: /**
1736: * Flush the store.
1737: */
1738: public void flush() {
1739: if (_lifecycle.isActive()) {
1740: if (_blockManager != null) {
1741: _blockManager.flush(this );
1742: }
1743: }
1744: }
1745:
1746: /**
1747: * True if destroyed.
1748: */
1749: public boolean isClosed() {
1750: return _lifecycle.isDestroyed();
1751: }
1752:
1753: /**
1754: * Closes the store.
1755: */
1756: public void close() {
1757: if (!_lifecycle.toDestroy())
1758: return;
1759:
1760: log.finer(this + " closing");
1761:
1762: if (_blockManager != null) {
1763: _blockManager.freeStore(this );
1764: _blockManager.freeStoreId(_id);
1765: }
1766:
1767: long id = _id;
1768: _id = 0;
1769:
1770: _path = null;
1771:
1772: RandomAccessWrapper wrapper = null;
1773:
1774: SoftReference<RandomAccessWrapper> ref = _cachedRowFile;
1775: _cachedRowFile = null;
1776:
1777: if (ref != null)
1778: wrapper = ref.get();
1779:
1780: if (wrapper != null) {
1781: try {
1782: wrapper.close();
1783: } catch (Throwable e) {
1784: }
1785: }
1786: }
1787:
1788: // debugging stuff.
1789: /**
1790: * Returns a copy of the allocation table.
1791: */
1792: public byte[] getAllocationTable() {
1793: byte[] table = new byte[_allocationTable.length];
1794:
1795: System.arraycopy(_allocationTable, 0, table, 0, table.length);
1796:
1797: return table;
1798: }
1799:
1800: private static IllegalStateException stateError(String msg) {
1801: IllegalStateException e = new IllegalStateException(msg);
1802: e.fillInStackTrace();
1803: log.log(Level.WARNING, e.toString(), e);
1804: return e;
1805: }
1806:
1807: /**
1808: * Reads the long.
1809: */
1810: public static long readLong(byte[] buffer, int offset) {
1811: return (((buffer[offset + 0] & 0xffL) << 56)
1812: + ((buffer[offset + 1] & 0xffL) << 48)
1813: + ((buffer[offset + 2] & 0xffL) << 40)
1814: + ((buffer[offset + 3] & 0xffL) << 32)
1815: + ((buffer[offset + 4] & 0xffL) << 24)
1816: + ((buffer[offset + 5] & 0xffL) << 16)
1817: + ((buffer[offset + 6] & 0xffL) << 8) + ((buffer[offset + 7] & 0xffL)));
1818: }
1819:
1820: /**
1821: * Writes the long.
1822: */
1823: public static void writeLong(byte[] buffer, int offset, long v) {
1824: buffer[offset + 0] = (byte) (v >> 56);
1825: buffer[offset + 1] = (byte) (v >> 48);
1826: buffer[offset + 2] = (byte) (v >> 40);
1827: buffer[offset + 3] = (byte) (v >> 32);
1828:
1829: buffer[offset + 4] = (byte) (v >> 24);
1830: buffer[offset + 5] = (byte) (v >> 16);
1831: buffer[offset + 6] = (byte) (v >> 8);
1832: buffer[offset + 7] = (byte) (v);
1833: }
1834:
1835: /**
1836: * Debug names for the allocation.
1837: */
1838: public static String codeToName(int code) {
1839: switch (code) {
1840: case ALLOC_FREE:
1841: return "free";
1842: case ALLOC_ROW:
1843: return "row";
1844: case ALLOC_USED:
1845: return "used";
1846: case ALLOC_FRAGMENT:
1847: return "fragment";
1848: case ALLOC_MINI_FRAG:
1849: return "mini-fragment";
1850: case ALLOC_INDEX:
1851: return "index";
1852: default:
1853: return String.valueOf(code);
1854: }
1855: }
1856:
1857: public String toString() {
1858: return "Store[" + _id + "]";
1859: }
1860:
1861: static class RandomAccessWrapper {
1862: private RandomAccessStream _file;
1863:
1864: RandomAccessWrapper(RandomAccessStream file) {
1865: _file = file;
1866: }
1867:
1868: RandomAccessStream getFile() {
1869: return _file;
1870: }
1871:
1872: void close() throws IOException {
1873: RandomAccessStream file = _file;
1874: _file = null;
1875:
1876: if (file != null)
1877: file.close();
1878: }
1879:
1880: protected void finalize() throws Throwable {
1881: super.finalize();
1882:
1883: close();
1884: }
1885: }
1886: }
|