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.imaging.common; 018 019import java.io.ByteArrayOutputStream; 020import java.io.IOException; 021import java.io.InputStream; 022import java.io.OutputStream; 023import java.io.PrintWriter; 024import java.io.RandomAccessFile; 025import java.nio.ByteOrder; 026import java.util.logging.Logger; 027 028import org.apache.commons.imaging.ImageReadException; 029 030/** 031 * Convenience methods for various binary and I/O operations. 032 */ 033public final class BinaryFunctions { 034 035 private static final Logger LOGGER = Logger.getLogger(BinaryFunctions.class.getName()); 036 037 private BinaryFunctions() { 038 } 039 040 public static boolean startsWith(final byte[] haystack, final byte[] needle) { 041 if (needle == null) { 042 return false; 043 } 044 if (haystack == null) { 045 return false; 046 } 047 if (needle.length > haystack.length) { 048 return false; 049 } 050 051 for (int i = 0; i < needle.length; i++) { 052 if (needle[i] != haystack[i]) { 053 return false; 054 } 055 } 056 057 return true; 058 } 059 060 public static boolean startsWith(final byte[] haystack, final BinaryConstant needle) { 061 if ((haystack == null) || (haystack.length < needle.size())) { 062 return false; 063 } 064 065 for (int i = 0; i < needle.size(); i++) { 066 if (haystack[i] != needle.get(i)) { 067 return false; 068 } 069 } 070 071 return true; 072 } 073 074 public static byte readByte(final String name, final InputStream is, final String exception) 075 throws IOException { 076 final int result = is.read(); 077 if ((result < 0)) { 078 throw new IOException(exception); 079 } 080 return (byte) (0xff & result); 081 } 082 083 public static byte[] readBytes(final String name, final InputStream is, final int length) 084 throws IOException { 085 final String exception = name + " could not be read."; 086 return readBytes(name, is, length, exception); 087 } 088 089 public static byte[] readBytes(final String name, final InputStream is, final int length, 090 final String exception) throws IOException { 091 if (length < 0) { 092 throw new IOException(String.format("%s, invalid length: %d", exception, length)); 093 } 094 final byte[] result = new byte[length]; 095 int read = 0; 096 while (read < length) { 097 final int count = is.read(result, read, length - read); 098 if (count < 0) { 099 throw new IOException(exception + " count: " + count 100 + " read: " + read + " length: " + length); 101 } 102 103 read += count; 104 } 105 106 return result; 107 } 108 109 public static byte[] readBytes(final InputStream is, final int count) throws IOException { 110 return readBytes("", is, count, "Unexpected EOF"); 111 } 112 113 public static void readAndVerifyBytes(final InputStream is, final byte[] expected, 114 final String exception) throws ImageReadException, IOException { 115 for (final byte element : expected) { 116 final int data = is.read(); 117 final byte b = (byte) (0xff & data); 118 119 if (data < 0) { 120 throw new ImageReadException("Unexpected EOF."); 121 } 122 123 if (b != element) { 124 throw new ImageReadException(exception); 125 } 126 } 127 } 128 129 public static void readAndVerifyBytes(final InputStream is, 130 final BinaryConstant expected, final String exception) 131 throws ImageReadException, IOException { 132 for (int i = 0; i < expected.size(); i++) { 133 final int data = is.read(); 134 final byte b = (byte) (0xff & data); 135 136 if (data < 0) { 137 throw new ImageReadException("Unexpected EOF."); 138 } 139 140 if (b != expected.get(i)) { 141 throw new ImageReadException(exception); 142 } 143 } 144 } 145 146 public static void skipBytes(final InputStream is, final long length, final String exception) 147 throws IOException { 148 long total = 0; 149 while (length != total) { 150 final long skipped = is.skip(length - total); 151 if (skipped < 1) { 152 throw new IOException(exception + " (" + skipped + ")"); 153 } 154 total += skipped; 155 } 156 } 157 158 public static byte[] remainingBytes(final String name, final byte[] bytes, final int count) { 159 return slice(bytes, count, bytes.length - count); 160 } 161 162 public static byte[] slice(final byte[] bytes, final int start, final int count) { 163 final byte[] result = new byte[count]; 164 System.arraycopy(bytes, start, result, 0, count); 165 return result; 166 } 167 168 public static byte[] head(final byte[] bytes, int count) { 169 if (count > bytes.length) { 170 count = bytes.length; 171 } 172 return slice(bytes, 0, count); 173 } 174 175 public static boolean compareBytes(final byte[] a, final int aStart, final byte[] b, 176 final int bStart, final int length) { 177 if (a.length < (aStart + length)) { 178 return false; 179 } 180 if (b.length < (bStart + length)) { 181 return false; 182 } 183 184 for (int i = 0; i < length; i++) { 185 if (a[aStart + i] != b[bStart + i]) { 186 return false; 187 } 188 } 189 190 return true; 191 } 192 193 public static int read4Bytes(final String name, final InputStream is, 194 final String exception, final ByteOrder byteOrder) throws IOException { 195 final int byte0 = is.read(); 196 final int byte1 = is.read(); 197 final int byte2 = is.read(); 198 final int byte3 = is.read(); 199 if ((byte0 | byte1 | byte2 | byte3) < 0) { 200 throw new IOException(exception); 201 } 202 203 final int result; 204 if (byteOrder == ByteOrder.BIG_ENDIAN) { 205 result = (byte0 << 24) | (byte1 << 16) 206 | (byte2 << 8) | (byte3 << 0); 207 } else { 208 result = (byte3 << 24) | (byte2 << 16) 209 | (byte1 << 8) | (byte0 << 0); 210 } 211 212 return result; 213 } 214 215 public static int read3Bytes(final String name, final InputStream is, 216 final String exception, final ByteOrder byteOrder) throws IOException { 217 final int byte0 = is.read(); 218 final int byte1 = is.read(); 219 final int byte2 = is.read(); 220 if ((byte0 | byte1 | byte2) < 0) { 221 throw new IOException(exception); 222 } 223 224 final int result; 225 if (byteOrder == ByteOrder.BIG_ENDIAN) { 226 result = (byte0 << 16) | (byte1 << 8) 227 | (byte2 << 0); 228 } else { 229 result = (byte2 << 16) | (byte1 << 8) 230 | (byte0 << 0); 231 } 232 233 return result; 234 } 235 236 public static int read2Bytes(final String name, final InputStream is, 237 final String exception, final ByteOrder byteOrder) throws IOException { 238 final int byte0 = is.read(); 239 final int byte1 = is.read(); 240 if ((byte0 | byte1) < 0) { 241 throw new IOException(exception); 242 } 243 244 final int result; 245 if (byteOrder == ByteOrder.BIG_ENDIAN) { 246 result = (byte0 << 8) | byte1; 247 } else { 248 result = (byte1 << 8) | byte0; 249 } 250 251 return result; 252 } 253 254 public static void printCharQuad(final String msg, final int i) { 255 LOGGER.finest(msg + ": '" + (char) (0xff & (i >> 24)) 256 + (char) (0xff & (i >> 16)) + (char) (0xff & (i >> 8)) 257 + (char) (0xff & (i >> 0)) + "'"); 258 259 } 260 261 public static void printCharQuad(final PrintWriter pw, final String msg, final int i) { 262 pw.println(msg + ": '" + (char) (0xff & (i >> 24)) 263 + (char) (0xff & (i >> 16)) + (char) (0xff & (i >> 8)) 264 + (char) (0xff & (i >> 0)) + "'"); 265 266 } 267 268 public static void printByteBits(final String msg, final byte i) { 269 LOGGER.finest(msg + ": '" + Integer.toBinaryString(0xff & i)); 270 } 271 272 public static int charsToQuad(final char c1, final char c2, final char c3, final char c4) { 273 return (((0xff & c1) << 24) | ((0xff & c2) << 16) | ((0xff & c3) << 8) | ((0xff & c4) << 0)); 274 } 275 276 /** 277 * Convert a quad into a byte array. 278 * @param quad quad 279 * @return a byte array 280 */ 281 public static byte[] quadsToByteArray(final int quad) { 282 final byte[] arr = new byte[4]; 283 arr[0] = (byte) (quad >> 24); 284 arr[1] = (byte) (quad >> 16); 285 arr[2] = (byte) (quad >> 8); 286 arr[3] = (byte) quad; 287 return arr; 288 } 289 290 /** 291 * Consumes the {@code InputStream} (without closing it) searching for a quad. It will 292 * stop either when the quad is found, or when there are no more bytes in the input stream. 293 * 294 * <p>Returns {@code true} if it found the quad, and {@code false} otherwise. 295 * 296 * @param quad a quad (the needle) 297 * @param bis an input stream (the haystack) 298 * @return {@code true} if it found the quad, and {@code false} otherwise 299 * @throws IOException if it fails to read from the given input stream 300 */ 301 public static boolean searchQuad(final int quad, final InputStream bis) throws IOException { 302 final byte[] needle = BinaryFunctions.quadsToByteArray(quad); 303 int b = -1; 304 int position = 0; 305 while ((b = bis.read()) != -1) { 306 if (needle[position] == b) { 307 position++; 308 if (position == needle.length) { 309 return true; 310 } 311 } else { 312 position = 0; 313 } 314 } 315 return false; 316 } 317 318 public static int findNull(final byte[] src) { 319 return findNull(src, 0); 320 } 321 322 public static int findNull(final byte[] src, final int start) { 323 for (int i = start; i < src.length; i++) { 324 if (src[i] == 0) { 325 return i; 326 } 327 } 328 return -1; 329 } 330 331 public static byte[] getRAFBytes(final RandomAccessFile raf, final long pos, 332 final int length, final String exception) throws IOException { 333 if (length < 0) { 334 throw new IOException(String.format("%s, invalid length: %d", exception, length)); 335 } 336 final byte[] result = new byte[length]; 337 338 raf.seek(pos); 339 340 int read = 0; 341 while (read < length) { 342 final int count = raf.read(result, read, length - read); 343 if (count < 0) { 344 throw new IOException(exception); 345 } 346 347 read += count; 348 } 349 350 return result; 351 352 } 353 354 public static void skipBytes(final InputStream is, final long length) throws IOException { 355 skipBytes(is, length, "Couldn't skip bytes"); 356 } 357 358 public static void copyStreamToStream(final InputStream is, final OutputStream os) 359 throws IOException { 360 final byte[] buffer = new byte[1024]; 361 int read; 362 while ((read = is.read(buffer)) > 0) { 363 os.write(buffer, 0, read); 364 } 365 } 366 367 public static byte[] getStreamBytes(final InputStream is) throws IOException { 368 final ByteArrayOutputStream os = new ByteArrayOutputStream(); 369 copyStreamToStream(is, os); 370 return os.toByteArray(); 371 } 372}