001/*
002 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003 *
004 *  This file is licensed to You under the Eclipse Public License (EPL);
005 *  You may not use this file except in compliance with the License. You
006 *  may obtain a copy of the License at
007 *
008 *      http://www.opensource.org/licenses/eclipse-1.0.php
009 *
010 *  See the COPYRIGHT.txt file distributed with this work for information
011 *  regarding copyright ownership.
012 */
013package org.jikesrvm.util;
014
015import java.io.InputStream;
016import java.io.IOException;
017
018import org.vmmagic.unboxed.Address;
019import org.vmmagic.unboxed.Extent;
020
021/**
022 * Access raw memory region as an input stream.
023 */
024public final class AddressInputStream extends InputStream {
025  /** Start address of memory region to be read */
026  private final Address startAddress;
027  /** Length of the memory region */
028  private final Extent length;
029  /** Offset to be read. Uses an Extent because the offset is always non-negative. */
030  private Extent offset;
031  /** Mark offset.  Uses an Extent because the offset is always non-negative. */
032  private Extent markOffset;
033
034  /**
035   *
036   * @param startAddress start address of the memory region
037   * @param length length of the region in bytes
038   */
039  public AddressInputStream(Address startAddress, Extent length) {
040    this.startAddress = startAddress;
041    this.length = length;
042
043    offset = Extent.zero();
044    markOffset = Extent.zero();
045  }
046
047  /** @return number of bytes that can be read */
048  @Override
049  public int available() {
050    int available = length.minus(offset).toInt();
051    available = (available > 0) ? available : 0;
052    return available;
053  }
054
055  /**
056   * Closing an AddressInputStream has no effect.
057   */
058  @Override
059  public void close() throws IOException {
060  }
061
062  /** Marks location. Read limit has no effect. */
063  @Override
064  public void mark(int readLimit) {
065    markOffset = offset;
066  }
067
068  /** Is mark/reset supported */
069  @Override
070  public boolean markSupported() {
071    return true;
072  }
073
074  /** Reads a byte */
075  @Override
076  public int read() {
077    if (offset.GE(length)) {
078      return -1;
079    }
080
081    Address readLocation = startAddress.plus(offset);
082    byte result = readLocation.loadByte();
083    offset = offset.plus(1);
084    return result & 0xFF;
085  }
086
087  /** Resets to mark */
088  @Override
089  public void reset() {
090    offset = markOffset;
091  }
092
093  /** Skips bytes (at most @code{Integer.MAX_VALUE} bytes) */
094  @Override
095  public long skip(long n) {
096    if (n < 0) {
097      return 0;
098    }
099
100    long maxInt = Integer.MAX_VALUE;
101    int skipAmount = (n > maxInt) ? Integer.MAX_VALUE : (int) n;
102    int available = available();
103    skipAmount = (skipAmount > available) ? available : skipAmount;
104
105    offset = offset.plus(skipAmount);
106    return skipAmount;
107  }
108}