| /* ==================================================================== |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You 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 org.apache.poi.util; |
| |
| import java.io.BufferedInputStream; |
| import java.io.FilterInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| |
| /** |
| * Wraps an {@link InputStream} providing {@link LittleEndianInput}<p> |
| * |
| * This class does not buffer any input, so the stream read position maintained |
| * by this class is consistent with that of the inner stream. |
| */ |
| public class LittleEndianInputStream extends FilterInputStream implements LittleEndianInput { |
| |
| private static final int BUFFERED_SIZE = 8096; |
| |
| private static final int EOF = -1; |
| private int readIndex = 0; |
| private int markIndex = -1; |
| |
| public LittleEndianInputStream(InputStream is) { |
| super(is.markSupported() ? is : new BufferedInputStream(is, BUFFERED_SIZE)); |
| } |
| |
| @Override |
| @SuppressForbidden("just delegating") |
| public int available() { |
| try { |
| return super.available(); |
| } catch (IOException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| @Override |
| public byte readByte() { |
| return (byte)readUByte(); |
| } |
| |
| @Override |
| public int readUByte() { |
| byte[] buf = new byte[1]; |
| try { |
| checkEOF(read(buf), 1); |
| } catch (IOException e) { |
| throw new RuntimeException(e); |
| } |
| return LittleEndian.getUByte(buf); |
| } |
| |
| /** |
| * get a float value, reads it in little endian format |
| * then converts the resulting revolting IEEE 754 (curse them) floating |
| * point number to a happy java float |
| * |
| * @return the float (32-bit) value |
| */ |
| public float readFloat() { |
| return Float.intBitsToFloat( readInt() ); |
| } |
| |
| @Override |
| public double readDouble() { |
| return Double.longBitsToDouble(readLong()); |
| } |
| |
| @Override |
| public int readInt() { |
| byte[] buf = new byte[LittleEndianConsts.INT_SIZE]; |
| try { |
| checkEOF(read(buf), buf.length); |
| } catch (IOException e) { |
| throw new RuntimeException(e); |
| } |
| return LittleEndian.getInt(buf); |
| } |
| |
| /** |
| * get an unsigned int value from an InputStream |
| * |
| * @return the unsigned int (32-bit) value |
| * @exception RuntimeException |
| * wraps any IOException thrown from reading the stream. |
| */ |
| //@Override |
| public long readUInt() { |
| long retNum = readInt(); |
| return retNum & 0x00FFFFFFFFL; |
| } |
| |
| @Override |
| public long readLong() { |
| byte[] buf = new byte[LittleEndianConsts.LONG_SIZE]; |
| try { |
| checkEOF(read(buf), LittleEndianConsts.LONG_SIZE); |
| } catch (IOException e) { |
| throw new RuntimeException(e); |
| } |
| return LittleEndian.getLong(buf); |
| } |
| |
| @Override |
| public short readShort() { |
| return (short)readUShort(); |
| } |
| |
| @Override |
| public int readUShort() { |
| byte[] buf = new byte[LittleEndianConsts.SHORT_SIZE]; |
| try { |
| checkEOF(read(buf), LittleEndianConsts.SHORT_SIZE); |
| } catch (IOException e) { |
| throw new RuntimeException(e); |
| } |
| return LittleEndian.getUShort(buf); |
| } |
| |
| private static void checkEOF(int actualBytes, int expectedBytes) { |
| if (expectedBytes != 0 && (actualBytes == -1 || actualBytes != expectedBytes)) { |
| throw new RuntimeException("Unexpected end-of-file"); |
| } |
| } |
| |
| @Override |
| public void readFully(byte[] buf) { |
| readFully(buf, 0, buf.length); |
| } |
| |
| @Override |
| public void readFully(byte[] buf, int off, int len) { |
| try { |
| checkEOF(_read(buf, off, len), len); |
| } catch (IOException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| @Override |
| public int read(byte[] b, int off, int len) throws IOException { |
| int readBytes = super.read(b, off, len); |
| readIndex += readBytes; |
| return readBytes; |
| } |
| |
| @Override |
| public synchronized void mark(int readlimit) { |
| super.mark(readlimit); |
| markIndex = readIndex; |
| } |
| |
| @Override |
| public synchronized void reset() throws IOException { |
| super.reset(); |
| if (markIndex > -1) { |
| readIndex = markIndex; |
| markIndex = -1; |
| } |
| } |
| |
| public int getReadIndex() { |
| return readIndex; |
| } |
| |
| |
| |
| //Makes repeated calls to super.read() until length is read or EOF is reached |
| private int _read(byte[] buffer, int offset, int length) throws IOException { |
| //lifted directly from org.apache.commons.io.IOUtils 2.4 |
| int remaining = length; |
| while (remaining > 0) { |
| int location = length - remaining; |
| int count = read(buffer, offset + location, remaining); |
| if (EOF == count) { |
| break; |
| } |
| remaining -= count; |
| } |
| |
| return length - remaining; |
| } |
| |
| @Override |
| public void readPlain(byte[] buf, int off, int len) { |
| readFully(buf, off, len); |
| } |
| |
| |
| public void skipFully(int len) throws IOException { |
| if (len == 0) { |
| return; |
| } |
| long skipped = IOUtils.skipFully(this, len); |
| if (skipped > Integer.MAX_VALUE) { |
| throw new IOException("can't skip further than "+Integer.MAX_VALUE); |
| } |
| checkEOF((int)skipped, len); |
| } |
| } |