blob: 21ed5d2c9e5b0420cad060d125fbc7a34008b641 [file] [log] [blame]
/*
* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
package org.eclipse.cdt.debug.edc.internal;
import java.nio.BufferUnderflowException;
import java.nio.ByteOrder;
import org.eclipse.cdt.debug.edc.IStreamBuffer;
/**
*
*/
public abstract class StreamBufferBase implements IStreamBuffer {
/* must be a power of 2 */
public static final int BUFFER_SIZE = 4096;
private static double LN_2 = 0.0;
private static double mathLogOf2() {
if (0.0 == LN_2) {
LN_2 = Math.log(2);
}
return LN_2;
}
/**
* get a smaller buffer size when capacity < 4096
* EXTENSION: go faster using bitshift operations.
* @return either BUFFER_SIZE or a smaller power of 2 that is
* larger than the sourceCapacity.
*/
private int getBufferSize() {
if (sourceCapacity >= BUFFER_SIZE) {
return BUFFER_SIZE;
}
return 1 << (int)Math.ceil(Math.log(sourceCapacity)/mathLogOf2());
}
protected ByteOrder order;
// absolute
private long position;
// absolute
private long sourceCapacity;
private byte[] buffer;
// absolute source position in buffer[0]
private long sourceOffset;
// absolute source position in buffer[buffer.length]
private long sourceLimit;
// offset from source to position
private final long baseOffset;
/**
* Create a buffer over some source content
* @param order native byte order of content
* @param baseOffset base offset from source to this buffer
* @param capacity total size of the source (from baseOffset)
*/
public StreamBufferBase(ByteOrder order, long baseOffset, long capacity) {
this.order = order;
this.baseOffset = baseOffset;
this.position = 0;
this.sourceCapacity = capacity;
this.buffer = new byte[getBufferSize()];
this.sourceOffset = 0;
this.sourceLimit = 0;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return getClass().getSimpleName() + " pos="+position() + " of "+ capacity() + " base="+ baseOffset; //$NON-NLS-N$ //$NON-NLS-2$ //$NON-NLS-3$
}
/**
* Fetch a page of content from the buffer.
* @param buffer the buffer
* @param sourceOffset absolute offset in original content
* @throws BufferUnderflowException
*/
protected abstract void fetchPage(byte[] buffer, long sourceOffset, int count);
protected abstract IStreamBuffer createSubBuffer(long offset, long size);
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#wrapSubsection(int)
*/
public IStreamBuffer wrapSubsection(long size) {
long availableSize = capacity() - position();
if (availableSize < size)
size = availableSize;
return createSubBuffer(position() + baseOffset, size);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#capacity()
*/
public long capacity() {
return sourceCapacity;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#hasRemaining()
*/
public boolean hasRemaining() {
return position < sourceCapacity;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#remaining()
*/
public long remaining() {
return sourceCapacity - position;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#position()
*/
public long position() {
return position;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#position(int)
*/
public IStreamBuffer position(long newPosition) {
if (newPosition < 0 || newPosition > sourceCapacity)
throw new IllegalArgumentException(newPosition + " not in 0.."+ sourceCapacity);
this.position = newPosition;
return this;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#get(byte[], int, int)
*/
public IStreamBuffer get(byte[] dst, int offset, int length) {
// read page-by-page if possible
while (length > 0) {
if (needFetch())
refetch();
int left = (int) Math.min(sourceLimit - position, length);
if (left > 0) {
System.arraycopy(buffer, (int) (position - sourceOffset), dst, offset, left);
offset += left;
position += left;
length -= left;
} else {
break;
}
}
return this;
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#get(byte[])
*/
public IStreamBuffer get(byte[] dst) {
return get(dst, 0, dst.length);
}
/**
* Fill memory buffer from source
*/
protected void refetch() {
long newSourceOffset = position - (position & buffer.length - 1);
if (newSourceOffset < 0)
throw new BufferUnderflowException();
if (newSourceOffset >= sourceCapacity)
throw new BufferUnderflowException();
int toFetch = (int) Math.min(sourceCapacity - newSourceOffset, buffer.length);
fetchPage(buffer, newSourceOffset + baseOffset, toFetch);
sourceOffset = newSourceOffset;
sourceLimit = sourceOffset + toFetch;
}
protected final boolean needFetch() {
return (position < sourceOffset || position >= sourceLimit);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#get()
*/
public byte get() {
if (needFetch())
refetch();
if (position < sourceCapacity)
return buffer[(int)((position++) - sourceOffset)];
else
throw new BufferUnderflowException();
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#getChar()
*/
public char getChar() {
return (char) getShort();
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#getShort()
*/
public short getShort() {
int a = get() & 0xff;
int b = get() & 0xff;
if (order == ByteOrder.LITTLE_ENDIAN)
return (short) (a | (b << 8));
else
return (short) (b | (a << 8));
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#getInt()
*/
public int getInt() {
int a = getShort() & 0xffff;
int b = getShort() & 0xffff;
if (order == ByteOrder.LITTLE_ENDIAN)
return a | (b << 16);
else
return b | (a << 16);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.internal.IStreamBuffer#getLong()
*/
public long getLong() {
long a = getInt();
long b = getInt();
if (order == ByteOrder.LITTLE_ENDIAN)
return a | (b << 32);
else
return b | (a << 32);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.IStreamBuffer#skip(long)
*/
public IStreamBuffer skip(long amount) {
return position(position() + amount);
}
public ByteOrder getOrder() {
return order;
}
public void setOrder(ByteOrder order) {
this.order = order;
}
}