In Java, data is stored in big-endian format (also called network order). That is, all data is represented sequentially starting from the most significant bit to the least significant. JDK versions prior to JDK 1.4 required definition of custom methods that manage reversing byte order to maintain compatibility with little-endian systems. Correct handling of byte order-related issues is critical when exchanging data in a networked environment that includes both big-endian and little-endian machines or when working with other languages via using JNI. Failure to handle byte-ordering issues can cause unexpected program behavior.
...
The read methods (readByte(), readShort(), readInt(), readLong(), readFloat()
, and readDouble()
) and the corresponding write methods defined by class java.io.DataInputStream
and class java.io.DataOutputStream
operate only on big-endian data. Use of these methods while interoperating with traditional languages, such as C or C++, is insecure because such languages lack any guarantees about endianness. This noncompliant code example shows such a discrepancy.
Code Block | ||
---|---|---|
| ||
try { DataInputStream dis = null; try { dis = new DataInputStream( new FileInputStream("data")); // Little-endian data might be read as big-endian int serialNumber = dis.readInt(); } catch (IOException x) { // handle error } finally { if (dis != null) { try { dis.close(); } } catch (IOException xe) { // handle error } } } |
Compliant Solution (ByteBuffer
)
Wiki Markup |
---|
This compliant solution uses methods provided by class {{ByteBuffer}} (see \[[API 2006|AA. Bibliography#API 06]\] [{{ByteBuffer}}|http://download.oracle.com/javase/6/docs/api/java/nio/ByteBuffer.html]) to correctly extract an {{int}} from the original input value. It wraps the input byte array with a {{ByteBuffer}}, sets the byte order to little-endian, and extracts the {{int}}. The result is stored in the integer {{serialNumber}}. Class {{ByteBuffer}} provides analogous get and put methods for other numeric types. |
Code Block | ||
---|---|---|
| ||
try { DataInputStream dis = null; try { dis = new DataInputStream( new FileInputStream("data")); byte[] buffer = new byte[4]; int bytesRead = dis.read(buffer); // Bytes are read into buffer if (bytesRead != 4) { throw new IOException("Unexpected End of Stream"); } int serialNumber = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).getInt(); } finally { if (dis != null) { try { dis.close(); } catch (IOException x) { // handle error } } } } catch (IOException x) { // handle error } |
...
Compliant Solution (Define Special-Purpose Methods)
...
Code Block | ||
---|---|---|
| ||
public static int reverse(int i) {
return Integer.reverseBytes(i);
}
|
...
Reading and writing data without considering endianness can lead to serious misinterpretations of both the magnitude and sign of the data.
...
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="630f83c93b03a9e2-d3eb749e-45c24285-be608c1f-ff67fbfe9a1692c4c2aad804"><ac:plain-text-body><![CDATA[ | [[API 2006 | AA. Bibliography#API 06]] | [Class | http://download.oracle.com/javase/6/docs/api/java/nio/ByteBuffer.html]: Methods | http://download.oracle.com/javase/6/docs/api/java/lang/Integer.html]: method | ]]></ac:plain-text-body></ac:structured-macro> |
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="b5c7e2f596ab3fc5-bda7484e-491f44a1-bbcfbb88-9bc0af4ac13964d0b0cce5d5"><ac:plain-text-body><![CDATA[ | [[Cohen 1981 | AA. Bibliography#Cohen 81]] | On Holy Wars and a Plea for Peace | ]]></ac:plain-text-body></ac:structured-macro> | ||
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="7242a2c7a2509034-3f5a210b-45314d86-8c00a7b7-ad2c6c93709ab9878fe543d5"><ac:plain-text-body><![CDATA[ | [[Harold 1997 | AA. Bibliography#Harold 97]] | Chapter 2, Primitive Data Types, Cross-Platform Issues | ]]></ac:plain-text-body></ac:structured-macro> |
...