001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.io; 018 019import static org.apache.commons.io.IOUtils.EOF; 020 021import java.io.EOFException; 022import java.io.IOException; 023import java.io.InputStream; 024import java.io.OutputStream; 025 026/** 027 * Helps with reading and writing primitive numeric types ({@code short}, 028 * {@code int}, {@code long}, {@code float}, and {@code double}) that are 029 * encoded in little endian using two's complement or unsigned representations. 030 * <p> 031 * Different computer architectures have different conventions for 032 * byte ordering. In "Little Endian" architectures (e.g. X86), 033 * the low-order byte is stored in memory at the lowest address, and 034 * subsequent bytes at higher addresses. In "Big Endian" architectures 035 * (e.g. Motorola 680X0), the situation is reversed. 036 * Most methods and classes throughout Java — e.g. {@code DataInputStream} and 037 * {@code Double.longBitsToDouble()} — assume data is laid out 038 * in big endian order with the most significant byte first. 039 * The methods in this class read and write data in little endian order, 040 * generally by reversing the bytes and then using the 041 * regular Java methods to convert the swapped bytes to a primitive type. 042 * </p> 043 * <p> 044 * Provenance: Excalibur 045 * </p> 046 * 047 * @see org.apache.commons.io.input.SwappedDataInputStream 048 */ 049public class EndianUtils { 050 051 /** 052 * Reads the next byte from the input stream. 053 * @param input the stream 054 * @return the byte 055 * @throws IOException if the end of file is reached 056 */ 057 private static int read(final InputStream input) throws IOException { 058 final int value = input.read(); 059 if (EOF == value) { 060 throw new EOFException("Unexpected EOF reached"); 061 } 062 return value; 063 } 064 065 /** 066 * Reads a little endian {@code double} value from a byte array at a given offset. 067 * 068 * @param data source byte array 069 * @param offset starting offset in the byte array 070 * @return the value read 071 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 8 bytes 072 */ 073 public static double readSwappedDouble(final byte[] data, final int offset) { 074 return Double.longBitsToDouble(readSwappedLong(data, offset)); 075 } 076 077 /** 078 * Reads a little endian {@code double} value from an InputStream. 079 * 080 * @param input source InputStream 081 * @return the value just read 082 * @throws IOException in case of an I/O problem 083 */ 084 public static double readSwappedDouble(final InputStream input) throws IOException { 085 return Double.longBitsToDouble(readSwappedLong(input)); 086 } 087 088 /** 089 * Reads a little endian {@code float} value from a byte array at a given offset. 090 * 091 * @param data source byte array 092 * @param offset starting offset in the byte array 093 * @return the value read 094 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes 095 */ 096 public static float readSwappedFloat(final byte[] data, final int offset) { 097 return Float.intBitsToFloat(readSwappedInteger(data, offset)); 098 } 099 100 /** 101 * Reads a little endian {@code float} value from an InputStream. 102 * 103 * @param input source InputStream 104 * @return the value just read 105 * @throws IOException in case of an I/O problem 106 */ 107 public static float readSwappedFloat(final InputStream input) throws IOException { 108 return Float.intBitsToFloat(readSwappedInteger(input)); 109 } 110 111 /** 112 * Reads a little endian {@code int} value from a byte array at a given offset. 113 * 114 * @param data source byte array 115 * @param offset starting offset in the byte array 116 * @return the value read 117 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes 118 */ 119 public static int readSwappedInteger(final byte[] data, final int offset) { 120 validateByteArrayOffset(data, offset, Integer.SIZE / Byte.SIZE); 121 return ((data[offset + 0] & 0xff) << 0) + 122 ((data[offset + 1] & 0xff) << 8) + 123 ((data[offset + 2] & 0xff) << 16) + 124 ((data[offset + 3] & 0xff) << 24); 125 } 126 127 /** 128 * Reads a little endian {@code int} value from an InputStream. 129 * 130 * @param input source InputStream 131 * @return the value just read 132 * @throws IOException in case of an I/O problem 133 */ 134 public static int readSwappedInteger(final InputStream input) throws IOException { 135 final int value1 = read(input); 136 final int value2 = read(input); 137 final int value3 = read(input); 138 final int value4 = read(input); 139 return ((value1 & 0xff) << 0) + ((value2 & 0xff) << 8) + ((value3 & 0xff) << 16) + ((value4 & 0xff) << 24); 140 } 141 142 /** 143 * Reads a little endian {@code long} value from a byte array at a given offset. 144 * 145 * @param data source byte array 146 * @param offset starting offset in the byte array 147 * @return the value read 148 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 8 bytes 149 */ 150 public static long readSwappedLong(final byte[] data, final int offset) { 151 validateByteArrayOffset(data, offset, Long.SIZE / Byte.SIZE); 152 final long low = readSwappedInteger(data, offset); 153 final long high = readSwappedInteger(data, offset + 4); 154 return (high << 32) + (0xffffffffL & low); 155 } 156 157 /** 158 * Reads a little endian {@code long} value from an InputStream. 159 * 160 * @param input source InputStream 161 * @return the value just read 162 * @throws IOException in case of an I/O problem 163 */ 164 public static long readSwappedLong(final InputStream input) throws IOException { 165 final byte[] bytes = new byte[8]; 166 for (int i = 0; i < 8; i++) { 167 bytes[i] = (byte) read(input); 168 } 169 return readSwappedLong(bytes, 0); 170 } 171 172 /** 173 * Reads a little endian {@code short} value from a byte array at a given offset. 174 * 175 * @param data source byte array 176 * @param offset starting offset in the byte array 177 * @return the value read 178 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 2 bytes 179 */ 180 public static short readSwappedShort(final byte[] data, final int offset) { 181 validateByteArrayOffset(data, offset, Short.SIZE / Byte.SIZE); 182 return (short) (((data[offset + 0] & 0xff) << 0) + ((data[offset + 1] & 0xff) << 8)); 183 } 184 185 /** 186 * Reads a little endian {@code short} value from an InputStream. 187 * 188 * @param input source InputStream 189 * @return the value just read 190 * @throws IOException in case of an I/O problem 191 */ 192 public static short readSwappedShort(final InputStream input) throws IOException { 193 return (short) (((read(input) & 0xff) << 0) + ((read(input) & 0xff) << 8)); 194 } 195 196 /** 197 * Reads a little endian unsigned integer (32-bit) value from a byte array at a given 198 * offset. 199 * 200 * @param data source byte array 201 * @param offset starting offset in the byte array 202 * @return the value read 203 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes 204 */ 205 public static long readSwappedUnsignedInteger(final byte[] data, final int offset) { 206 validateByteArrayOffset(data, offset, Integer.SIZE / Byte.SIZE); 207 final long low = ((data[offset + 0] & 0xff) << 0) + 208 ((data[offset + 1] & 0xff) << 8) + 209 ((data[offset + 2] & 0xff) << 16); 210 final long high = data[offset + 3] & 0xff; 211 return (high << 24) + (0xffffffffL & low); 212 } 213 214 /** 215 * Reads a little endian unsigned integer (32-bit) from an InputStream. 216 * 217 * @param input source InputStream 218 * @return the value just read 219 * @throws IOException in case of an I/O problem 220 */ 221 public static long readSwappedUnsignedInteger(final InputStream input) throws IOException { 222 final int value1 = read(input); 223 final int value2 = read(input); 224 final int value3 = read(input); 225 final int value4 = read(input); 226 final long low = ((value1 & 0xff) << 0) + ((value2 & 0xff) << 8) + ((value3 & 0xff) << 16); 227 final long high = value4 & 0xff; 228 return (high << 24) + (0xffffffffL & low); 229 } 230 231 /** 232 * Reads an unsigned short (16-bit) value from a byte array in little endian order at a given 233 * offset. 234 * 235 * @param data source byte array 236 * @param offset starting offset in the byte array 237 * @return the value read 238 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 2 bytes 239 */ 240 public static int readSwappedUnsignedShort(final byte[] data, final int offset) { 241 validateByteArrayOffset(data, offset, Short.SIZE / Byte.SIZE); 242 return ((data[offset + 0] & 0xff) << 0) + ((data[offset + 1] & 0xff) << 8); 243 } 244 245 /** 246 * Reads an unsigned short (16-bit) from an InputStream in little endian order. 247 * 248 * @param input source InputStream 249 * @return the value just read 250 * @throws IOException in case of an I/O problem 251 */ 252 public static int readSwappedUnsignedShort(final InputStream input) throws IOException { 253 final int value1 = read(input); 254 final int value2 = read(input); 255 256 return ((value1 & 0xff) << 0) + ((value2 & 0xff) << 8); 257 } 258 259 /** 260 * Converts a {@code double} value from big endian to little endian 261 * and vice versa. That is, it converts the {@code double} to bytes, 262 * reverses the bytes, and then reinterprets those bytes as a new {@code double}. 263 * This can be useful if you have a number that was read from the 264 * underlying source in the wrong endianness. 265 * 266 * @param value value to convert 267 * @return the converted value 268 */ 269 public static double swapDouble(final double value) { 270 return Double.longBitsToDouble(swapLong(Double.doubleToLongBits(value))); 271 } 272 273 /** 274 * Converts a {@code float} value from big endian to little endian and vice versa. 275 * 276 * @param value value to convert 277 * @return the converted value 278 */ 279 public static float swapFloat(final float value) { 280 return Float.intBitsToFloat(swapInteger(Float.floatToIntBits(value))); 281 } 282 283 /** 284 * Converts an {@code int} value from big endian to little endian and vice versa. 285 * 286 * @param value value to convert 287 * @return the converted value 288 */ 289 public static int swapInteger(final int value) { 290 return 291 ((value >> 0 & 0xff) << 24) + 292 ((value >> 8 & 0xff) << 16) + 293 ((value >> 16 & 0xff) << 8) + 294 ((value >> 24 & 0xff) << 0); 295 } 296 297 /** 298 * Converts a {@code long} value from big endian to little endian and vice versa. 299 * 300 * @param value value to convert 301 * @return the converted value 302 */ 303 public static long swapLong(final long value) { 304 return 305 ((value >> 0 & 0xff) << 56) + 306 ((value >> 8 & 0xff) << 48) + 307 ((value >> 16 & 0xff) << 40) + 308 ((value >> 24 & 0xff) << 32) + 309 ((value >> 32 & 0xff) << 24) + 310 ((value >> 40 & 0xff) << 16) + 311 ((value >> 48 & 0xff) << 8) + 312 ((value >> 56 & 0xff) << 0); 313 } 314 315 /** 316 * Converts a {@code short} value from big endian to little endian and vice versa. 317 * 318 * @param value value to convert 319 * @return the converted value 320 */ 321 public static short swapShort(final short value) { 322 return (short) (((value >> 0 & 0xff) << 8) + 323 ((value >> 8 & 0xff) << 0)); 324 } 325 326 /** 327 * Validates if the provided byte array has enough data. 328 * 329 * @param data the input byte array 330 * @param offset the input offset 331 * @param byteNeeded the needed number of bytes 332 * @throws IllegalArgumentException if the byte array does not have enough data 333 */ 334 private static void validateByteArrayOffset(final byte[] data, final int offset, final int byteNeeded) { 335 if (data.length < offset + byteNeeded) { 336 throw new IllegalArgumentException("Data only has " + data.length + "bytes, needed " + (offset + byteNeeded) + "bytes."); 337 } 338 } 339 340 /** 341 * Writes the 8 bytes of a {@code double} to a byte array at a given offset in little endian order. 342 * 343 * @param data target byte array 344 * @param offset starting offset in the byte array 345 * @param value value to write 346 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 8 bytes 347 */ 348 public static void writeSwappedDouble(final byte[] data, final int offset, final double value) { 349 writeSwappedLong(data, offset, Double.doubleToLongBits(value)); 350 } 351 352 /** 353 * Writes the 8 bytes of a {@code double} to an output stream in little endian order. 354 * 355 * @param output target OutputStream 356 * @param value value to write 357 * @throws IOException in case of an I/O problem 358 */ 359 public static void writeSwappedDouble(final OutputStream output, final double value) throws IOException { 360 writeSwappedLong(output, Double.doubleToLongBits(value)); 361 } 362 363 /** 364 * Writes the 4 bytes of a {@code float} to a byte array at a given offset in little endian order. 365 * 366 * @param data target byte array 367 * @param offset starting offset in the byte array 368 * @param value value to write 369 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes 370 */ 371 public static void writeSwappedFloat(final byte[] data, final int offset, final float value) { 372 writeSwappedInteger(data, offset, Float.floatToIntBits(value)); 373 } 374 375 /** 376 * Writes the 4 bytes of a {@code float} to an output stream in little endian order. 377 * 378 * @param output target OutputStream 379 * @param value value to write 380 * @throws IOException in case of an I/O problem 381 */ 382 public static void writeSwappedFloat(final OutputStream output, final float value) throws IOException { 383 writeSwappedInteger(output, Float.floatToIntBits(value)); 384 } 385 386 /** 387 * Writes the 4 bytes of an {@code int} to a byte array at a given offset in little endian order. 388 * 389 * @param data target byte array 390 * @param offset starting offset in the byte array 391 * @param value value to write 392 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes 393 */ 394 public static void writeSwappedInteger(final byte[] data, final int offset, final int value) { 395 validateByteArrayOffset(data, offset, Integer.SIZE / Byte.SIZE); 396 data[offset + 0] = (byte) (value >> 0 & 0xff); 397 data[offset + 1] = (byte) (value >> 8 & 0xff); 398 data[offset + 2] = (byte) (value >> 16 & 0xff); 399 data[offset + 3] = (byte) (value >> 24 & 0xff); 400 } 401 402 /** 403 * Writes the 4 bytes of an {@code int} to an output stream in little endian order. 404 * 405 * @param output target OutputStream 406 * @param value value to write 407 * @throws IOException in case of an I/O problem 408 */ 409 public static void writeSwappedInteger(final OutputStream output, final int value) throws IOException { 410 output.write((byte) (value >> 0 & 0xff)); 411 output.write((byte) (value >> 8 & 0xff)); 412 output.write((byte) (value >> 16 & 0xff)); 413 output.write((byte) (value >> 24 & 0xff)); 414 } 415 416 /** 417 * Writes the 8 bytes of a {@code long} to a byte array at a given offset in little endian order. 418 * 419 * @param data target byte array 420 * @param offset starting offset in the byte array 421 * @param value value to write 422 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 8 bytes 423 */ 424 public static void writeSwappedLong(final byte[] data, final int offset, final long value) { 425 validateByteArrayOffset(data, offset, Long.SIZE / Byte.SIZE); 426 data[offset + 0] = (byte) (value >> 0 & 0xff); 427 data[offset + 1] = (byte) (value >> 8 & 0xff); 428 data[offset + 2] = (byte) (value >> 16 & 0xff); 429 data[offset + 3] = (byte) (value >> 24 & 0xff); 430 data[offset + 4] = (byte) (value >> 32 & 0xff); 431 data[offset + 5] = (byte) (value >> 40 & 0xff); 432 data[offset + 6] = (byte) (value >> 48 & 0xff); 433 data[offset + 7] = (byte) (value >> 56 & 0xff); 434 } 435 436 /** 437 * Writes the 8 bytes of a {@code long} to an output stream in little endian order. 438 * 439 * @param output target OutputStream 440 * @param value value to write 441 * @throws IOException in case of an I/O problem 442 */ 443 public static void writeSwappedLong(final OutputStream output, final long value) throws IOException { 444 output.write((byte) (value >> 0 & 0xff)); 445 output.write((byte) (value >> 8 & 0xff)); 446 output.write((byte) (value >> 16 & 0xff)); 447 output.write((byte) (value >> 24 & 0xff)); 448 output.write((byte) (value >> 32 & 0xff)); 449 output.write((byte) (value >> 40 & 0xff)); 450 output.write((byte) (value >> 48 & 0xff)); 451 output.write((byte) (value >> 56 & 0xff)); 452 } 453 454 /** 455 * Writes the 2 bytes of a {@code short} to a byte array at a given offset in little endian order. 456 * 457 * @param data target byte array 458 * @param offset starting offset in the byte array 459 * @param value value to write 460 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 2 bytes 461 */ 462 public static void writeSwappedShort(final byte[] data, final int offset, final short value) { 463 validateByteArrayOffset(data, offset, Short.SIZE / Byte.SIZE); 464 data[offset + 0] = (byte) (value >> 0 & 0xff); 465 data[offset + 1] = (byte) (value >> 8 & 0xff); 466 } 467 468 /** 469 * Writes the 2 bytes of a {@code short} to an output stream using little endian encoding. 470 * 471 * @param output target OutputStream 472 * @param value value to write 473 * @throws IOException in case of an I/O problem 474 */ 475 public static void writeSwappedShort(final OutputStream output, final short value) throws IOException { 476 output.write((byte) (value >> 0 & 0xff)); 477 output.write((byte) (value >> 8 & 0xff)); 478 } 479 480 /** 481 * Instances should NOT be constructed in standard programming. 482 * 483 * @deprecated TODO Make private in 3.0. 484 */ 485 @Deprecated 486 public EndianUtils() { 487 // empty 488 } 489}