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 final byte[] result = new byte[length]; 092 int read = 0; 093 while (read < length) { 094 final int count = is.read(result, read, length - read); 095 if (count < 0) { 096 throw new IOException(exception + " count: " + count 097 + " read: " + read + " length: " + length); 098 } 099 100 read += count; 101 } 102 103 return result; 104 } 105 106 public static byte[] readBytes(final InputStream is, final int count) throws IOException { 107 return readBytes("", is, count, "Unexpected EOF"); 108 } 109 110 public static void readAndVerifyBytes(final InputStream is, final byte[] expected, 111 final String exception) throws ImageReadException, IOException { 112 for (final byte element : expected) { 113 final int data = is.read(); 114 final byte b = (byte) (0xff & data); 115 116 if (data < 0) { 117 throw new ImageReadException("Unexpected EOF."); 118 } 119 120 if (b != element) { 121 throw new ImageReadException(exception); 122 } 123 } 124 } 125 126 public static void readAndVerifyBytes(final InputStream is, 127 final BinaryConstant expected, final String exception) 128 throws ImageReadException, IOException { 129 for (int i = 0; i < expected.size(); i++) { 130 final int data = is.read(); 131 final byte b = (byte) (0xff & data); 132 133 if (data < 0) { 134 throw new ImageReadException("Unexpected EOF."); 135 } 136 137 if (b != expected.get(i)) { 138 throw new ImageReadException(exception); 139 } 140 } 141 } 142 143 public static void skipBytes(final InputStream is, final long length, final String exception) 144 throws IOException { 145 long total = 0; 146 while (length != total) { 147 final long skipped = is.skip(length - total); 148 if (skipped < 1) { 149 throw new IOException(exception + " (" + skipped + ")"); 150 } 151 total += skipped; 152 } 153 } 154 155 public static byte[] remainingBytes(final String name, final byte[] bytes, final int count) { 156 return slice(bytes, count, bytes.length - count); 157 } 158 159 public static byte[] slice(final byte[] bytes, final int start, final int count) { 160 final byte[] result = new byte[count]; 161 System.arraycopy(bytes, start, result, 0, count); 162 return result; 163 } 164 165 public static byte[] head(final byte[] bytes, int count) { 166 if (count > bytes.length) { 167 count = bytes.length; 168 } 169 return slice(bytes, 0, count); 170 } 171 172 public static boolean compareBytes(final byte[] a, final int aStart, final byte[] b, 173 final int bStart, final int length) { 174 if (a.length < (aStart + length)) { 175 return false; 176 } 177 if (b.length < (bStart + length)) { 178 return false; 179 } 180 181 for (int i = 0; i < length; i++) { 182 if (a[aStart + i] != b[bStart + i]) { 183 return false; 184 } 185 } 186 187 return true; 188 } 189 190 public static int read4Bytes(final String name, final InputStream is, 191 final String exception, final ByteOrder byteOrder) throws IOException { 192 final int byte0 = is.read(); 193 final int byte1 = is.read(); 194 final int byte2 = is.read(); 195 final int byte3 = is.read(); 196 if ((byte0 | byte1 | byte2 | byte3) < 0) { 197 throw new IOException(exception); 198 } 199 200 final int result; 201 if (byteOrder == ByteOrder.BIG_ENDIAN) { 202 result = (byte0 << 24) | (byte1 << 16) 203 | (byte2 << 8) | (byte3 << 0); 204 } else { 205 result = (byte3 << 24) | (byte2 << 16) 206 | (byte1 << 8) | (byte0 << 0); 207 } 208 209 return result; 210 } 211 212 public static int read3Bytes(final String name, final InputStream is, 213 final String exception, final ByteOrder byteOrder) throws IOException { 214 final int byte0 = is.read(); 215 final int byte1 = is.read(); 216 final int byte2 = is.read(); 217 if ((byte0 | byte1 | byte2) < 0) { 218 throw new IOException(exception); 219 } 220 221 final int result; 222 if (byteOrder == ByteOrder.BIG_ENDIAN) { 223 result = (byte0 << 16) | (byte1 << 8) 224 | (byte2 << 0); 225 } else { 226 result = (byte2 << 16) | (byte1 << 8) 227 | (byte0 << 0); 228 } 229 230 return result; 231 } 232 233 public static int read2Bytes(final String name, final InputStream is, 234 final String exception, final ByteOrder byteOrder) throws IOException { 235 final int byte0 = is.read(); 236 final int byte1 = is.read(); 237 if ((byte0 | byte1) < 0) { 238 throw new IOException(exception); 239 } 240 241 final int result; 242 if (byteOrder == ByteOrder.BIG_ENDIAN) { 243 result = (byte0 << 8) | byte1; 244 } else { 245 result = (byte1 << 8) | byte0; 246 } 247 248 return result; 249 } 250 251 public static void printCharQuad(final String msg, final int i) { 252 LOGGER.finest(msg + ": '" + (char) (0xff & (i >> 24)) 253 + (char) (0xff & (i >> 16)) + (char) (0xff & (i >> 8)) 254 + (char) (0xff & (i >> 0)) + "'"); 255 256 } 257 258 public static void printCharQuad(final PrintWriter pw, final String msg, final int i) { 259 pw.println(msg + ": '" + (char) (0xff & (i >> 24)) 260 + (char) (0xff & (i >> 16)) + (char) (0xff & (i >> 8)) 261 + (char) (0xff & (i >> 0)) + "'"); 262 263 } 264 265 public static void printByteBits(final String msg, final byte i) { 266 LOGGER.finest(msg + ": '" + Integer.toBinaryString(0xff & i)); 267 } 268 269 public static int charsToQuad(final char c1, final char c2, final char c3, final char c4) { 270 return (((0xff & c1) << 24) | ((0xff & c2) << 16) | ((0xff & c3) << 8) | ((0xff & c4) << 0)); 271 } 272 273 /** 274 * Convert a quad into a byte array. 275 * @param quad quad 276 * @return a byte array 277 */ 278 public static byte[] quadsToByteArray(int quad) { 279 byte[] arr = new byte[4]; 280 arr[0] = (byte) (quad >> 24); 281 arr[1] = (byte) (quad >> 16); 282 arr[2] = (byte) (quad >> 8); 283 arr[3] = (byte) quad; 284 return arr; 285 } 286 287 /** 288 * Consumes the {@code InputStream} (without closing it) searching for a quad. It will 289 * stop either when the quad is found, or when there are no more bytes in the input stream. 290 * 291 * <p>Returns {@code true} if it found the quad, and {@code false} otherwise. 292 * 293 * @param quad a quad (the needle) 294 * @param bis an input stream (the haystack) 295 * @return {@code true} if it found the quad, and {@code false} otherwise 296 * @throws IOException 297 */ 298 public static boolean searchQuad(int quad, InputStream bis) throws IOException { 299 byte[] needle = BinaryFunctions.quadsToByteArray(quad); 300 int b = -1; 301 int position = 0; 302 while ((b = bis.read()) != -1) { 303 if (needle[position] == b) { 304 position++; 305 if (position == needle.length) { 306 return true; 307 } 308 } else { 309 position = 0; 310 } 311 } 312 return false; 313 } 314 315 public static int findNull(final byte[] src) { 316 return findNull(src, 0); 317 } 318 319 public static int findNull(final byte[] src, final int start) { 320 for (int i = start; i < src.length; i++) { 321 if (src[i] == 0) { 322 return i; 323 } 324 } 325 return -1; 326 } 327 328 public static byte[] getRAFBytes(final RandomAccessFile raf, final long pos, 329 final int length, final String exception) throws IOException { 330 final byte[] result = new byte[length]; 331 332 raf.seek(pos); 333 334 int read = 0; 335 while (read < length) { 336 final int count = raf.read(result, read, length - read); 337 if (count < 0) { 338 throw new IOException(exception); 339 } 340 341 read += count; 342 } 343 344 return result; 345 346 } 347 348 public static void skipBytes(final InputStream is, final long length) throws IOException { 349 skipBytes(is, length, "Couldn't skip bytes"); 350 } 351 352 public static void copyStreamToStream(final InputStream is, final OutputStream os) 353 throws IOException { 354 final byte[] buffer = new byte[1024]; 355 int read; 356 while ((read = is.read(buffer)) > 0) { 357 os.write(buffer, 0, read); 358 } 359 } 360 361 public static byte[] getStreamBytes(final InputStream is) throws IOException { 362 final ByteArrayOutputStream os = new ByteArrayOutputStream(); 363 copyStreamToStream(is, os); 364 return os.toByteArray(); 365 } 366}