/**
* Copyright 2012 Tobias Gierke <tobias.gierke@code-sourcery.de>
*
* 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 de.codesourcery.jasm16.compiler.io;
import java.io.IOException;
import java.io.OutputStream;
import de.codesourcery.jasm16.Address;
import de.codesourcery.jasm16.WordAddress;
/**
* Abstract base-class for implementing {@link IObjectCodeWriter}s.
*
* <p>This class takes care of properly handling {@link #advanceToWriteOffset(Address)}
* and should be subclasses whenever possible.</p>
* @author tobias.gierke@code-sourcery.de
*/
public abstract class AbstractObjectCodeWriter implements IObjectCodeWriter
{
private OutputStream writer;
private int currentWriteOffset = 0;
private int firstWriteOffset=0;
private int initialOffset;
protected AbstractObjectCodeWriter() {
this( WordAddress.ZERO );
}
protected AbstractObjectCodeWriter(WordAddress initialOffset)
{
this.initialOffset = initialOffset.getByteAddressValue();
}
@Override
public final void close() throws IOException
{
try {
closeHook();
}
finally
{
currentWriteOffset = 0;
if ( writer != null ) {
writer.close();
writer = null;
}
}
}
protected abstract void closeHook() throws IOException;
protected final OutputStream getOutputStream() throws IOException
{
if ( writer == null )
{
writer = createOutputStream();
if ( currentWriteOffset != 0 )
{
if ( currentWriteOffset > initialOffset ) {
writeZeros( currentWriteOffset - initialOffset );
}
}
}
return writer;
}
@Override
public Address getFirstWriteOffset()
{
return Address.byteAddress( firstWriteOffset+initialOffset );
}
private int getActualOffset() {
return currentWriteOffset+initialOffset;
}
protected abstract OutputStream createOutputStream() throws IOException;
@Override
public final void writeObjectCode(byte[] data) throws IOException
{
writeObjectCode( data , 0 , data.length );
}
@Override
public final void writeObjectCode(byte[] data, int offset, int length) throws IOException
{
writeObjectCodeHook(data,offset,length);
currentWriteOffset += length;
}
protected void writeObjectCodeHook(byte[] data, int offset, int length) throws IOException
{
getOutputStream().write( data ,offset,length );
}
@Override
public final void deleteOutput() throws IOException
{
this.currentWriteOffset = 0;
try {
close();
} catch(IOException e) {
// ok
}
deleteOutputHook();
}
protected abstract void deleteOutputHook() throws IOException;
@Override
public final Address getCurrentWriteOffset()
{
return Address.byteAddress( getActualOffset() );
}
@Override
public void advanceToWriteOffset(Address offset) throws IOException
{
if (offset == null) {
throw new IllegalArgumentException("offset must not be NULL.");
}
if ( offset.getValue() < getActualOffset() ) {
throw new IllegalStateException("Writer "+this+" is already at "+this.currentWriteOffset+" , cannot output object code at "+offset);
}
if ( offset.getValue() == getActualOffset() ) {
return;
}
if ( firstWriteOffset == 0 ) {
firstWriteOffset = offset.getValue();
}
if ( this.writer == null ) {
// delay writing zeros until the writer is actually opened, see getOutputStream()
this.currentWriteOffset = offset.getValue();
return;
}
final int delta = offset.getValue() - getActualOffset();
writeZeros( delta );
this.currentWriteOffset = offset.getValue();
}
protected final void writeZeros(int byteCount) throws IOException
{
if ( byteCount == 0 ) {
return;
}
int delta = byteCount;
int bufferSize = ( byteCount >> 1) << 1;
do {
delta = writeZeros( delta , bufferSize);
bufferSize = bufferSize >> 1;
} while( delta > 0 && bufferSize > 0);
while( delta > 0 ) {
writer.write( 0 );
delta--;
}
if ( delta != 0 ) {
throw new RuntimeException("Internal error, delta != 0 , was: "+delta);
}
}
protected final int writeZeros(int byteCount, int bufferSize) throws IOException
{
final byte[] buffer = new byte[bufferSize];
int delta = byteCount;
while( delta > bufferSize) {
delta=writeZeros( buffer , delta );
}
return delta;
}
protected final int writeZeros(byte[] buffer, int byteCount) throws IOException
{
int delta = byteCount;
final int bufferSize = buffer.length;
while( delta > bufferSize )
{
writer.write( buffer );
delta -= bufferSize;
}
return delta;
}
}