Skip to content

Commit 1024954

Browse files
authored
Fix misuse of read and skipBytes (#2557)
* Fix misuse of read and skipBytes * improve exception messages * don't throw EOF exception if the stride steps outside the stream
1 parent 084deae commit 1024954

File tree

10 files changed

+146
-60
lines changed

10 files changed

+146
-60
lines changed

jme3-core/src/main/java/com/jme3/util/LittleEndien.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ public int read(byte[] buf) throws IOException {
6262
return in.read(buf);
6363
}
6464

65+
/**
66+
* Attempts(!) to read up to len bytes into buf at offset off.
67+
*/
6568
@Override
6669
public int read(byte[] buf, int off, int len) throws IOException {
6770
return in.read(buf, off, len);
@@ -142,14 +145,24 @@ public double readDouble() throws IOException {
142145

143146
@Override
144147
public void readFully(byte b[]) throws IOException {
145-
in.read(b, 0, b.length);
148+
readFully(b, 0, b.length);
146149
}
147150

148151
@Override
149152
public void readFully(byte b[], int off, int len) throws IOException {
150-
in.read(b, off, len);
153+
int totalRead = 0;
154+
while (totalRead < len) {
155+
int bytesRead = in.read(b, off + totalRead, len - totalRead);
156+
if (bytesRead < 0) {
157+
throw new EOFException("Reached end of stream before reading fully.");
158+
}
159+
totalRead += bytesRead;
160+
}
151161
}
152162

163+
/**
164+
* Attempts(!) to skip n bytes in the input stream.
165+
*/
153166
@Override
154167
public int skipBytes(int n) throws IOException {
155168
return (int) in.skip(n);

jme3-core/src/plugins/java/com/jme3/audio/plugins/WAVLoader.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import com.jme3.audio.AudioKey;
3939
import com.jme3.audio.AudioStream;
4040
import com.jme3.audio.SeekableStream;
41+
import com.jme3.export.binary.ByteUtils;
4142
import com.jme3.util.BufferUtils;
4243
import com.jme3.util.LittleEndien;
4344
import java.io.BufferedInputStream;
@@ -112,7 +113,7 @@ public void setTime(float time) {
112113
}
113114
InputStream newStream = info.openStream();
114115
try {
115-
newStream.skip(resetOffset);
116+
ByteUtils.skipFully(newStream, resetOffset);
116117
this.in = new BufferedInputStream(newStream);
117118
} catch (IOException ex) {
118119
// Resource could have gotten lost, etc.
@@ -172,7 +173,7 @@ private void readFormatChunk(int chunkSize, AudioData audioData) throws IOExcept
172173
// Skip any extra parameters in the format chunk (e.g., for non-PCM formats)
173174
int remainingChunkBytes = chunkSize - 16;
174175
if (remainingChunkBytes > 0) {
175-
in.skipBytes(remainingChunkBytes);
176+
ByteUtils.skipFully((InputStream)in, remainingChunkBytes);
176177
}
177178
}
178179

jme3-core/src/plugins/java/com/jme3/export/binary/ByteUtils.java

Lines changed: 80 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
package com.jme3.export.binary;
3333

3434
import java.io.ByteArrayOutputStream;
35+
import java.io.DataInput;
36+
import java.io.EOFException;
3537
import java.io.IOException;
3638
import java.io.InputStream;
3739
import java.io.OutputStream;
@@ -50,6 +52,77 @@ public class ByteUtils {
5052
private ByteUtils() {
5153
}
5254

55+
56+
public static void readFully(InputStream in, byte[] b) throws IOException {
57+
int bytesRead = 0;
58+
int read = 0;
59+
while (bytesRead < b.length && (read = in.read(b, bytesRead, b.length - bytesRead)) != -1) {
60+
bytesRead += read;
61+
}
62+
if (bytesRead < b.length) {
63+
throw new IOException(
64+
"End of stream reached prematurely after " + bytesRead + " of " + b.length + " bytes.");
65+
}
66+
}
67+
68+
69+
public static void skipFully(InputStream in, long n) throws IOException {
70+
skipFully(in, n, true);
71+
}
72+
73+
public static void skipFully(InputStream in, long n, boolean throwOnEOF) throws IOException {
74+
while (n > 0) {
75+
long skipped = in.skip(n);
76+
if (skipped > 0 && skipped <= n) { // skipped some bytes
77+
n -= skipped;
78+
} else if (skipped == 0) { // skipped nothing
79+
// distinguish between EOF and no bytes available
80+
if (in.read() == -1) {
81+
if (throwOnEOF) {
82+
throw new EOFException();
83+
} else {
84+
return;
85+
}
86+
} else {
87+
// stream was just hangling
88+
n--;
89+
}
90+
} else {
91+
throw new IOException(
92+
"Unable to skip exactly " + n + " bytes. Only " + skipped + " bytes were skipped.");
93+
}
94+
}
95+
}
96+
97+
public static void skipFully(DataInput in, int n) throws IOException {
98+
skipFully(in, n, true);
99+
}
100+
101+
public static void skipFully(DataInput in, int n, boolean throwOnEOF) throws IOException {
102+
while (n > 0) {
103+
long skipped = in.skipBytes(n);
104+
if (skipped > 0 && skipped <= n) { // skipped some bytes
105+
n -= skipped;
106+
} else if (skipped == 0) { // skipped nothing
107+
// distinguish between EOF and no bytes available
108+
try {
109+
in.readByte();
110+
} catch (EOFException e) {
111+
if (throwOnEOF) {
112+
throw e;
113+
} else {
114+
return;
115+
}
116+
}
117+
n--;
118+
} else {
119+
throw new IOException(
120+
"Unable to skip exactly " + n + " bytes. Only " + skipped + " bytes were skipped.");
121+
}
122+
}
123+
}
124+
125+
53126
/**
54127
* Takes an InputStream and returns the complete byte content of it
55128
*
@@ -125,7 +198,7 @@ public static short readShort(InputStream inputStream) throws IOException {
125198
byte[] byteArray = new byte[2];
126199

127200
// Read in the next 2 bytes
128-
inputStream.read(byteArray);
201+
readFully(inputStream, byteArray);
129202

130203
short number = convertShortFromBytes(byteArray);
131204

@@ -187,7 +260,7 @@ public static int readInt(InputStream inputStream) throws IOException {
187260
byte[] byteArray = new byte[4];
188261

189262
// Read in the next 4 bytes
190-
inputStream.read(byteArray);
263+
readFully(inputStream, byteArray);
191264

192265
int number = convertIntFromBytes(byteArray);
193266

@@ -263,7 +336,7 @@ public static long readLong(InputStream inputStream) throws IOException {
263336
byte[] byteArray = new byte[8];
264337

265338
// Read in the next 8 bytes
266-
inputStream.read(byteArray);
339+
readFully(inputStream, byteArray);
267340

268341
long number = convertLongFromBytes(byteArray);
269342

@@ -326,7 +399,7 @@ public static double readDouble(InputStream inputStream) throws IOException {
326399
byte[] byteArray = new byte[8];
327400

328401
// Read in the next 8 bytes
329-
inputStream.read(byteArray);
402+
readFully(inputStream, byteArray);
330403

331404
double number = convertDoubleFromBytes(byteArray);
332405

@@ -382,7 +455,7 @@ public static float readFloat(InputStream inputStream) throws IOException {
382455
byte[] byteArray = new byte[4];
383456

384457
// Read in the next 4 bytes
385-
inputStream.read(byteArray);
458+
readFully(inputStream, byteArray);
386459

387460
float number = convertFloatFromBytes(byteArray);
388461

@@ -440,7 +513,7 @@ public static boolean readBoolean(InputStream inputStream) throws IOException {
440513
byte[] byteArray = new byte[1];
441514

442515
// Read in the next byte
443-
inputStream.read(byteArray);
516+
readFully(inputStream, byteArray);
444517

445518
return convertBooleanFromBytes(byteArray);
446519
}
@@ -470,9 +543,7 @@ public static boolean convertBooleanFromBytes(byte[] byteArray, int offset) {
470543
* if bytes greater than the length of the store.
471544
*/
472545
public static byte[] readData(byte[] store, int bytes, InputStream is) throws IOException {
473-
for (int i = 0; i < bytes; i++) {
474-
store[i] = (byte)is.read();
475-
}
546+
readFully(is, store);
476547
return store;
477548
}
478549

jme3-core/src/plugins/java/com/jme3/texture/plugins/DDSLoader.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import com.jme3.asset.AssetInfo;
3535
import com.jme3.asset.AssetLoader;
3636
import com.jme3.asset.TextureKey;
37+
import com.jme3.export.binary.ByteUtils;
3738
import com.jme3.texture.Image;
3839
import com.jme3.texture.Image.Format;
3940
import com.jme3.texture.Texture;
@@ -160,7 +161,7 @@ private void loadDX10Header() throws IOException {
160161
}
161162
}
162163

163-
in.skipBytes(4); // skip reserved value
164+
ByteUtils.skipFully(in, 4); // skip reserved value
164165
}
165166

166167
private void setPixelFormat(int dxgiFormat) throws IOException {
@@ -227,13 +228,13 @@ private void loadHeader() throws IOException {
227228
pitchOrSize = in.readInt();
228229
depth = in.readInt();
229230
mipMapCount = in.readInt();
230-
in.skipBytes(44);
231+
ByteUtils.skipFully(in, 44);
231232
pixelFormat = null;
232233
directx10 = false;
233234
readPixelFormat();
234235
caps1 = in.readInt();
235236
caps2 = in.readInt();
236-
in.skipBytes(12);
237+
ByteUtils.skipFully(in, 12);
237238
texture3D = false;
238239

239240
if (!directx10) {
@@ -292,7 +293,7 @@ private void readPixelFormat() throws IOException {
292293
compressed = true;
293294
int fourcc = in.readInt();
294295
int swizzle = in.readInt();
295-
in.skipBytes(16);
296+
ByteUtils.skipFully(in, 16);
296297

297298
switch (fourcc) {
298299
case PF_DXT1:

jme3-core/src/plugins/java/com/jme3/texture/plugins/HDRLoader.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import com.jme3.asset.AssetInfo;
3535
import com.jme3.asset.AssetLoader;
3636
import com.jme3.asset.TextureKey;
37+
import com.jme3.export.binary.ByteUtils;
3738
import com.jme3.math.FastMath;
3839
import com.jme3.texture.Image;
3940
import com.jme3.texture.Image.Format;
@@ -180,16 +181,14 @@ private boolean decodeScanlineRLE(InputStream in, int width) throws IOException{
180181
return true;
181182
}
182183

183-
private boolean decodeScanlineUncompressed(InputStream in, int width) throws IOException{
184+
private void decodeScanlineUncompressed(InputStream in, int width) throws IOException{
184185
byte[] rgbe = new byte[4];
185186

186187
for (int i = 0; i < width; i+=3){
187-
if (in.read(rgbe) < 1)
188-
return false;
189-
188+
ByteUtils.readFully(in, rgbe);
190189
writeRGBE(rgbe);
191190
}
192-
return true;
191+
193192
}
194193

195194
private void decodeScanline(InputStream in, int width) throws IOException{
@@ -200,7 +199,7 @@ private void decodeScanline(InputStream in, int width) throws IOException{
200199

201200
// check format
202201
byte[] data = new byte[4];
203-
in.read(data);
202+
ByteUtils.readFully(in, data);
204203
if (data[0] != 0x02 || data[1] != 0x02 || (data[2] & 0x80) != 0){
205204
// not RLE data
206205
decodeScanlineUncompressed(in, width-1);

jme3-core/src/plugins/java/com/jme3/texture/plugins/PFMLoader.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import com.jme3.asset.AssetInfo;
3535
import com.jme3.asset.AssetLoader;
3636
import com.jme3.asset.TextureKey;
37+
import com.jme3.export.binary.ByteUtils;
3738
import com.jme3.texture.Image;
3839
import com.jme3.texture.Image.Format;
3940
import com.jme3.texture.image.ColorSpace;
@@ -115,12 +116,7 @@ private Image load(InputStream in, boolean needYFlip) throws IOException{
115116
if (!needYFlip)
116117
imageData.position(scanLineBytes * y);
117118

118-
int read = 0;
119-
int off = 0;
120-
do {
121-
read = in.read(scanline, off, scanline.length - off);
122-
off += read;
123-
} while (read > 0);
119+
ByteUtils.readFully(in, scanline);
124120

125121
if (needEndianFlip){
126122
flipScanline(scanline);

jme3-core/src/plugins/java/com/jme3/texture/plugins/TGALoader.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import com.jme3.asset.AssetInfo;
3535
import com.jme3.asset.AssetLoader;
3636
import com.jme3.asset.TextureKey;
37+
import com.jme3.export.binary.ByteUtils;
3738
import com.jme3.math.FastMath;
3839
import com.jme3.texture.Image;
3940
import com.jme3.texture.Image.Format;
@@ -158,7 +159,7 @@ public static Image load(InputStream in, boolean flip) throws IOException {
158159

159160
// Skip image ID
160161
if (idLength > 0) {
161-
dis.skip(idLength);
162+
ByteUtils.skipFully((InputStream) dis, idLength);
162163
}
163164

164165
ColorMapEntry[] cMapEntries = null;
@@ -168,7 +169,7 @@ public static Image load(InputStream in, boolean flip) throws IOException {
168169
int bitsPerColor = Math.min(cMapDepth / 3, 8);
169170

170171
byte[] cMapData = new byte[bytesInColorMap];
171-
dis.read(cMapData);
172+
ByteUtils.readFully((InputStream) dis, cMapData);
172173

173174
// Only go to the trouble of constructing the color map
174175
// table if this is declared a color mapped image.

jme3-core/src/plugins/java/com/jme3/texture/plugins/ktx/KTXLoader.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import com.jme3.asset.AssetInfo;
3535
import com.jme3.asset.AssetLoader;
3636
import com.jme3.asset.TextureKey;
37+
import com.jme3.export.binary.ByteUtils;
3738
import com.jme3.renderer.Caps;
3839
import com.jme3.renderer.opengl.GLImageFormat;
3940
import com.jme3.renderer.opengl.GLImageFormats;
@@ -95,7 +96,7 @@ private Image load(InputStream stream) {
9596

9697
DataInput in = new DataInputStream(stream);
9798
try {
98-
stream.read(fileId, 0, 12);
99+
ByteUtils.readFully(stream, fileId);
99100
if (!checkFileIdentifier(fileId)) {
100101
throw new IllegalArgumentException("Unrecognized ktx file identifier : " + new String(fileId) + " should be " + new String(fileIdentifier));
101102
}
@@ -193,13 +194,13 @@ private Image load(InputStream stream) {
193194
}
194195
//cube padding
195196
if (numberOfFaces == 6 && numberOfArrayElements == 0) {
196-
in.skipBytes(3 - ((nbPixelRead + 3) % 4));
197+
ByteUtils.skipFully(in, 3 - ((nbPixelRead + 3) % 4));
197198
}
198199
}
199200
}
200201
//mip padding
201202
log.log(Level.FINE, "skipping {0}", (3 - ((imageSize + 3) % 4)));
202-
in.skipBytes(3 - ((imageSize + 3) % 4));
203+
ByteUtils.skipFully(in, 3 - ((imageSize + 3) % 4));
203204
offset+=imageSize;
204205
}
205206
//there are loaded mip maps we set the sizes
@@ -305,7 +306,7 @@ private PixelReader parseMetaData(int bytesOfKeyValueData, DataInput in) throws
305306
//padding
306307
int padding = 3 - ((keyAndValueByteSize + 3) % 4);
307308
if (padding > 0) {
308-
in.skipBytes(padding);
309+
ByteUtils.skipFully(in, padding);
309310
}
310311
i += 4 + keyAndValueByteSize + padding;
311312
}

0 commit comments

Comments
 (0)