From: Josh Micich Date: Sun, 5 Oct 2008 04:43:48 +0000 (+0000) Subject: Better bounds checking in RecordInputStream. Removed rarely used methods readShortArr... X-Git-Tag: REL_3_2_FINAL~12 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=794d4051bb8b8d74e2eeeb5b1c05dd7bcc307501;p=poi.git Better bounds checking in RecordInputStream. Removed rarely used methods readShortArray and putShortArray git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@701747 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/org/apache/poi/hssf/record/RecordInputStream.java b/src/java/org/apache/poi/hssf/record/RecordInputStream.java index fe6a4b2ea3..c23868c711 100755 --- a/src/java/org/apache/poi/hssf/record/RecordInputStream.java +++ b/src/java/org/apache/poi/hssf/record/RecordInputStream.java @@ -33,7 +33,7 @@ public class RecordInputStream extends InputStream { /** Maximum size of a single record (minus the 4 byte header) without a continue*/ public final static short MAX_RECORD_DATA_SIZE = 8224; private static final int INVALID_SID_VALUE = -1; - + private InputStream in; protected short currentSid; protected short currentLength = -1; @@ -42,34 +42,34 @@ public class RecordInputStream extends InputStream { protected byte[] data = new byte[MAX_RECORD_DATA_SIZE]; protected short recordOffset; protected long pos; - + private boolean autoContinue = true; - public RecordInputStream(InputStream in) throws RecordFormatException { + public RecordInputStream(InputStream in) throws RecordFormatException { this.in = in; try { nextSid = LittleEndian.readShort(in); - //Dont increment the pos just yet (technically we are at the start of - //the record stream until nextRecord is called). + //Don't increment the pos just yet (technically we are at the start of + //the record stream until nextRecord is called). } catch (IOException ex) { throw new RecordFormatException("Error reading bytes", ex); } } - - /** This method will read a byte from the current record*/ - public int read() { - checkRecordPosition(); - - byte result = data[recordOffset]; - recordOffset += 1; - pos += 1; - return result; - } - + + /** This method will read a byte from the current record*/ + public int read() { + checkRecordPosition(LittleEndian.BYTE_SIZE); + + byte result = data[recordOffset]; + recordOffset += LittleEndian.BYTE_SIZE; + pos += LittleEndian.BYTE_SIZE; + return result; + } + public short getSid() { return currentSid; } - + public short getLength() { return currentLength; } @@ -85,12 +85,11 @@ public class RecordInputStream extends InputStream { public boolean hasNextRecord() { return nextSid != INVALID_SID_VALUE; } - + /** Moves to the next record in the stream. - * + * * Note: The auto continue flag is reset to true */ - public void nextRecord() throws RecordFormatException { if ((currentLength != -1) && (currentLength != recordOffset)) { System.out.println("WARN. Unread "+remaining()+" bytes of record 0x"+Integer.toHexString(currentSid)); @@ -100,7 +99,7 @@ public class RecordInputStream extends InputStream { autoContinue = true; try { recordOffset = 0; - currentLength = LittleEndian.readShort(in); + currentLength = LittleEndian.readShort(in); if (currentLength > MAX_RECORD_DATA_SIZE) throw new RecordFormatException("The content of an excel record cannot exceed "+MAX_RECORD_DATA_SIZE+" bytes"); pos += LittleEndian.SHORT_SIZE; @@ -113,138 +112,124 @@ public class RecordInputStream extends InputStream { // ex45582-22397.xls has one extra byte after the last record // Excel reads that file OK } - nextSid = INVALID_SID_VALUE; + nextSid = INVALID_SID_VALUE; } else { nextSid = LittleEndian.readShort(in); if (nextSid == INVALID_SID_VALUE) { throw new RecordFormatException("Found sid " + nextSid + " after record with sid 0x" + Integer.toHexString(currentSid).toUpperCase()); } - } + } } catch (IOException ex) { throw new RecordFormatException("Error reading bytes", ex); } } - + public void setAutoContinue(boolean enable) { - this.autoContinue = enable; + this.autoContinue = enable; } - + public boolean getAutoContinue() { return autoContinue; } - - protected void checkRecordPosition() { - if (remaining() <= 0) { - if (isContinueNext() && autoContinue) { - nextRecord(); - } - else throw new ArrayIndexOutOfBoundsException(); - } - } - - /** - * Reads an 8 bit, signed value - */ - public byte readByte() { - checkRecordPosition(); - - byte result = data[recordOffset]; - recordOffset += 1; - pos += 1; - return result; - } - - /** - * Reads a 16 bit, signed value - */ - public short readShort() { - checkRecordPosition(); - - short result = LittleEndian.getShort(data, recordOffset); - recordOffset += LittleEndian.SHORT_SIZE; - pos += LittleEndian.SHORT_SIZE; - return result; - } - public int readInt() { - checkRecordPosition(); - - int result = LittleEndian.getInt(data, recordOffset); - recordOffset += LittleEndian.INT_SIZE; - pos += LittleEndian.INT_SIZE; - return result; - } + private void checkRecordPosition(int requiredByteCount) { - public long readLong() { - checkRecordPosition(); - - long result = LittleEndian.getLong(data, recordOffset); - recordOffset += LittleEndian.LONG_SIZE; - pos += LittleEndian.LONG_SIZE; - return result; - } + if (remaining() < requiredByteCount) { + if (isContinueNext() && autoContinue) { + nextRecord(); + } else { + throw new ArrayIndexOutOfBoundsException(); + } + } + } - /** - * Reads an 8 bit, unsigned value - */ - public short readUByte() { - short s = readByte(); - if(s < 0) { - s += 256; - } - return s; - } + /** + * Reads an 8 bit, signed value + */ + public byte readByte() { + checkRecordPosition(LittleEndian.BYTE_SIZE); - /** - * Reads a 16 bit,un- signed value. - * @return - */ - public int readUShort() { - checkRecordPosition(); - - int result = LittleEndian.getUShort(data, recordOffset); - recordOffset += LittleEndian.SHORT_SIZE; - pos += LittleEndian.SHORT_SIZE; - return result; - } + byte result = data[recordOffset]; + recordOffset += LittleEndian.BYTE_SIZE; + pos += LittleEndian.BYTE_SIZE; + return result; + } - public double readDouble() { - checkRecordPosition(); - long valueLongBits = LittleEndian.getLong(data, recordOffset); - double result = Double.longBitsToDouble(valueLongBits); - if (Double.isNaN(result)) { - throw new RuntimeException("Did not expect to read NaN"); - } - recordOffset += LittleEndian.DOUBLE_SIZE; - pos += LittleEndian.DOUBLE_SIZE; - return result; - } + /** + * Reads a 16 bit, signed value + */ + public short readShort() { + checkRecordPosition(LittleEndian.SHORT_SIZE); - - public short[] readShortArray() { - checkRecordPosition(); - - short[] arr = LittleEndian.getShortArray(data, recordOffset); - final int size = (2 * (arr.length +1)); - recordOffset += size; - pos += size; - - return arr; - } - - /** - * given a byte array of 16-bit unicode characters, compress to 8-bit and - * return a string - * - * { 0x16, 0x00 } -0x16 - * + short result = LittleEndian.getShort(data, recordOffset); + recordOffset += LittleEndian.SHORT_SIZE; + pos += LittleEndian.SHORT_SIZE; + return result; + } + + public int readInt() { + checkRecordPosition(LittleEndian.INT_SIZE); + + int result = LittleEndian.getInt(data, recordOffset); + recordOffset += LittleEndian.INT_SIZE; + pos += LittleEndian.INT_SIZE; + return result; + } + + public long readLong() { + checkRecordPosition(LittleEndian.LONG_SIZE); + + long result = LittleEndian.getLong(data, recordOffset); + recordOffset += LittleEndian.LONG_SIZE; + pos += LittleEndian.LONG_SIZE; + return result; + } + + /** + * Reads an 8 bit, unsigned value + */ + public short readUByte() { + return (short) (readByte() & 0x00FF); + } + + /** + * Reads a 16 bit, unsigned value. + * @return + */ + public int readUShort() { + checkRecordPosition(LittleEndian.SHORT_SIZE); + + int result = LittleEndian.getUShort(data, recordOffset); + recordOffset += LittleEndian.SHORT_SIZE; + pos += LittleEndian.SHORT_SIZE; + return result; + } + + public double readDouble() { + checkRecordPosition(LittleEndian.DOUBLE_SIZE); + long valueLongBits = LittleEndian.getLong(data, recordOffset); + double result = Double.longBitsToDouble(valueLongBits); + if (Double.isNaN(result)) { + throw new RuntimeException("Did not expect to read NaN"); // (Because Excel typically doesn't write NaN + } + recordOffset += LittleEndian.DOUBLE_SIZE; + pos += LittleEndian.DOUBLE_SIZE; + return result; + } + + /** + * given a byte array of 16-bit unicode characters, compress to 8-bit and + * return a string + * + * { 0x16, 0x00 } -0x16 + * * @param length the length of the final string * @return the converted string * @exception IllegalArgumentException if len is too large (i.e., - * there is not enough data in string to create a String of that - * length) - */ + * there is not enough data in string to create a String of that + * length) + */ public String readUnicodeLEString(int length) { if ((length < 0) || (((remaining() / 2) < length) && !isContinueNext())) { throw new IllegalArgumentException("Illegal length - asked for " + length + " but only " + (remaining()/2) + " left!"); @@ -258,11 +243,11 @@ public class RecordInputStream extends InputStream { if(compressByte != 1) throw new IllegalArgumentException("compressByte in continue records must be 1 while reading unicode LE string"); } char ch = (char)readShort(); - buf.append(ch); + buf.append(ch); } return buf.toString(); } - + public String readCompressedUnicode(int length) { if ((length < 0) || ((remaining() < length) && !isContinueNext())) { throw new IllegalArgumentException("Illegal length " + length); @@ -277,23 +262,23 @@ public class RecordInputStream extends InputStream { } byte b = readByte(); char ch = (char)(0x00FF & b); // avoid sex - buf.append(ch); + buf.append(ch); } - return buf.toString(); + return buf.toString(); } - + /** Returns an excel style unicode string from the bytes reminaing in the record. * Note: Unicode strings differ from normal strings due to the addition of * formatting information. - * + * * @return The unicode string representation of the remaining bytes. */ public UnicodeString readUnicodeString() { return new UnicodeString(this); } - + /** Returns the remaining bytes for the current record. - * + * * @return The remaining bytes of the current record. */ public byte[] readRemainder() { @@ -304,39 +289,39 @@ public class RecordInputStream extends InputStream { pos += size; return result; } - + /** Reads all byte data for the current record, including any * that overlaps into any following continue records. - * + * * @deprecated Best to write a input stream that wraps this one where there is * special sub record that may overlap continue records. - */ + */ public byte[] readAllContinuedRemainder() { //Using a ByteArrayOutputStream is just an easy way to get a //growable array of the data. ByteArrayOutputStream out = new ByteArrayOutputStream(2*MAX_RECORD_DATA_SIZE); while (isContinueNext()) { - byte[] b = readRemainder(); + byte[] b = readRemainder(); out.write(b, 0, b.length); nextRecord(); } - byte[] b = readRemainder(); - out.write(b, 0, b.length); - + byte[] b = readRemainder(); + out.write(b, 0, b.length); + return out.toByteArray(); } /** The remaining number of bytes in the current record. - * + * * @return The number of bytes remaining in the current record */ public int remaining() { return (currentLength - recordOffset); } - /** Returns true iif a Continue record is next in the excel stream - * + /** Returns true iif a Continue record is next in the excel stream + * * @return True when a ContinueRecord is next. */ public boolean isContinueNext() { diff --git a/src/java/org/apache/poi/hssf/record/SeriesListRecord.java b/src/java/org/apache/poi/hssf/record/SeriesListRecord.java index 2894f15877..4753eae05f 100644 --- a/src/java/org/apache/poi/hssf/record/SeriesListRecord.java +++ b/src/java/org/apache/poi/hssf/record/SeriesListRecord.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,66 +14,66 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - package org.apache.poi.hssf.record; - - -import org.apache.poi.util.*; +import org.apache.poi.util.LittleEndian; /** - * The series list record defines the series displayed as an overlay to the main chart record. - * NOTE: This source is automatically generated please do not modify this file. Either subclass or - * remove the record in src/records/definitions. - + * + * The series list record defines the series displayed as an overlay to the main chart record.
+ * TODO - does this record (0x1016) really exist. It doesn't seem to be referenced in either the OOO or MS doc + * * @author Glen Stampoultzis (glens at apache.org) */ -public class SeriesListRecord - extends Record -{ - public final static short sid = 0x1016; +public final class SeriesListRecord extends Record { + public final static short sid = 0x1016; private short[] field_1_seriesNumbers; - - public SeriesListRecord() - { - + public SeriesListRecord(short[] seriesNumbers) { + field_1_seriesNumbers = seriesNumbers; } - public SeriesListRecord(RecordInputStream in) - { - field_1_seriesNumbers = in.readShortArray(); + public SeriesListRecord(RecordInputStream in) { + int nItems = in.readUShort(); + short[] ss = new short[nItems]; + for (int i = 0; i < nItems; i++) { + ss[i] = in.readShort(); + + } + field_1_seriesNumbers = ss; } - public String toString() - { + public String toString() { StringBuffer buffer = new StringBuffer(); buffer.append("[SERIESLIST]\n"); - buffer.append(" .seriesNumbers = ") - .append(" (").append( getSeriesNumbers() ).append(" )"); - buffer.append(System.getProperty("line.separator")); + buffer.append(" .seriesNumbers= ").append(" (").append( getSeriesNumbers() ).append(" )"); + buffer.append("\n"); buffer.append("[/SERIESLIST]\n"); return buffer.toString(); } - public int serialize(int offset, byte[] data) - { - int pos = 0; + public int serialize(int offset, byte[] data) { - LittleEndian.putShort(data, 0 + offset, sid); - LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize() - 4)); + int nItems = field_1_seriesNumbers.length; + int dataSize = 2 + 2 * nItems; + + LittleEndian.putUShort(data, 0 + offset, sid); + LittleEndian.putUShort(data, 2 + offset, dataSize); - LittleEndian.putShortArray(data, 4 + offset + pos, field_1_seriesNumbers); + LittleEndian.putUShort(data, 4 + offset, nItems); + + int pos = offset + 6; + for (int i = 0; i < nItems; i++) { + LittleEndian.putUShort(data, pos, field_1_seriesNumbers[i]); + pos += 2; + } - return getRecordSize(); + return 4 + dataSize; } - /** - * Size of record (exluding 4 byte header) - */ public int getRecordSize() { return 4 + field_1_seriesNumbers.length * 2 + 2; @@ -86,34 +85,23 @@ public class SeriesListRecord } public Object clone() { - SeriesListRecord rec = new SeriesListRecord(); - - rec.field_1_seriesNumbers = field_1_seriesNumbers; - return rec; + return new SeriesListRecord((short[]) field_1_seriesNumbers.clone()); } - - - /** * Get the series numbers field for the SeriesList record. */ - public short[] getSeriesNumbers() - { + public short[] getSeriesNumbers() { return field_1_seriesNumbers; } /** * Set the series numbers field for the SeriesList record. */ - public void setSeriesNumbers(short[] field_1_seriesNumbers) - { + public void setSeriesNumbers(short[] field_1_seriesNumbers) { this.field_1_seriesNumbers = field_1_seriesNumbers; } - - -} // END OF CLASS - +} diff --git a/src/java/org/apache/poi/util/LittleEndian.java b/src/java/org/apache/poi/util/LittleEndian.java index 6838eb2aa4..373710b47a 100644 --- a/src/java/org/apache/poi/util/LittleEndian.java +++ b/src/java/org/apache/poi/util/LittleEndian.java @@ -250,20 +250,6 @@ public final class LittleEndian implements LittleEndianConsts { putNumber(data, offset, value, LittleEndianConsts.BYTE_SIZE); } - /** - * put a array of shorts into a byte array - * - *@param data the byte array - *@param offset a starting offset into the byte array - *@param value the short array - */ - public static void putShortArray(final byte[] data, final int offset, final short[] value) { - putNumber(data, offset, value.length, SHORT_SIZE); - for (int i = 0; i < value.length; i++) { - putNumber(data, offset + 2 + (i * 2), value[i], SHORT_SIZE); - } - } - /** * put an unsigned short value into a byte array * diff --git a/src/testcases/org/apache/poi/hssf/record/TestSeriesListRecord.java b/src/testcases/org/apache/poi/hssf/record/TestSeriesListRecord.java index 7e81f29280..027b6c3867 100644 --- a/src/testcases/org/apache/poi/hssf/record/TestSeriesListRecord.java +++ b/src/testcases/org/apache/poi/hssf/record/TestSeriesListRecord.java @@ -29,7 +29,7 @@ import junit.framework.TestCase; * @author Glen Stampoultzis (glens at apache.org) */ public final class TestSeriesListRecord extends TestCase { - byte[] data = new byte[] { + private static final byte[] data = { (byte)0x02,(byte)0x00,(byte)0x01,(byte)0x20,(byte)0xff,(byte)0xf0 }; @@ -43,10 +43,8 @@ public final class TestSeriesListRecord extends TestCase { assertEquals( 4 + 6, record.getRecordSize() ); } - public void testStore() - { - SeriesListRecord record = new SeriesListRecord(); - record.setSeriesNumbers( new short[] { (short)0x2001, (short)0xf0ff } ); + public void testStore() { + SeriesListRecord record = new SeriesListRecord(new short[] { (short)0x2001, (short)0xf0ff } ); byte [] recordBytes = record.serialize(); assertEquals(recordBytes.length - 4, data.length);