/* This file is part of the db4o object database http://www.db4o.com
Copyright (C) 2004 - 2011 Versant Corporation http://www.versant.com
db4o is free software; you can redistribute it and/or modify it under
the terms of version 3 of the GNU General Public License as published
by the Free Software Foundation.
db4o 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. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see http://www.gnu.org/licenses/. */
package com.db4o.internal;
import com.db4o.*;
import com.db4o.config.*;
import com.db4o.ext.*;
import com.db4o.foundation.*;
import com.db4o.internal.btree.*;
import com.db4o.internal.convert.*;
import com.db4o.internal.events.*;
import com.db4o.internal.fileheader.*;
import com.db4o.internal.freespace.*;
import com.db4o.internal.ids.*;
import com.db4o.internal.qlin.*;
import com.db4o.internal.query.processor.*;
import com.db4o.internal.query.result.*;
import com.db4o.internal.references.*;
import com.db4o.internal.slots.*;
import com.db4o.qlin.*;
/**
* @exclude
*/
public abstract class LocalObjectContainer extends ExternalObjectContainer implements InternalObjectContainer, EmbeddedObjectContainer{
protected FileHeader _fileHeader;
private final Collection4 _dirtyClassMetadata = new Collection4();
private FreespaceManager _freespaceManager;
private boolean i_isServer = false;
private Lock4 _semaphoresLock = new Lock4();
private Hashtable4 _semaphores;
private int _blockEndAddress;
private SystemData _systemData;
private IdSystem _idSystem;
private final byte[] _pointerBuffer = new byte[Const4.POINTER_LENGTH];
protected final ByteArrayBuffer _pointerIo = new ByteArrayBuffer(Const4.POINTER_LENGTH);
private long _maximumDatabaseFileSize;
LocalObjectContainer(Configuration config) {
super(config);
Config4Impl configImpl = (Config4Impl)config;
_maximumDatabaseFileSize = maximumDatabaseFileSize(configImpl);
}
protected long maximumDatabaseFileSize(Config4Impl configImpl) {
return configImpl.maximumDatabaseFileSize();
}
public Transaction newTransaction(Transaction parentTransaction, ReferenceSystem referenceSystem, boolean isSystemTransaction) {
TransactionalIdSystem systemIdSystem = null;
if(! isSystemTransaction){
systemIdSystem = systemTransaction().idSystem();
}
Closure4<IdSystem> idSystem = new Closure4<IdSystem>() {
public IdSystem run() {
return idSystem();
}
};
TransactionalIdSystem transactionalIdSystem = newTransactionalIdSystem(systemIdSystem, idSystem);
return new LocalTransaction(this, parentTransaction, transactionalIdSystem, referenceSystem);
}
public TransactionalIdSystem newTransactionalIdSystem(TransactionalIdSystem systemIdSystem, Closure4<IdSystem> idSystem) {
return new TransactionalIdSystemImpl(
new Closure4<FreespaceManager>() {
public FreespaceManager run() {
return freespaceManager();
}
},
idSystem,
(TransactionalIdSystemImpl)systemIdSystem);
}
public FreespaceManager freespaceManager() {
return _freespaceManager;
}
public void blockSizeReadFromFile(int size){
blockSize(size);
setRegularEndAddress(fileLength());
}
public void setRegularEndAddress(long address){
_blockEndAddress = _blockConverter.bytesToBlocks(address);
}
final protected void close2() {
try {
if (!_config.isReadOnly()) {
commitTransaction();
shutdown();
}
}
finally {
shutdownObjectContainer();
}
}
public void commit1(Transaction trans) {
trans.commit();
}
void configureNewFile() {
blockSize(configImpl().blockSize());
_fileHeader = FileHeader.newCurrentFileHeader();
setRegularEndAddress(_fileHeader.length());
newSystemData(configImpl().freespaceSystem(), configImpl().idSystemType());
systemData().converterVersion(Converter.VERSION);
createStringIO(_systemData.stringEncoding());
createIdSystem();
initializeClassMetadataRepository();
initalizeWeakReferenceSupport();
generateNewIdentity();
AbstractFreespaceManager blockedFreespaceManager = AbstractFreespaceManager.createNew(this);
installFreespaceManager(blockedFreespaceManager);
initNewClassCollection();
initializeEssentialClasses();
_fileHeader.initNew(this);
blockedFreespaceManager.start(0);
}
private void newSystemData(byte freespaceSystemType, byte idSystemType){
_systemData = new SystemData();
_systemData.stringEncoding(configImpl().encoding());
_systemData.freespaceSystem(freespaceSystemType);
_systemData.idSystemType(idSystemType);
}
public int converterVersion() {
return _systemData.converterVersion();
}
public long currentVersion() {
return _timeStampIdGenerator.last();
}
void initNewClassCollection() {
// overridden in YapObjectCarrier to do nothing
classCollection().initTables(1);
}
public final BTree createBTreeClassIndex(int id){
return new BTree(_transaction, id, new IDHandler());
}
public final AbstractQueryResult newQueryResult(Transaction trans) {
return newQueryResult(trans, config().evaluationMode());
}
public final AbstractQueryResult newQueryResult(Transaction trans, QueryEvaluationMode mode) {
if (trans == null) {
throw new ArgumentNullException();
}
if(mode == QueryEvaluationMode.IMMEDIATE){
return new IdListQueryResult(trans);
}
return new HybridQueryResult(trans, mode);
}
public final boolean delete4(Transaction transaction, ObjectReference ref, Object obj, int cascade, boolean userCall) {
int id = ref.getID();
StatefulBuffer reader = readStatefulBufferById(transaction, id);
if (reader != null) {
if (obj != null) {
if ((!showInternalClasses())
&& Const4.CLASS_INTERNAL.isAssignableFrom(obj.getClass())) {
return false;
}
}
reader.setCascadeDeletes(cascade);
transaction.idSystem().notifySlotDeleted(id, SlotChangeFactory.USER_OBJECTS);
ClassMetadata classMetadata = ref.classMetadata();
classMetadata.delete(reader, obj);
return true;
}
return false;
}
public abstract long fileLength();
public abstract String fileName();
public void free(Slot slot) {
if(slot.isNull()){
return;
// TODO: This should really be an IllegalArgumentException but old database files
// with index-based FreespaceManagers appear to deliver zeroed slots.
// throw new IllegalArgumentException();
}
if(_freespaceManager == null){
// Can happen on early free before freespacemanager
// is up, during conversion.
return;
}
if(DTrace.enabled){
DTrace.FILE_FREE.logLength(slot.address(), slot.length());
}
_freespaceManager.free(slot);
}
public void free(int address, int a_length) {
free(new Slot(address, a_length));
}
public void generateNewIdentity(){
synchronized(_lock){
setIdentity(Db4oDatabase.generate());
}
}
public AbstractQueryResult queryAllObjects(Transaction trans) {
return getAll(trans, config().evaluationMode());
}
public AbstractQueryResult getAll(Transaction trans, QueryEvaluationMode mode) {
final AbstractQueryResult queryResult = newQueryResult(trans, mode);
queryResult.loadFromClassIndexes(classCollection().iterator());
return queryResult;
}
public int allocatePointerSlot() {
int id = allocateSlot(Const4.POINTER_LENGTH).address();
if(!isValidPointer(id)){
return allocatePointerSlot();
}
// write a zero pointer first
// to prevent delete interaction trouble
writePointer(id, Slot.ZERO);
if(DTrace.enabled){
DTrace.GET_POINTER_SLOT.log(id);
}
return id;
}
protected boolean isValidPointer(int id) {
// We have to make sure that object IDs do not collide
// with built-in type IDs.
return ! _handlers.isSystemHandler(id);
}
public Slot allocateSlot(int length){
if(length <= 0){
throw new IllegalArgumentException();
}
if(_freespaceManager != null && _freespaceManager.isStarted()){
Slot slot = _freespaceManager.allocateSlot(length);
if(slot != null){
if(DTrace.enabled){
DTrace.GET_SLOT.logLength(slot.address(), slot.length());
}
return slot;
}
while(growDatabaseByConfiguredSize()){
slot = _freespaceManager.allocateSlot(length);
if(slot != null){
if(DTrace.enabled){
DTrace.GET_SLOT.logLength(slot.address(), slot.length());
}
return slot;
}
}
}
Slot appendedSlot = appendBytes(length);
if(DTrace.enabled){
DTrace.GET_SLOT.logLength(appendedSlot.address(), appendedSlot.length());
}
return appendedSlot;
}
private boolean growDatabaseByConfiguredSize() {
int reservedStorageSpace = configImpl().databaseGrowthSize();
if(reservedStorageSpace <= 0){
return false;
}
int reservedBlocks = _blockConverter.bytesToBlocks(reservedStorageSpace);
int reservedBytes = _blockConverter.blocksToBytes(reservedBlocks);
Slot slot = new Slot(_blockEndAddress, reservedBlocks);
if (Debug4.xbytes && Deploy.overwrite) {
overwriteDeletedBlockedSlot(slot);
}else{
writeBytes(new ByteArrayBuffer(reservedBytes), _blockEndAddress, 0);
}
_freespaceManager.free(_blockConverter.toNonBlockedLength(slot));
_blockEndAddress += reservedBlocks;
return true;
}
public final Slot appendBytes(long bytes){
int blockCount = _blockConverter.bytesToBlocks(bytes);
int blockedStartAddress = _blockEndAddress;
int blockedEndAddress = _blockEndAddress + blockCount;
int blockedMaximumFileSize = _blockConverter.bytesToBlocks(_maximumDatabaseFileSize);
if(blockedEndAddress < 0 || blockedEndAddress >= blockedMaximumFileSize){
switchToReadOnlyMode();
throw new DatabaseMaximumSizeReachedException(_blockConverter.blocksToBytes(_blockEndAddress));
}
_blockEndAddress = blockedEndAddress;
Slot slot = new Slot(blockedStartAddress, blockCount);
if (Debug4.xbytes && Deploy.overwrite) {
overwriteDeletedBlockedSlot(slot);
}
return _blockConverter.toNonBlockedLength(slot);
}
private void switchToReadOnlyMode() {
_config.readOnly(true);
}
// When a file gets opened, it uses the file size to determine where
// new slots can be appended. If this method would not be called, the
// freespace system could already contain a slot that points beyond
// the end of the file and this space could be allocated and used twice,
// for instance if a slot was allocated and freed without ever being
// written to file.
void ensureLastSlotWritten(){
if (!Debug4.xbytes){
if(Deploy.overwrite){
if(_blockEndAddress > _blockConverter.bytesToBlocks(fileLength())){
StatefulBuffer writer = createStatefulBuffer(systemTransaction(), _blockEndAddress - 1, blockSize());
writer.write();
}
}
}
}
public Db4oDatabase identity() {
return _systemData.identity();
}
public void setIdentity(Db4oDatabase identity){
synchronized(lock()){
_systemData.identity(identity);
// The dirty TimeStampIdGenerator triggers writing of
// the variable part of the systemdata. We need to
// make it dirty here, so the new identity is persisted:
_timeStampIdGenerator.generate();
_fileHeader.writeVariablePart(this);
}
}
boolean isServer() {
return i_isServer;
}
public final int idForNewUserObject(Transaction trans) {
return trans.idSystem().newId(SlotChangeFactory.USER_OBJECTS);
}
public void raiseCommitTimestamp(long minimumVersion) {
synchronized (lock()) {
_timeStampIdGenerator.setMinimumNext(minimumVersion);
}
}
public StatefulBuffer readStatefulBufferById(Transaction a_ta, int a_id) {
return readStatefulBufferById(a_ta, a_id, false);
}
@Override
public ByteArrayBuffer[] readSlotBuffers(Transaction transaction, int ids[]) {
ByteArrayBuffer[] buffers = new ByteArrayBuffer[ids.length];
for (int i = 0; i < ids.length; ++i) {
if (ids[i] == 0) {
buffers[i] = null;
} else {
buffers[i] = readBufferById(transaction, ids[i]);
}
}
return buffers;
}
public ByteArrayBuffer readBufferById(Transaction trans, int id) {
return readBufferById(trans, id, false);
}
public final ByteArrayBuffer readBufferById(Transaction trans, int id, boolean lastCommitted) {
if (id <= 0) {
throw new IllegalArgumentException();
}
Slot slot = lastCommitted ? trans.idSystem().committedSlot(id) : trans.idSystem().currentSlot(id);
if(DTrace.enabled){
DTrace.SLOT_READ.logLength(id, slot);
}
return readBufferBySlot(slot);
}
public StatefulBuffer readStatefulBufferById(Transaction trans, int id, boolean lastCommitted) {
if (id <= 0) {
throw new IllegalArgumentException("id=" + id);
}
Slot slot = lastCommitted ? trans.idSystem().committedSlot(id) : trans.idSystem().currentSlot(id);
if(DTrace.enabled){
DTrace.SLOT_READ.logLength(id, slot);
}
return readStatefulBufferBySlot(trans, id, slot);
}
public ByteArrayBuffer readBufferBySlot(Slot slot) {
if (Slot.isNull(slot)) {
return null;
}
if (DTrace.enabled) {
DTrace.READ_SLOT.logLength(slot.address(), slot.length());
}
ByteArrayBuffer buffer = new ByteArrayBuffer(slot.length());
buffer.readEncrypt(this, slot.address());
return buffer;
}
public StatefulBuffer readStatefulBufferBySlot(Transaction trans, int id, Slot slot) {
if (Slot.isNull(slot)) {
return null;
}
if (DTrace.enabled) {
DTrace.READ_SLOT.logLength(slot.address(), slot.length());
}
StatefulBuffer buffer = createStatefulBuffer(trans, slot.address(), slot.length());
buffer.setID(id);
buffer.readEncrypt(this, slot.address());
return buffer;
}
protected boolean doFinalize() {
return _fileHeader != null;
}
void readThis() throws OldFormatException {
newSystemData(AbstractFreespaceManager.FM_LEGACY_RAM, StandardIdSystemFactory.LEGACY);
blockSizeReadFromFile(1);
_fileHeader = FileHeader.read(this);
if (config().generateCommitTimestamps().isUnspecified()) {
config().generateCommitTimestamps(_systemData.idToTimestampIndexId() != 0);
}
createStringIO(_systemData.stringEncoding());
createIdSystem();
initializeClassMetadataRepository();
initalizeWeakReferenceSupport();
setNextTimeStampId(systemData().lastTimeStampID());
classCollection().setID(_systemData.classCollectionID());
classCollection().read(systemTransaction());
Converter.convert(new ConversionStage.ClassCollectionAvailableStage(this));
_fileHeader.readIdentity(this);
if(_config.isReadOnly()) {
return;
}
if (!configImpl().commitRecoveryDisabled()) {
_fileHeader.completeInterruptedTransaction(this);
}
FreespaceManager blockedFreespaceManager = AbstractFreespaceManager.createNew(this,
_systemData.freespaceSystem());
installFreespaceManager(blockedFreespaceManager);
blockedFreespaceManager.read(this, _systemData.inMemoryFreespaceSlot());
blockedFreespaceManager.start(_systemData.bTreeFreespaceId());
_fileHeader = _fileHeader.convert(this);
if(freespaceMigrationRequired(blockedFreespaceManager)){
migrateFreespace(blockedFreespaceManager);
}
writeHeader(true, false);
if(Converter.convert(new ConversionStage.SystemUpStage(this))){
_systemData.converterVersion(Converter.VERSION);
_fileHeader.writeVariablePart(this);
transaction().commit();
}
}
private void installFreespaceManager(
FreespaceManager blockedFreespaceManager) {
_freespaceManager = blockSize() == 1 ?
blockedFreespaceManager :
new BlockAwareFreespaceManager(blockedFreespaceManager, _blockConverter);
}
protected void createIdSystem() {
_idSystem = StandardIdSystemFactory.newInstance(this);
}
private boolean freespaceMigrationRequired(FreespaceManager freespaceManager) {
if(freespaceManager == null){
return false;
}
byte readSystem = _systemData.freespaceSystem();
byte configuredSystem = configImpl().freespaceSystem();
if(freespaceManager.systemType() == configuredSystem){
return false;
}
if (configuredSystem != 0){
return true;
}
return AbstractFreespaceManager.migrationRequired(readSystem);
}
private void migrateFreespace(FreespaceManager oldFreespaceManager) {
FreespaceManager newFreespaceManager = AbstractFreespaceManager.createNew(this, configImpl().freespaceSystem());
newFreespaceManager.start(0);
systemData().freespaceSystem(configImpl().freespaceSystem());
installFreespaceManager(newFreespaceManager);
AbstractFreespaceManager.migrate(oldFreespaceManager, newFreespaceManager);
_fileHeader.writeVariablePart(this);
}
public final void releaseSemaphore(String name) {
releaseSemaphore(null, name);
}
public final void releaseSemaphore(final Transaction trans, final String name) {
synchronized(_lock){
if (_semaphores == null) {
return;
}
}
_semaphoresLock.run(new Closure4() { public Object run() {
Transaction transaction = checkTransaction(trans);
if (_semaphores != null && transaction == _semaphores.get(name)) {
_semaphores.remove(name);
}
_semaphoresLock.awake();
return null;
}});
}
public void releaseSemaphores(final Transaction trans) {
if (_semaphores != null) {
final Hashtable4 semaphores = _semaphores;
_semaphoresLock.run(new Closure4() { public Object run() {
semaphores.forEachKeyForIdentity(new Visitor4() {
public void visit(Object a_object) {
semaphores.remove(a_object);
}
}, trans);
_semaphoresLock.awake();
return null;
}});
}
}
public final void rollback1(Transaction trans) {
trans.rollback();
}
public final void setDirtyInSystemTransaction(PersistentBase a_object) {
a_object.setStateDirty();
a_object.cacheDirty(_dirtyClassMetadata);
}
public final boolean setSemaphore(String name, int timeout) {
return setSemaphore(null, name, timeout);
}
public final boolean setSemaphore(final Transaction trans, final String name, final int timeout) {
if (name == null) {
throw new NullPointerException();
}
synchronized (_lock) {
if (_semaphores == null) {
_semaphores = new Hashtable4(10);
}
}
final BooleanByRef acquired = new BooleanByRef();
_semaphoresLock.run(new Closure4() { public Object run() {
try{
Transaction transaction = checkTransaction(trans);
Object candidateTransaction = _semaphores.get(name);
if (trans == candidateTransaction) {
acquired.value = true;
return null;
}
if (candidateTransaction == null) {
_semaphores.put(name, transaction);
acquired.value = true;
return null;
}
long endtime = System.currentTimeMillis() + timeout;
long waitTime = timeout;
while (waitTime > 0) {
_semaphoresLock.awake();
_semaphoresLock.snooze(waitTime);
if (classCollection() == null) {
acquired.value = false;
return null;
}
candidateTransaction = _semaphores.get(name);
if (candidateTransaction == null) {
_semaphores.put(name, transaction);
acquired.value = true;
return null;
}
waitTime = endtime - System.currentTimeMillis();
}
acquired.value = false;
return null;
} finally{
_semaphoresLock.awake();
}
}});
return acquired.value;
}
public void setServer(boolean flag) {
i_isServer = flag;
}
public abstract void syncFiles();
public abstract void syncFiles(Runnable runnable);
protected String defaultToString() {
return fileName();
}
public void shutdown() {
writeHeader(false, true);
}
public final void commitTransaction() {
_transaction.commit();
}
public abstract void writeBytes(ByteArrayBuffer buffer, int blockedAddress, int addressOffset);
public final void writeDirtyClassMetadata() {
writeCachedDirty();
}
private void writeCachedDirty() {
Iterator4 i = _dirtyClassMetadata.iterator();
while (i.moveNext()) {
PersistentBase dirty = (PersistentBase) i.current();
dirty.write(systemTransaction());
dirty.notCachedDirty();
}
_dirtyClassMetadata.clear();
}
public final void writeEncrypt(ByteArrayBuffer buffer, int address, int addressOffset) {
_handlers.encrypt(buffer);
writeBytes(buffer, address, addressOffset);
_handlers.decrypt(buffer);
}
public void writeHeader(boolean startFileLockingThread, boolean shuttingDown) {
if(shuttingDown){
_freespaceManager.write(this);
_freespaceManager = null;
}
StatefulBuffer writer = createStatefulBuffer(systemTransaction(), 0, _fileHeader.length());
_fileHeader.writeFixedPart(this, startFileLockingThread, shuttingDown, writer, blockSize());
if(shuttingDown){
ensureLastSlotWritten();
}
syncFiles();
}
public final void writeNew(Transaction trans, Pointer4 pointer, ClassMetadata classMetadata, ByteArrayBuffer buffer) {
writeEncrypt(buffer, pointer.address(), 0);
if(classMetadata == null){
return;
}
classMetadata.addToIndex(trans, pointer.id());
}
// This is a reroute of writeBytes to write the free blocks
// unchecked.
public abstract void overwriteDeletedBytes(int address, int length);
public void overwriteDeletedBlockedSlot(Slot slot) {
overwriteDeletedBytes(slot.address(), _blockConverter.blocksToBytes(slot.length()));
}
public final void writeTransactionPointer(int pointer) {
_fileHeader.writeTransactionPointer(systemTransaction(), pointer);
}
public final Slot allocateSlotForUserObjectUpdate(Transaction trans, int id, int length){
Slot slot = allocateSlot(length);
trans.idSystem().notifySlotUpdated(id, slot, SlotChangeFactory.USER_OBJECTS);
return slot;
}
public final Slot allocateSlotForNewUserObject(Transaction trans, int id, int length){
Slot slot = allocateSlot(length);
trans.idSystem().notifySlotCreated(id, slot, SlotChangeFactory.USER_OBJECTS);
return slot;
}
public final void writeUpdate(Transaction trans, Pointer4 pointer, ClassMetadata classMetadata, ArrayType arrayType, ByteArrayBuffer buffer) {
int address = pointer.address();
if(address == 0){
address = allocateSlotForUserObjectUpdate(trans, pointer.id(), pointer.length()).address();
}
writeEncrypt(buffer, address, 0);
}
public void setNextTimeStampId(long val) {
_timeStampIdGenerator.setMinimumNext(val);
}
public SystemInfo systemInfo() {
return new SystemInfoFileImpl(this);
}
public FileHeader getFileHeader() {
return _fileHeader;
}
public void installDebugFreespaceManager(FreespaceManager manager) {
_freespaceManager = manager;
}
public SystemData systemData() {
return _systemData;
}
public long[] getIDsForClass(Transaction trans, ClassMetadata clazz){
final IntArrayList ids = new IntArrayList();
clazz.index().traverseIds(trans, new Visitor4() {
public void visit(Object obj) {
ids.add(((Integer)obj).intValue());
}
});
return ids.asLong();
}
public QueryResult classOnlyQuery(QQueryBase query, ClassMetadata clazz){
if (!clazz.hasClassIndex()) {
return new IdListQueryResult(query.transaction());
}
final AbstractQueryResult queryResult = newQueryResult(query.transaction());
queryResult.loadFromClassIndex(clazz);
return queryResult;
}
public QueryResult executeQuery(QQuery query){
AbstractQueryResult queryResult = newQueryResult(query.transaction());
queryResult.loadFromQuery(query);
return queryResult;
}
public LocalTransaction localSystemTransaction() {
return (LocalTransaction)systemTransaction();
}
public int instanceCount(ClassMetadata clazz, Transaction trans) {
synchronized(lock()) {
return clazz.indexEntryCount(trans);
}
}
public ObjectContainer openSession(){
synchronized(lock()) {
return new ObjectContainerSession(this);
}
}
@Override
public boolean isDeleted(Transaction trans, int id){
return trans.idSystem().isDeleted(id);
}
public void writePointer(int id, Slot slot) {
if(DTrace.enabled){
DTrace.WRITE_POINTER.log(id);
DTrace.WRITE_POINTER.logLength(slot);
}
_pointerIo.seek(0);
if (Deploy.debug) {
_pointerIo.writeBegin(Const4.YAPPOINTER);
}
_pointerIo.writeInt(slot.address());
_pointerIo.writeInt(slot.length());
if (Deploy.debug) {
_pointerIo.writeEnd();
}
if(Debug4.xbytes){
_pointerIo.checkXBytes(false);
}
writeBytes(_pointerIo, id, 0);
}
public Slot debugReadPointerSlot(int id) {
if (Deploy.debug) {
readBytes(_pointerIo._buffer, id, Const4.POINTER_LENGTH);
_pointerIo.seek(0);
_pointerIo.readBegin(Const4.YAPPOINTER);
int debugAddress = _pointerIo.readInt();
int debugLength = _pointerIo.readInt();
_pointerIo.readEnd();
return new Slot(debugAddress, debugLength);
}
return null;
}
public final Slot readPointerSlot(int id) {
if (Deploy.debug) {
return debugReadPointerSlot(id);
}
if(!isValidId(id)){
throw new InvalidIDException(id);
}
readBytes(_pointerBuffer, id, Const4.POINTER_LENGTH);
int address = (_pointerBuffer[3] & 255)
| (_pointerBuffer[2] & 255) << 8 | (_pointerBuffer[1] & 255) << 16
| _pointerBuffer[0] << 24;
int length = (_pointerBuffer[7] & 255)
| (_pointerBuffer[6] & 255) << 8 | (_pointerBuffer[5] & 255) << 16
| _pointerBuffer[4] << 24;
if(!isValidSlot(address, length)){
throw new InvalidSlotException(address, length, id);
}
return new Slot(address, length);
}
private boolean isValidId(int id) {
return fileLength() >= id;
}
private boolean isValidSlot(int address, int length) {
// just in case overflow
long fileLength = fileLength();
boolean validAddress = fileLength >= address;
boolean validLength = fileLength >= length ;
boolean validSlot = fileLength >= (address+length);
return validAddress && validLength && validSlot;
}
protected void closeIdSystem(){
if(_idSystem != null){
_idSystem.close();
}
}
public IdSystem idSystem(){
return _idSystem;
}
public Runnable commitHook() {
_systemData.lastTimeStampID(_timeStampIdGenerator.last());
return _fileHeader.commit(false);
}
public final Slot allocateSafeSlot(int length) {
Slot reusedSlot = freespaceManager().allocateSafeSlot(length);
if(reusedSlot != null){
return reusedSlot;
}
return appendBytes(length);
}
public EventRegistryImpl newEventRegistry(){
return new EventRegistryImpl();
}
public <T> QLin<T> from(Class<T> clazz) {
return new QLinRoot<T>(query(), clazz);
}
}