/* 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 java.io.*; import com.db4o.*; import com.db4o.defragment.*; import com.db4o.foundation.*; import com.db4o.internal.encoding.*; import com.db4o.internal.mapping.*; import com.db4o.internal.marshall.*; import com.db4o.internal.slots.*; import com.db4o.marshall.*; import com.db4o.typehandlers.*; /** * @exclude */ public final class DefragmentContextImpl implements ReadWriteBuffer, DefragmentContext { private ByteArrayBuffer _source; private ByteArrayBuffer _target; private DefragmentServices _services; private final ObjectHeader _objectHeader; private int _declaredAspectCount; private int _currentParentSourceID; public DefragmentContextImpl(ByteArrayBuffer source, DefragmentContextImpl context) { this(source, context._services, context._objectHeader); } public DefragmentContextImpl(ByteArrayBuffer source,DefragmentServices services) { this(source, services, null); } public DefragmentContextImpl(ByteArrayBuffer source, DefragmentServices services, ObjectHeader header){ _source = source; _services=services; _target = new ByteArrayBuffer(length()); _source.copyTo(_target, 0, 0, length()); _objectHeader = header; } public DefragmentContextImpl(DefragmentContextImpl parentContext, ObjectHeader header){ _source = parentContext._source; _target = parentContext._target; _services = parentContext._services; _objectHeader = header; } public int offset() { return _source.offset(); } public void seek(int offset) { _source.seek(offset); _target.seek(offset); } public void incrementOffset(int numBytes) { _source.incrementOffset(numBytes); _target.incrementOffset(numBytes); } public void incrementIntSize() { incrementOffset(Const4.INT_LENGTH); } public int copySlotlessID() { return copyUnindexedId(false); } public int copyUnindexedID() { return copyUnindexedId(true); } private int copyUnindexedId(boolean doRegister){ int orig=_source.readInt(); // TODO: There is no test case for the zero case if(orig == 0){ _target.writeInt(0); return 0; } int mapped=-1; try { mapped=_services.strictMappedID(orig); } catch (MappingNotFoundException exc) { mapped=_services.targetNewId(); _services.mapIDs(orig,mapped, false); if(doRegister){ _services.registerUnindexed(orig); } } _target.writeInt(mapped); return mapped; } public int copyID() { // This code is slightly redundant. // The profiler shows it's a hotspot. // The following would be non-redudant. // return copy(false, false); int id = _source.readInt(); return writeMappedID(id); } public int copyID(boolean flipNegative) { int id=_source.readInt(); return internalCopyID(flipNegative, id); } public int copyIDReturnOriginalID() { return copyIDReturnOriginalID(false); } public int copyIDReturnOriginalID(boolean flipNegative) { int id=_source.readInt(); internalCopyID(flipNegative, id); boolean flipped = flipNegative && (id < 0); if(flipped) { return -id; } return id; } private int internalCopyID(boolean flipNegative, int id) { boolean flipped = flipNegative && (id < 0); if(flipped) { id=-id; } int mapped=_services.mappedID(id); if(flipped) { mapped=-mapped; } _target.writeInt(mapped); return mapped; } public void readBegin(byte identifier) { _source.readBegin(identifier); _target.readBegin(identifier); } public byte readByte() { byte value=_source.readByte(); _target.incrementOffset(1); return value; } public void readBytes(byte[] bytes) { _source.readBytes(bytes); _target.incrementOffset(bytes.length); } public int readInt() { int value=_source.readInt(); _target.incrementOffset(Const4.INT_LENGTH); return value; } public void writeInt(int value) { _source.incrementOffset(Const4.INT_LENGTH); _target.writeInt(value); } public void write(LocalObjectContainer file,int address) { file.writeBytes(_target,address,0); } public void incrementStringOffset(LatinStringIO sio) { incrementStringOffset(sio, _source); incrementStringOffset(sio, _target); } private void incrementStringOffset(LatinStringIO sio, ByteArrayBuffer buffer) { sio.readLengthAndString(buffer); } public ByteArrayBuffer sourceBuffer() { return _source; } public ByteArrayBuffer targetBuffer() { return _target; } public IDMapping mapping() { return _services; } public Transaction systemTrans() { return transaction(); } public DefragmentServices services() { return _services; } public static void processCopy(DefragmentServices context, int sourceID,SlotCopyHandler command) { ByteArrayBuffer sourceReader = context.sourceBufferByID(sourceID); processCopy(context, sourceID, command, sourceReader); } public static void processCopy(DefragmentServices services, int sourceID,SlotCopyHandler command, ByteArrayBuffer sourceReader) { int targetID=services.strictMappedID(sourceID); Slot targetSlot = services.allocateTargetSlot(sourceReader.length()); services.mapping().mapId(targetID, targetSlot); DefragmentContextImpl context=new DefragmentContextImpl(sourceReader,services); command.processCopy(context); services.targetWriteBytes(context,targetSlot.address()); } public void writeByte(byte value) { _source.incrementOffset(1); _target.writeByte(value); } public long readLong() { long value=_source.readLong(); _target.incrementOffset(Const4.LONG_LENGTH); return value; } public void writeLong(long value) { _source.incrementOffset(Const4.LONG_LENGTH); _target.writeLong(value); } public BitMap4 readBitMap(int bitCount) { BitMap4 value=_source.readBitMap(bitCount); _target.incrementOffset(value.marshalledLength()); return value; } public void readEnd() { _source.readEnd(); _target.readEnd(); } public int writeMappedID(int originalID) { int mapped=_services.mappedID(originalID); _target.writeInt(mapped); return mapped; } public int length() { return _source.length(); } public Transaction transaction() { return services().systemTrans(); } public ObjectContainerBase container() { return transaction().container(); } public TypeHandler4 typeHandlerForId(int id) { return container().typeHandlerForClassMetadataID(id); } public int handlerVersion(){ return _objectHeader.handlerVersion(); } public boolean isLegacyHandlerVersion() { return handlerVersion() == 0; } public int mappedID(int origID) { return mapping().strictMappedID(origID); } public ObjectContainer objectContainer() { return container(); } /** * only used by old handlers: OpenTypeHandler0, StringHandler0, ArrayHandler0. * Doesn't need to work with modern IdSystems. */ public Slot allocateTargetSlot(int length) { return _services.allocateTargetSlot(length); } /** * only used by old handlers: OpenTypeHandler0, StringHandler0, ArrayHandler0. * Doesn't need to work with modern IdSystems. */ public Slot allocateMappedTargetSlot(int sourceAddress, int length) { Slot slot = allocateTargetSlot(length); _services.mapIDs(sourceAddress, slot.address(), false); return slot; } public int copySlotToNewMapped(int sourceAddress, int length) throws IOException { Slot slot = allocateMappedTargetSlot(sourceAddress, length); ByteArrayBuffer sourceBuffer = sourceBufferByAddress(sourceAddress, length); targetWriteBytes(slot.address(), sourceBuffer); return slot.address(); } public void targetWriteBytes(int address, ByteArrayBuffer buffer) { _services.targetWriteBytes(buffer, address); } public ByteArrayBuffer sourceBufferByAddress(int sourceAddress, int length) throws IOException { ByteArrayBuffer sourceBuffer = _services.sourceBufferByAddress(sourceAddress, length); return sourceBuffer; } public ByteArrayBuffer sourceBufferById(int sourceId) throws IOException { ByteArrayBuffer sourceBuffer = _services.sourceBufferByID(sourceId); return sourceBuffer; } public void writeToTarget(int address) { _services.targetWriteBytes(this, address); } public void writeBytes(byte[] bytes) { _target.writeBytes(bytes); _source.incrementOffset(bytes.length); } public ReadBuffer buffer() { return _source; } public void defragment(TypeHandler4 handler) { final TypeHandler4 typeHandler = HandlerRegistry.correctHandlerVersion(this, handler); if(Handlers4.useDedicatedSlot(this, typeHandler)){ if(Handlers4.hasClassIndex(typeHandler)){ copyID(); } else { copyUnindexedID(); } return; } typeHandler.defragment(DefragmentContextImpl.this); } public void beginSlot() { // do nothing } public ClassMetadata classMetadata() { return _objectHeader.classMetadata(); } public boolean isNull(int fieldIndex) { return _objectHeader._headerAttributes.isNull(fieldIndex); } public int declaredAspectCount() { return _declaredAspectCount; } public void declaredAspectCount(int count) { _declaredAspectCount = count; } public SlotFormat slotFormat() { return SlotFormat.forHandlerVersion(handlerVersion()); } public void currentParentSourceID(int id) { _currentParentSourceID = id; } public int consumeCurrentParentSourceID() { int id = _currentParentSourceID; _currentParentSourceID = 0; return id; } public void copyAddress() { int sourceEntryAddress = _source.readInt(); int sourceId = consumeCurrentParentSourceID(); int sourceObjectAddress = _services.sourceAddressByID(sourceId); int entryOffset = sourceEntryAddress - sourceObjectAddress; int targetObjectAddress = _services.targetAddressByID(_services.strictMappedID(sourceId)); _target.writeInt(targetObjectAddress + entryOffset); } }