/**
* 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;
import java.util.ArrayList;
import java.util.List;
/**
* An (immutable) address range made up of starting address and size.
*
* @author tobias.gierke@code-sourcery.de
*/
public final class AddressRange
{
// note that client codes RELIES on this class being immutable !
private final Address start;
private final Address end;
private final Size size;
public AddressRange(Address start, Address end) {
if ( start == null ) {
throw new IllegalArgumentException("start of address range must not be NULL.");
}
if ( end == null ) {
throw new IllegalArgumentException("end of address range must not be NULL.");
}
if ( start.isGreaterThan( end ) ) {
throw new IllegalArgumentException("Start of address range must not be greater than end, start: "+start+" , end: "+end);
}
this.start = start;
this.size = Size.bytes( end.toByteAddress().getValue() - start.toByteAddress().getValue() );
this.end = start.plus( size , false );
}
public boolean overlaps(AddressRange other) {
if ( contains( other.getStartAddress() ) ) {
return true;
}
if ( other.contains( getStartAddress() ) ) {
return true;
}
if ( this.getStartAddress().isLessThan( other.getStartAddress() ) &&
this.getEndAddress().isEqualOrGreaterThan( other.getEndAddress() ) )
{
return true;
}
if ( other.getStartAddress().isLessThan( this.getStartAddress() ) &&
other.getEndAddress().isEqualOrGreaterThan( this.getEndAddress() ) )
{
return true;
}
return false;
}
@Override
public final boolean equals(Object obj)
{
if ( this == obj ) {
return true;
}
if ( obj instanceof AddressRange) {
final AddressRange other = (AddressRange) obj;
return this.getStartAddress().equals( other.getStartAddress() ) &&
this.getSize().equals( other.getSize() );
}
return false;
}
/**
* Converts a global address to a local one (relative to this adress range's start).
*
* <p>If the input address
* </p>
* @param address
* @return
*/
public int globalAddressToLocal(int address) {
return address - start.getWordAddressValue();
}
@Override
public final int hashCode()
{
int result = 31 + start.hashCode();
result += 31*result + size.hashCode();
return result;
}
public AddressRange(Address start, Size size) {
if ( start == null ) {
throw new IllegalArgumentException("start must not be NULL.");
}
if (size == null) {
throw new IllegalArgumentException("words must not be NULL.");
}
this.start = start;
this.size = size;
this.end = start.plus( size , false );
}
public Address getStartAddress()
{
return start;
}
public Address getEndAddress()
{
return end;
}
public Size getSize()
{
return size;
}
@Override
public String toString()
{
return getStartAddress()+" - "+getEndAddress()+" ( "+size.toSizeInWords()+" / "+size.toSizeInBytes()+" )";
}
public boolean contains(int wordAddress)
{
return wordAddress >= this.start.getWordAddressValue() &&
wordAddress < this.end.getWordAddressValue();
}
public boolean contains(Address address)
{
// (start,end]
return address.isEqualOrGreaterThan( getStartAddress() ) && address.isLessThan( getEndAddress() );
}
public boolean intersectsWith(AddressRange other) {
if ( contains( other.getStartAddress() ) )
{
return true;
}
if ( getStartAddress().isLessThan( other.getEndAddress() ) && other.getEndAddress().isLessThan( getEndAddress() ) )
{
return true;
}
return other.getStartAddress().isLessThan( getStartAddress() ) && other.getEndAddress().isGreaterThan( getEndAddress() );
}
public AddressRange addOffset(Size size) {
return new AddressRange( this.getStartAddress().plus( size , false ) , getSize() );
}
public AddressRange addOffset(Address offset) {
return new AddressRange( this.getStartAddress().plus( offset , false ) , getSize() );
}
public List<AddressRange> subtract(AddressRange gap)
{
if (gap == null) {
throw new IllegalArgumentException("gap must not be NULL.");
}
final List<AddressRange> result = new ArrayList<>();
if ( ! intersectsWith( gap ) ) {
throw new IllegalArgumentException("Gap "+gap+" does not intersect with memory region "+this);
}
final Address gapStart = gap.getStartAddress();
final Address gapEnd = gap.getEndAddress();
// simple cases: just shrink this region by the specified amount
if ( gapStart.equals( getStartAddress() ) )
{
// |---- gap ----|---- a ----|
result.add( new AddressRange( gapEnd , getEndAddress() ) );
}
else if ( gapEnd.equals( getEndAddress() ) )
{
// |---- a ----|---- gap ----|
result.add( new AddressRange( getStartAddress(), gap.getStartAddress() ) );
}
else
{
/* two disjoint regions.
*
* |---- a ----|---- gap ----|---- c ----|
*/
result.add( new AddressRange( getStartAddress() , gap.getStartAddress() ) );
result.add( new AddressRange( gap.getEndAddress() , getEndAddress() ) );
}
return result;
}
public boolean isAdjactantTo(AddressRange other) {
return this.getStartAddress().equals( other.getEndAddress() ) ||
this.getEndAddress().equals( other.getStartAddress() );
}
public AddressRange mergeWith(AddressRange other)
{
if ( contains( other ) ) {
return this;
}
if ( other.contains( this ) ) {
return other;
}
Address newStart;
Address newEnd;
if ( getStartAddress().isEqualOrLessThan( other.getStartAddress() ) ) {
newStart = this.start;
newEnd = other.getEndAddress();
} else {
newStart = other.start;
newEnd = this.getEndAddress();
}
final AddressRange result = new AddressRange( newStart , newEnd );
if ( result.size.getSizeInBytes() != ( size.getSizeInBytes() + other.size.getSizeInBytes() ) ) {
throw new IllegalArgumentException("Cannot merge non-adjactant address ranges "+this+" and "+other);
}
return result;
}
public boolean contains(AddressRange newRange)
{
return getStartAddress().isEqualOrLessThan( newRange.getStartAddress() ) && getEndAddress().isEqualOrGreaterThan( newRange.getEndAddress() );
}
}