View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.imaging.common;
18  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.io.PrintWriter;
22  import java.io.RandomAccessFile;
23  import java.nio.ByteOrder;
24  import java.util.logging.Logger;
25  
26  import org.apache.commons.imaging.ImagingException;
27  import org.apache.commons.io.IOUtils;
28  import org.apache.commons.io.RandomAccessFiles;
29  
30  /**
31   * Convenience methods for various binary and I/O operations.
32   */
33  public final class BinaryFunctions {
34  
35      private static final Logger LOGGER = Logger.getLogger(BinaryFunctions.class.getName());
36  
37      public static int charsToQuad(final char c1, final char c2, final char c3, final char c4) {
38          return (0xff & c1) << 24 | (0xff & c2) << 16 | (0xff & c3) << 8 | (0xff & c4) << 0;
39      }
40  
41      public static boolean compareBytes(final byte[] a, final int aStart, final byte[] b, final int bStart, final int length) {
42          if (a.length < aStart + length) {
43              return false;
44          }
45          if (b.length < bStart + length) {
46              return false;
47          }
48  
49          for (int i = 0; i < length; i++) {
50              if (a[aStart + i] != b[bStart + i]) {
51                  return false;
52              }
53          }
54  
55          return true;
56      }
57  
58      public static int findNull(final byte[] src, final int start, final String message) throws ImagingException {
59          for (int i = start; i < src.length; i++) {
60              if (src[i] == 0) {
61                  return i;
62              }
63          }
64          throw new ImagingException(message);
65      }
66  
67      public static int findNull(final byte[] src, final String message) throws ImagingException {
68          return findNull(src, 0, message);
69      }
70  
71      public static byte[] getBytes(final RandomAccessFile raf, final long pos, final int length, final String exception) throws IOException {
72          if (length < 0) {
73              throw new IOException(String.format("%s, invalid length: %d", exception, length));
74          }
75          Allocator.checkByteArray(length);
76          return RandomAccessFiles.read(raf, pos, length);
77  
78      }
79  
80      public static byte[] head(final byte[] bytes, int count) {
81          if (count > bytes.length) {
82              count = bytes.length;
83          }
84          return slice(bytes, 0, count);
85      }
86  
87      public static void logByteBits(final String msg, final byte i) {
88          LOGGER.finest(msg + ": '" + Integer.toBinaryString(0xff & i));
89      }
90  
91      public static void logCharQuad(final String msg, final int i) {
92          LOGGER.finest(msg + ": '" + (char) (0xff & i >> 24) + (char) (0xff & i >> 16) + (char) (0xff & i >> 8) + (char) (0xff & i >> 0) + "'");
93  
94      }
95  
96      public static void printCharQuad(final PrintWriter pw, final String msg, final int i) {
97          pw.println(msg + ": '" + (char) (0xff & i >> 24) + (char) (0xff & i >> 16) + (char) (0xff & i >> 8) + (char) (0xff & i >> 0) + "'");
98  
99      }
100 
101     /**
102      * Convert a quad into a byte array.
103      *
104      * @param quad quad
105      * @return a byte array
106      */
107     public static byte[] quadsToByteArray(final int quad) {
108         final byte[] arr = new byte[4];
109         arr[0] = (byte) (quad >> 24);
110         arr[1] = (byte) (quad >> 16);
111         arr[2] = (byte) (quad >> 8);
112         arr[3] = (byte) quad;
113         return arr;
114     }
115 
116     public static int read2Bytes(final String name, final InputStream is, final String exception, final ByteOrder byteOrder) throws IOException {
117         final int byte0 = is.read();
118         final int byte1 = is.read();
119         if ((byte0 | byte1) < 0) {
120             throw new IOException(exception);
121         }
122 
123         final int result;
124         if (byteOrder == ByteOrder.BIG_ENDIAN) {
125             result = byte0 << 8 | byte1;
126         } else {
127             result = byte1 << 8 | byte0;
128         }
129 
130         return result;
131     }
132 
133     public static int read3Bytes(final String name, final InputStream is, final String exception, final ByteOrder byteOrder) throws IOException {
134         final int byte0 = is.read();
135         final int byte1 = is.read();
136         final int byte2 = is.read();
137         if ((byte0 | byte1 | byte2) < 0) {
138             throw new IOException(exception);
139         }
140 
141         final int result;
142         if (byteOrder == ByteOrder.BIG_ENDIAN) {
143             result = byte0 << 16 | byte1 << 8 | byte2 << 0;
144         } else {
145             result = byte2 << 16 | byte1 << 8 | byte0 << 0;
146         }
147 
148         return result;
149     }
150 
151     public static int read4Bytes(final String name, final InputStream is, final String exception, final ByteOrder byteOrder) throws IOException {
152         final int byte0 = is.read();
153         final int byte1 = is.read();
154         final int byte2 = is.read();
155         final int byte3 = is.read();
156         if ((byte0 | byte1 | byte2 | byte3) < 0) {
157             throw new IOException(exception);
158         }
159 
160         final int result;
161         if (byteOrder == ByteOrder.BIG_ENDIAN) {
162             result = byte0 << 24 | byte1 << 16 | byte2 << 8 | byte3 << 0;
163         } else {
164             result = byte3 << 24 | byte2 << 16 | byte1 << 8 | byte0 << 0;
165         }
166 
167         return result;
168     }
169 
170     /**
171      * Read eight bytes from the specified input stream, adjust for byte order, and return a long integer.
172      *
173      * @param name      a descriptive identifier used for diagnostic purposes
174      * @param is        a valid input stream
175      * @param exception application-defined message to be used for constructing an exception if an error condition is triggered.
176      * @param byteOrder the order in which the InputStream marshals data
177      * @return a long integer interpreted from next 8 bytes in the InputStream
178      * @throws IOException in the event of a non-recoverable error, such as an attempt to read past the end of file.
179      */
180     public static long read8Bytes(final String name, final InputStream is, final String exception, final ByteOrder byteOrder) throws IOException {
181 
182         final long byte0 = is.read();
183         final long byte1 = is.read();
184         final long byte2 = is.read();
185         final long byte3 = is.read();
186         final long byte4 = is.read();
187         final long byte5 = is.read();
188         final long byte6 = is.read();
189         final long byte7 = is.read();
190 
191         if ((byte0 | byte1 | byte2 | byte3 | byte4 | byte5 | byte6 | byte7) < 0) {
192             throw new IOException(exception);
193         }
194 
195         final long result;
196         if (byteOrder == ByteOrder.BIG_ENDIAN) {
197             result = byte0 << 56 | byte1 << 48 | byte2 << 40 | byte3 << 32 | byte4 << 24 | byte5 << 16 | byte6 << 8 | byte7 << 0;
198         } else {
199             result = byte7 << 56 | byte6 << 48 | byte5 << 40 | byte4 << 32 | byte3 << 24 | byte2 << 16 | byte1 << 8 | byte0 << 0;
200         }
201 
202         return result;
203     }
204 
205     public static void readAndVerifyBytes(final InputStream is, final BinaryConstant expected, final String exception) throws ImagingException, IOException {
206         for (int i = 0; i < expected.size(); i++) {
207             final int data = is.read();
208             final byte b = (byte) (0xff & data);
209 
210             if (data < 0) {
211                 throw new ImagingException("Unexpected EOF.");
212             }
213 
214             if (b != expected.get(i)) {
215                 throw new ImagingException(exception);
216             }
217         }
218     }
219 
220     public static void readAndVerifyBytes(final InputStream is, final byte[] expected, final String exception) throws ImagingException, IOException {
221         for (final byte element : expected) {
222             final int data = is.read();
223             final byte b = (byte) (0xff & data);
224 
225             if (data < 0) {
226                 throw new ImagingException("Unexpected EOF.");
227             }
228 
229             if (b != element) {
230                 throw new ImagingException(exception);
231             }
232         }
233     }
234 
235     public static byte readByte(final String name, final InputStream is, final String exceptionMessage) throws IOException {
236         final int result = is.read();
237         if (result < 0) {
238             throw new IOException(exceptionMessage);
239         }
240         return (byte) (0xff & result);
241     }
242 
243     public static byte[] readBytes(final InputStream is, final int count) throws IOException {
244         return readBytes("", is, count, "Unexpected EOF");
245     }
246 
247     public static byte[] readBytes(final String name, final InputStream is, final int length) throws IOException {
248         return readBytes(name, is, length, name + " could not be read.");
249     }
250 
251     public static byte[] readBytes(final String name, final InputStream is, final int length, final String exception) throws IOException {
252         try {
253             return IOUtils.toByteArray(is, Allocator.check(length));
254         } catch (final IOException e) {
255             throw new IOException(exception + ", name: " + name + ", length: " + length);
256         }
257     }
258 
259     public static byte[] remainingBytes(final String name, final byte[] bytes, final int count) {
260         return slice(bytes, count, bytes.length - count);
261     }
262 
263     /**
264      * Consumes the {@code InputStream} (without closing it) searching for a quad. It will stop either when the quad is found, or when there are no more bytes
265      * in the input stream.
266      *
267      * <p>
268      * Returns {@code true} if it found the quad, and {@code false} otherwise.
269      *
270      * @param quad a quad (the needle)
271      * @param bis  an input stream (the haystack)
272      * @return {@code true} if it found the quad, and {@code false} otherwise
273      * @throws IOException if it fails to read from the given input stream
274      */
275     public static boolean searchQuad(final int quad, final InputStream bis) throws IOException {
276         final byte[] needle = BinaryFunctions.quadsToByteArray(quad);
277         int b = -1;
278         int position = 0;
279         while ((b = bis.read()) != -1) {
280             if (needle[position] == b) {
281                 position++;
282                 if (position == needle.length) {
283                     return true;
284                 }
285             } else {
286                 position = 0;
287             }
288         }
289         return false;
290     }
291 
292     public static long skipBytes(final InputStream is, final long length) throws IOException {
293         return skipBytes(is, length, "Couldn't skip bytes");
294     }
295 
296     public static long skipBytes(final InputStream is, final long length, final String exMessage) throws IOException {
297         try {
298             return IOUtils.skip(is, length);
299         } catch (final IOException e) {
300             throw new IOException(exMessage, e);
301         }
302     }
303 
304     public static byte[] slice(final byte[] bytes, final int start, final int count) {
305         final byte[] result = Allocator.byteArray(count);
306         System.arraycopy(bytes, start, result, 0, count);
307         return result;
308     }
309 
310     public static boolean startsWith(final byte[] buffer, final BinaryConstant search) {
311         if (buffer == null || buffer.length < search.size()) {
312             return false;
313         }
314 
315         for (int i = 0; i < search.size(); i++) {
316             if (buffer[i] != search.get(i)) {
317                 return false;
318             }
319         }
320 
321         return true;
322     }
323 
324     public static boolean startsWith(final byte[] buffer, final byte[] search) {
325         if (search == null) {
326             return false;
327         }
328         if (buffer == null) {
329             return false;
330         }
331         if (search.length > buffer.length) {
332             return false;
333         }
334 
335         for (int i = 0; i < search.length; i++) {
336             if (search[i] != buffer[i]) {
337                 return false;
338             }
339         }
340 
341         return true;
342     }
343 
344     private BinaryFunctions() {
345     }
346 }