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 * https://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 * 054 * @param input the stream. 055 * @return the byte. 056 * @throws IOException if the end of file is reached. 057 */ 058 private static int read(final InputStream input) throws IOException { 059 final int value = input.read(); 060 if (EOF == value) { 061 throw new EOFException("Unexpected EOF reached"); 062 } 063 return value; 064 } 065 066 /** 067 * Reads a little-endian {@code double} value from a byte array at a given offset. 068 * 069 * @param data source byte array. 070 * @param offset starting offset in the byte array. 071 * @return the value read. 072 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 8 bytes. 073 */ 074 public static double readSwappedDouble(final byte[] data, final int offset) { 075 return Double.longBitsToDouble(readSwappedLong(data, offset)); 076 } 077 078 /** 079 * Reads a little-endian {@code double} value from an InputStream. 080 * 081 * @param input source InputStream. 082 * @return the value just read. 083 * @throws IOException in case of an I/O problem. 084 */ 085 public static double readSwappedDouble(final InputStream input) throws IOException { 086 return Double.longBitsToDouble(readSwappedLong(input)); 087 } 088 089 /** 090 * Reads a little-endian {@code float} value from a byte array at a given offset. 091 * 092 * @param data source byte array. 093 * @param offset starting offset in the byte array. 094 * @return the value read. 095 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes. 096 */ 097 public static float readSwappedFloat(final byte[] data, final int offset) { 098 return Float.intBitsToFloat(readSwappedInteger(data, offset)); 099 } 100 101 /** 102 * Reads a little-endian {@code float} value from an InputStream. 103 * 104 * @param input source InputStream. 105 * @return the value just read. 106 * @throws IOException in case of an I/O problem. 107 */ 108 public static float readSwappedFloat(final InputStream input) throws IOException { 109 return Float.intBitsToFloat(readSwappedInteger(input)); 110 } 111 112 /** 113 * Reads a little-endian {@code int} value from a byte array at a given offset. 114 * 115 * @param data source byte array. 116 * @param offset starting offset in the byte array. 117 * @return the value read. 118 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes. 119 */ 120 public static int readSwappedInteger(final byte[] data, final int offset) { 121 validateByteArrayOffset(data, offset, Integer.SIZE / Byte.SIZE); 122 return 123 ((data[offset + 0] & 0xff) << 0) + 124 ((data[offset + 1] & 0xff) << 8) + 125 ((data[offset + 2] & 0xff) << 16) + 126 ((data[offset + 3] & 0xff) << 24); 127 } 128 129 /** 130 * Reads a little-endian {@code int} value from an InputStream. 131 * 132 * @param input source InputStream. 133 * @return the value just read. 134 * @throws IOException in case of an I/O problem. 135 */ 136 public static int readSwappedInteger(final InputStream input) throws IOException { 137 final int value1 = read(input); 138 final int value2 = read(input); 139 final int value3 = read(input); 140 final int value4 = read(input); 141 return 142 ((value1 & 0xff) << 0) + 143 ((value2 & 0xff) << 8) + 144 ((value3 & 0xff) << 16) + 145 ((value4 & 0xff) << 24); 146 } 147 148 /** 149 * Reads a little-endian {@code long} value from a byte array at a given offset. 150 * 151 * @param data source byte array. 152 * @param offset starting offset in the byte array. 153 * @return the value read. 154 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 8 bytes. 155 */ 156 public static long readSwappedLong(final byte[] data, final int offset) { 157 validateByteArrayOffset(data, offset, Long.SIZE / Byte.SIZE); 158 final long low = readSwappedInteger(data, offset); 159 final long high = readSwappedInteger(data, offset + 4); 160 return (high << 32) + (0xffffffffL & low); 161 } 162 163 /** 164 * Reads a little-endian {@code long} value from an InputStream. 165 * 166 * @param input source InputStream. 167 * @return the value just read. 168 * @throws IOException in case of an I/O problem. 169 */ 170 public static long readSwappedLong(final InputStream input) throws IOException { 171 final byte[] bytes = new byte[8]; 172 for (int i = 0; i < 8; i++) { 173 bytes[i] = (byte) read(input); 174 } 175 return readSwappedLong(bytes, 0); 176 } 177 178 /** 179 * Reads a little-endian {@code short} value from a byte array at a given offset. 180 * 181 * @param data source byte array. 182 * @param offset starting offset in the byte array. 183 * @return the value read. 184 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 2 bytes. 185 */ 186 public static short readSwappedShort(final byte[] data, final int offset) { 187 validateByteArrayOffset(data, offset, Short.SIZE / Byte.SIZE); 188 return (short) ( 189 ((data[offset + 0] & 0xff) << 0) + 190 ((data[offset + 1] & 0xff) << 8) 191 ); 192 } 193 194 /** 195 * Reads a little-endian {@code short} value from an InputStream. 196 * 197 * @param input source InputStream. 198 * @return the value just read. 199 * @throws IOException in case of an I/O problem. 200 */ 201 public static short readSwappedShort(final InputStream input) throws IOException { 202 return (short) ( 203 ((read(input) & 0xff) << 0) + 204 ((read(input) & 0xff) << 8) 205 ); 206 } 207 208 /** 209 * Reads a little-endian unsigned integer (32-bit) value from a byte array at a given 210 * offset. 211 * 212 * @param data source byte array. 213 * @param offset starting offset in the byte array. 214 * @return the value read. 215 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes. 216 */ 217 public static long readSwappedUnsignedInteger(final byte[] data, final int offset) { 218 validateByteArrayOffset(data, offset, Integer.SIZE / Byte.SIZE); 219 final long low = ((data[offset + 0] & 0xff) << 0) + 220 ((data[offset + 1] & 0xff) << 8) + 221 ((data[offset + 2] & 0xff) << 16); 222 final long high = data[offset + 3] & 0xff; 223 return (high << 24) + (0xffffffffL & low); 224 } 225 226 /** 227 * Reads a little-endian unsigned integer (32-bit) from an InputStream. 228 * 229 * @param input source InputStream. 230 * @return the value just read. 231 * @throws IOException in case of an I/O problem. 232 */ 233 public static long readSwappedUnsignedInteger(final InputStream input) throws IOException { 234 final int value1 = read(input); 235 final int value2 = read(input); 236 final int value3 = read(input); 237 final int value4 = read(input); 238 final long low = ((value1 & 0xff) << 0) + 239 ((value2 & 0xff) << 8) + 240 ((value3 & 0xff) << 16); 241 final long high = value4 & 0xff; 242 return (high << 24) + (0xffffffffL & low); 243 } 244 245 /** 246 * Reads an unsigned short (16-bit) value from a byte array in little-endian order at a given 247 * offset. 248 * 249 * @param data source byte array. 250 * @param offset starting offset in the byte array. 251 * @return the value read. 252 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 2 bytes. 253 */ 254 public static int readSwappedUnsignedShort(final byte[] data, final int offset) { 255 validateByteArrayOffset(data, offset, Short.SIZE / Byte.SIZE); 256 return ((data[offset + 0] & 0xff) << 0) + 257 ((data[offset + 1] & 0xff) << 8); 258 } 259 260 /** 261 * Reads an unsigned short (16-bit) from an InputStream in little-endian order. 262 * 263 * @param input source InputStream. 264 * @return the value just read. 265 * @throws IOException in case of an I/O problem. 266 */ 267 public static int readSwappedUnsignedShort(final InputStream input) throws IOException { 268 final int value1 = read(input); 269 final int value2 = read(input); 270 271 return ((value1 & 0xff) << 0) + 272 ((value2 & 0xff) << 8); 273 } 274 275 /** 276 * Converts a {@code double} value from big-endian to little-endian 277 * and vice versa. That is, it converts the {@code double} to bytes, 278 * reverses the bytes, and then reinterprets those bytes as a new {@code double}. 279 * This can be useful if you have a number that was read from the 280 * underlying source in the wrong endianness. 281 * 282 * @param value value to convert. 283 * @return the converted value. 284 */ 285 public static double swapDouble(final double value) { 286 return Double.longBitsToDouble(swapLong(Double.doubleToLongBits(value))); 287 } 288 289 /** 290 * Converts a {@code float} value from big-endian to little-endian and vice versa. 291 * 292 * @param value value to convert. 293 * @return the converted value. 294 */ 295 public static float swapFloat(final float value) { 296 return Float.intBitsToFloat(swapInteger(Float.floatToIntBits(value))); 297 } 298 299 /** 300 * Converts an {@code int} value from big-endian to little-endian and vice versa. 301 * 302 * @param value value to convert. 303 * @return the converted value. 304 */ 305 public static int swapInteger(final int value) { 306 return 307 ((value >> 0 & 0xff) << 24) + 308 ((value >> 8 & 0xff) << 16) + 309 ((value >> 16 & 0xff) << 8) + 310 ((value >> 24 & 0xff) << 0); 311 } 312 313 /** 314 * Converts a {@code long} value from big-endian to little-endian and vice versa. 315 * 316 * @param value value to convert. 317 * @return the converted value. 318 */ 319 public static long swapLong(final long value) { 320 return 321 ((value >> 0 & 0xff) << 56) + 322 ((value >> 8 & 0xff) << 48) + 323 ((value >> 16 & 0xff) << 40) + 324 ((value >> 24 & 0xff) << 32) + 325 ((value >> 32 & 0xff) << 24) + 326 ((value >> 40 & 0xff) << 16) + 327 ((value >> 48 & 0xff) << 8) + 328 ((value >> 56 & 0xff) << 0); 329 } 330 331 /** 332 * Converts a {@code short} value from big-endian to little-endian and vice versa. 333 * 334 * @param value value to convert. 335 * @return the converted value. 336 */ 337 public static short swapShort(final short value) { 338 return (short) ( 339 ((value >> 0 & 0xff) << 8) + 340 ((value >> 8 & 0xff) << 0) 341 ); 342 } 343 344 /** 345 * Validates if the provided byte array has enough data. 346 * 347 * @param data the input byte array. 348 * @param offset the input offset. 349 * @param byteNeeded the needed number of bytes. 350 * @throws IllegalArgumentException if the byte array does not have enough data. 351 */ 352 private static void validateByteArrayOffset(final byte[] data, final int offset, final int byteNeeded) { 353 if (data.length < offset + byteNeeded) { 354 throw new IllegalArgumentException("Data only has " + data.length + "bytes, needed " + (offset + byteNeeded) + "bytes."); 355 } 356 } 357 358 /** 359 * Writes the 8 bytes of a {@code double} to a byte array at a given offset in little-endian order. 360 * 361 * @param data target byte array. 362 * @param offset starting offset in the byte array. 363 * @param value value to write. 364 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 8 bytes. 365 */ 366 public static void writeSwappedDouble(final byte[] data, final int offset, final double value) { 367 writeSwappedLong(data, offset, Double.doubleToLongBits(value)); 368 } 369 370 /** 371 * Writes the 8 bytes of a {@code double} to an output stream in little-endian order. 372 * 373 * @param output target OutputStream. 374 * @param value value to write. 375 * @throws IOException in case of an I/O problem. 376 */ 377 public static void writeSwappedDouble(final OutputStream output, final double value) throws IOException { 378 writeSwappedLong(output, Double.doubleToLongBits(value)); 379 } 380 381 /** 382 * Writes the 4 bytes of a {@code float} to a byte array at a given offset in little-endian order. 383 * 384 * @param data target byte array. 385 * @param offset starting offset in the byte array. 386 * @param value value to write. 387 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes. 388 */ 389 public static void writeSwappedFloat(final byte[] data, final int offset, final float value) { 390 writeSwappedInteger(data, offset, Float.floatToIntBits(value)); 391 } 392 393 /** 394 * Writes the 4 bytes of a {@code float} to an output stream in little-endian order. 395 * 396 * @param output target OutputStream. 397 * @param value value to write. 398 * @throws IOException in case of an I/O problem. 399 */ 400 public static void writeSwappedFloat(final OutputStream output, final float value) throws IOException { 401 writeSwappedInteger(output, Float.floatToIntBits(value)); 402 } 403 404 /** 405 * Writes the 4 bytes of an {@code int} to a byte array at a given offset in little-endian order. 406 * 407 * @param data target byte array. 408 * @param offset starting offset in the byte array. 409 * @param value value to write. 410 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 4 bytes. 411 */ 412 public static void writeSwappedInteger(final byte[] data, final int offset, final int value) { 413 validateByteArrayOffset(data, offset, Integer.SIZE / Byte.SIZE); 414 data[offset + 0] = (byte) (value >> 0 & 0xff); 415 data[offset + 1] = (byte) (value >> 8 & 0xff); 416 data[offset + 2] = (byte) (value >> 16 & 0xff); 417 data[offset + 3] = (byte) (value >> 24 & 0xff); 418 } 419 420 /** 421 * Writes the 4 bytes of an {@code int} to an output stream in little-endian order. 422 * 423 * @param output target OutputStream. 424 * @param value value to write. 425 * @throws IOException in case of an I/O problem. 426 */ 427 public static void writeSwappedInteger(final OutputStream output, final int value) throws IOException { 428 output.write((byte) (value >> 0 & 0xff)); 429 output.write((byte) (value >> 8 & 0xff)); 430 output.write((byte) (value >> 16 & 0xff)); 431 output.write((byte) (value >> 24 & 0xff)); 432 } 433 434 /** 435 * Writes the 8 bytes of a {@code long} to a byte array at a given offset in little-endian order. 436 * 437 * @param data target byte array. 438 * @param offset starting offset in the byte array. 439 * @param value value to write. 440 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 8 bytes. 441 */ 442 public static void writeSwappedLong(final byte[] data, final int offset, final long value) { 443 validateByteArrayOffset(data, offset, Long.SIZE / Byte.SIZE); 444 data[offset + 0] = (byte) (value >> 0 & 0xff); 445 data[offset + 1] = (byte) (value >> 8 & 0xff); 446 data[offset + 2] = (byte) (value >> 16 & 0xff); 447 data[offset + 3] = (byte) (value >> 24 & 0xff); 448 data[offset + 4] = (byte) (value >> 32 & 0xff); 449 data[offset + 5] = (byte) (value >> 40 & 0xff); 450 data[offset + 6] = (byte) (value >> 48 & 0xff); 451 data[offset + 7] = (byte) (value >> 56 & 0xff); 452 } 453 454 /** 455 * Writes the 8 bytes of a {@code long} to an output stream in little-endian order. 456 * 457 * @param output target OutputStream. 458 * @param value value to write. 459 * @throws IOException in case of an I/O problem. 460 */ 461 public static void writeSwappedLong(final OutputStream output, final long value) throws IOException { 462 output.write((byte) (value >> 0 & 0xff)); 463 output.write((byte) (value >> 8 & 0xff)); 464 output.write((byte) (value >> 16 & 0xff)); 465 output.write((byte) (value >> 24 & 0xff)); 466 output.write((byte) (value >> 32 & 0xff)); 467 output.write((byte) (value >> 40 & 0xff)); 468 output.write((byte) (value >> 48 & 0xff)); 469 output.write((byte) (value >> 56 & 0xff)); 470 } 471 472 /** 473 * Writes the 2 bytes of a {@code short} to a byte array at a given offset in little-endian order. 474 * 475 * @param data target byte array. 476 * @param offset starting offset in the byte array. 477 * @param value value to write. 478 * @throws IllegalArgumentException if the part of the byte array starting at offset does not have at least 2 bytes. 479 */ 480 public static void writeSwappedShort(final byte[] data, final int offset, final short value) { 481 validateByteArrayOffset(data, offset, Short.SIZE / Byte.SIZE); 482 data[offset + 0] = (byte) (value >> 0 & 0xff); 483 data[offset + 1] = (byte) (value >> 8 & 0xff); 484 } 485 486 /** 487 * Writes the 2 bytes of a {@code short} to an output stream using little-endian encoding. 488 * 489 * @param output target OutputStream. 490 * @param value value to write. 491 * @throws IOException in case of an I/O problem. 492 */ 493 public static void writeSwappedShort(final OutputStream output, final short value) throws IOException { 494 output.write((byte) (value >> 0 & 0xff)); 495 output.write((byte) (value >> 8 & 0xff)); 496 } 497 498 /** 499 * Instances should NOT be constructed in standard programming. 500 * 501 * @deprecated TODO Make private in 3.0. 502 */ 503 @Deprecated 504 public EndianUtils() { 505 // empty 506 } 507}