View Javadoc
1   /*
2   Copyright (c) 2005 Health Market Science, Inc.
3   
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7   
8       http://www.apache.org/licenses/LICENSE-2.0
9   
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15  */
16  
17  package com.healthmarketscience.jackcess.impl;
18  
19  import java.io.Closeable;
20  import java.io.FileWriter;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.OutputStream;
24  import java.io.PrintWriter;
25  import java.nio.ByteBuffer;
26  import java.nio.ByteOrder;
27  import java.util.Arrays;
28  
29  /**
30   * Byte manipulation and display utilities
31   * @author Tim McCune
32   */
33  public final class ByteUtil {
34    
35    private static final String[] HEX_CHARS = new String[] {
36        "0", "1", "2", "3", "4", "5", "6", "7",
37        "8", "9", "A", "B", "C", "D", "E", "F"};
38  
39    private static final int NUM_BYTES_PER_BLOCK = 4;
40    private static final int NUM_BYTES_PER_LINE = 24;
41        
42    private ByteUtil() {}
43  
44    /**
45     * Put an integer into the given buffer at the given offset as a 3-byte
46     * integer.
47     * @param buffer buffer into which to insert the int
48     * @param val Int to convert
49     */    
50    public static void put3ByteInt(ByteBuffer buffer, int val)
51    {
52      put3ByteInt(buffer, val, buffer.order());
53    }
54    
55    /**
56     * Put an integer into the given buffer at the given offset as a 3-byte
57     * integer.
58     * @param buffer buffer into which to insert the int
59     * @param val Int to convert
60     * @param order  the order to insert the bytes of the int
61     */    
62    public static void put3ByteInt(ByteBuffer buffer, int val, ByteOrder order)
63    {
64      int pos = buffer.position();
65      put3ByteInt(buffer, val, pos, order);
66      buffer.position(pos + 3);
67    }
68    
69    /**
70     * Put an integer into the given buffer at the given offset as a 3-byte
71     * integer.
72     * @param buffer buffer into which to insert the int
73     * @param val Int to convert
74     * @param offset offset at which to insert the int
75     * @param order  the order to insert the bytes of the int
76     */    
77    public static void put3ByteInt(ByteBuffer buffer, int val, int offset,
78                                   ByteOrder order) {
79  
80      int offInc = 1;
81      if(order == ByteOrder.BIG_ENDIAN) {
82        offInc = -1;
83        offset += 2;
84      }
85  
86      buffer.put(offset, (byte) (val & 0xFF));
87      buffer.put(offset + (1 * offInc), (byte) ((val >>> 8) & 0xFF));
88      buffer.put(offset + (2 * offInc), (byte) ((val >>> 16) & 0xFF));
89    }
90  
91    /**
92     * Read a 3 byte int from a buffer
93     * @param buffer Buffer containing the bytes
94     * @return The int
95     */
96    public static int get3ByteInt(ByteBuffer buffer) {
97      return get3ByteInt(buffer, buffer.order());
98    }
99  
100   /**
101    * Read a 3 byte int from a buffer
102    * @param buffer Buffer containing the bytes
103    * @param order  the order of the bytes of the int
104    * @return The int
105    */
106   public static int get3ByteInt(ByteBuffer buffer, ByteOrder order) {  
107     int pos = buffer.position();
108     int rtn = get3ByteInt(buffer, pos, order);
109     buffer.position(pos + 3);
110     return rtn;
111   }
112 
113   /**
114    * Read a 3 byte int from a buffer
115    * @param buffer Buffer containing the bytes
116    * @param offset Offset at which to start reading the int
117    * @return The int
118    */
119   public static int get3ByteInt(ByteBuffer buffer, int offset) {
120     return get3ByteInt(buffer, offset, buffer.order());
121   }
122   
123   /**
124    * Read a 3 byte int from a buffer
125    * @param buffer Buffer containing the bytes
126    * @param offset Offset at which to start reading the int
127    * @param order  the order of the bytes of the int
128    * @return The int
129    */
130   public static int get3ByteInt(ByteBuffer buffer, int offset,
131                                 ByteOrder order) {
132 
133     int offInc = 1;
134     if(order == ByteOrder.BIG_ENDIAN) {
135       offInc = -1;
136       offset += 2;
137     }
138     
139     int rtn = getUnsignedByte(buffer, offset);
140     rtn += (getUnsignedByte(buffer, offset + (1 * offInc)) << 8);
141     rtn += (getUnsignedByte(buffer, offset + (2 * offInc)) << 16);
142     return rtn;
143   }
144 
145   /**
146    * Read an unsigned byte from a buffer
147    * @param buffer Buffer containing the bytes
148    * @return The unsigned byte as an int
149    */
150   public static int getUnsignedByte(ByteBuffer buffer) {
151     int pos = buffer.position();
152     int rtn = getUnsignedByte(buffer, pos);
153     buffer.position(pos + 1);
154     return rtn;
155   }
156 
157   /**
158    * Read an unsigned byte from a buffer
159    * @param buffer Buffer containing the bytes
160    * @param offset Offset at which to read the byte
161    * @return The unsigned byte as an int
162    */
163   public static int getUnsignedByte(ByteBuffer buffer, int offset) {  
164     return asUnsignedByte(buffer.get(offset));
165   }
166   
167   /**
168    * Read an unsigned short from a buffer
169    * @param buffer Buffer containing the short
170    * @return The unsigned short as an int
171    */
172   public static int getUnsignedShort(ByteBuffer buffer) {
173     int pos = buffer.position();
174     int rtn = getUnsignedShort(buffer, pos);
175     buffer.position(pos + 2);
176     return rtn;
177   }
178 
179   /**
180    * Read an unsigned short from a buffer
181    * @param buffer Buffer containing the short
182    * @param offset Offset at which to read the short
183    * @return The unsigned short as an int
184    */
185   public static int getUnsignedShort(ByteBuffer buffer, int offset) {  
186     return asUnsignedShort(buffer.getShort(offset));
187   }
188 
189   
190   /**
191    * @param buffer Buffer containing the bytes
192    * @param order  the order of the bytes of the int
193    * @return an int from the current position in the given buffer, read using
194    *         the given ByteOrder
195    */
196   public static int getInt(ByteBuffer buffer, ByteOrder order) {
197     int offset = buffer.position();
198     int rtn = getInt(buffer, offset, order);
199     buffer.position(offset + 4);
200     return rtn;
201   }
202   
203   /**
204    * @param buffer Buffer containing the bytes
205    * @param offset Offset at which to start reading the int
206    * @param order  the order of the bytes of the int
207    * @return an int from the given position in the given buffer, read using
208    *         the given ByteOrder
209    */
210   public static int getInt(ByteBuffer buffer, int offset, ByteOrder order) {
211     ByteOrder origOrder = buffer.order();
212     try {
213       return buffer.order(order).getInt(offset);
214     } finally {
215       buffer.order(origOrder);
216     }
217   }
218   
219   /**
220    * Writes an int at the current position in the given buffer, using the
221    * given ByteOrder
222    * @param buffer buffer into which to insert the int
223    * @param val Int to insert
224    * @param order the order to insert the bytes of the int
225    */
226   public static void putInt(ByteBuffer buffer, int val, ByteOrder order) {
227     int offset = buffer.position();
228     putInt(buffer, val, offset, order);
229     buffer.position(offset + 4);
230   }
231   
232   /**
233    * Writes an int at the given position in the given buffer, using the
234    * given ByteOrder
235    * @param buffer buffer into which to insert the int
236    * @param val Int to insert
237    * @param offset offset at which to insert the int
238    * @param order the order to insert the bytes of the int
239    */
240   public static void putInt(ByteBuffer buffer, int val, int offset,
241                             ByteOrder order)
242   {
243     ByteOrder origOrder = buffer.order();
244     try {
245       buffer.order(order).putInt(offset, val);
246     } finally {
247       buffer.order(origOrder);
248     }
249   }
250 
251   /**
252    * Read an unsigned variable length int from a buffer
253    * @param buffer Buffer containing the variable length int
254    * @return The unsigned int
255    */
256   public static int getUnsignedVarInt(ByteBuffer buffer, int numBytes) {
257     int pos = buffer.position();
258     int rtn = getUnsignedVarInt(buffer, pos, numBytes);
259     buffer.position(pos + numBytes);
260     return rtn;
261   }
262 
263   /**
264    * Read an unsigned variable length int from a buffer
265    * @param buffer Buffer containing the variable length int
266    * @param offset Offset at which to read the value
267    * @return The unsigned int
268    */
269   public static int getUnsignedVarInt(ByteBuffer buffer, int offset, 
270                                       int numBytes) {  
271     switch(numBytes) {
272     case 1:
273       return getUnsignedByte(buffer, offset);
274     case 2:
275       return getUnsignedShort(buffer, offset);
276     case 3:
277       return get3ByteInt(buffer, offset);
278     case 4:
279       return buffer.getInt(offset);
280     default:
281       throw new IllegalArgumentException("Invalid num bytes " + numBytes);
282     }
283   }
284 
285   /**
286    * Reads an array of bytes from the given buffer
287    * @param buffer Buffer containing the desired bytes
288    * @param len length of the desired bytes
289    * @return a new buffer with the given number of bytes from the current
290    *         position in the given buffer
291    */
292   public static byte[] getBytes(ByteBuffer buffer, int len)
293   {
294       byte[] bytes = new byte[len];
295       buffer.get(bytes);    
296       return bytes;
297   }
298 
299   /**
300    * Reads an array of bytes from the given buffer at the given offset
301    * @param buffer Buffer containing the desired bytes
302    * @param offset Offset at which to read the bytes
303    * @param len length of the desired bytes
304    * @return a new buffer with the given number of bytes from the given
305    *         position in the given buffer
306    */
307   public static byte[] getBytes(ByteBuffer buffer, int offset, int len)
308   {
309     int origPos = buffer.position();
310     try {
311       buffer.position(offset);
312       return getBytes(buffer, len);
313     } finally {
314       buffer.position(origPos);
315     }
316   }
317 
318   /**
319    * Concatenates and returns the given byte arrays.
320    */
321   public static byte[] concat(byte[] b1, byte[] b2) {
322     byte[] out = new byte[b1.length + b2.length];
323     System.arraycopy(b1, 0, out, 0, b1.length);
324     System.arraycopy(b2, 0, out, b1.length, b2.length);
325     return out;
326   }
327   
328   /**
329    * Sets all bits in the given remaining byte range to 0.
330    */
331   public static void clearRemaining(ByteBuffer buffer)
332   {
333     if(!buffer.hasRemaining()) {
334       return;
335     }
336     int pos = buffer.position();
337     clearRange(buffer, pos, pos + buffer.remaining());
338   }
339 
340   /**
341    * Sets all bits in the given byte range to 0.
342    */
343   public static void clearRange(ByteBuffer buffer, int start,
344                                 int end)
345   {
346     putRange(buffer, start, end, (byte)0x00);
347   }
348 
349   /**
350    * Sets all bits in the given byte range to 1.
351    */
352   public static void fillRange(ByteBuffer buffer, int start,
353                                int end)
354   {
355     putRange(buffer, start, end, (byte)0xff);
356   }
357   
358   /**
359    * Sets all bytes in the given byte range to the given byte value.
360    */
361   public static void putRange(ByteBuffer buffer, int start,
362                               int end, byte b)
363   {
364     for(int i = start; i < end; ++i) {
365       buffer.put(i, b);
366     }
367   }
368 
369   /**
370    * Matches a pattern of bytes against the given buffer starting at the given
371    * offset.
372    */
373   public static boolean matchesRange(ByteBuffer buffer, int start,
374                                      byte[] pattern)
375   {
376     for(int i = 0; i < pattern.length; ++i) {
377       if(pattern[i] != buffer.get(start + i)) {
378         return false;
379       }
380     }
381     return true;
382   }
383 
384   /**
385    * Searches for a pattern of bytes in the given buffer starting at the
386    * given offset.
387    * @return the offset of the pattern if a match is found, -1 otherwise
388    */
389   public static int findRange(ByteBuffer buffer, int start, byte[] pattern)
390   {
391     byte firstByte = pattern[0];
392     int limit = buffer.limit() - pattern.length;
393     for(int i = start; i < limit; ++i) {
394       if((firstByte == buffer.get(i)) && matchesRange(buffer, i, pattern)) {
395         return i;
396       }
397     }
398     return -1;
399   }
400 
401   /**
402    * Inserts empty data of the given length at the current position of the
403    * given buffer (moving existing data forward the given length).  The limit
404    * of the buffer is adjusted by the given length.  The buffer is expecting
405    * to have the required capacity available.
406    */
407   public static void insertEmptyData(ByteBuffer buffer, int len) {
408     byte[] buf = buffer.array();
409     int pos = buffer.position();
410     int limit = buffer.limit();
411     System.arraycopy(buf, pos, buf, pos + len, limit - pos);
412     Arrays.fill(buf, pos, pos + len, (byte)0);
413     buffer.limit(limit + len);
414   }
415   
416   /**
417    * Convert a byte buffer to a hexadecimal string for display
418    * @param buffer Buffer to display, starting at offset 0
419    * @param size Number of bytes to read from the buffer
420    * @return The display String
421    */
422   public static String toHexString(ByteBuffer buffer, int size) {
423     return toHexString(buffer, 0, size);
424   }
425   
426   /**
427    * Convert a byte array to a hexadecimal string for display
428    * @param array byte array to display, starting at offset 0
429    * @return The display String
430    */
431   public static String toHexString(byte[] array) {
432     return toHexString(ByteBuffer.wrap(array), 0, array.length);
433   }
434   
435   /**
436    * Convert a byte buffer to a hexadecimal string for display
437    * @param buffer Buffer to display, starting at offset 0
438    * @param offset Offset at which to start reading the buffer
439    * @param size Number of bytes to read from the buffer
440    * @return The display String
441    */
442   public static String toHexString(ByteBuffer buffer, int offset, int size) {
443     return toHexString(buffer, offset, size, true);
444   }    
445 
446   /**
447    * Convert a byte buffer to a hexadecimal string for display
448    * @param buffer Buffer to display, starting at offset 0
449    * @param offset Offset at which to start reading the buffer
450    * @param size Number of bytes to read from the buffer
451    * @param formatted flag indicating if formatting is required
452    * @return The display String
453    */
454   public static String toHexString(ByteBuffer buffer,
455                                    int offset, int size, boolean formatted) {
456     
457     int bufLen = size * 2;
458     if(formatted) {
459       bufLen += size + 
460         (7 * ((size + NUM_BYTES_PER_LINE - 1) / NUM_BYTES_PER_LINE));
461     }
462     StringBuilder rtn = new StringBuilder(bufLen);
463     int position = buffer.position();
464     buffer.position(offset);
465     size = Math.min(size, buffer.remaining());
466 
467     for (int i = 0; i < size; i++) {
468       byte b = buffer.get();
469       byte h = (byte) (b & 0xF0);
470       h = (byte) (h >>> 4);
471       h = (byte) (h & 0x0F);
472       rtn.append(HEX_CHARS[h]);
473       h = (byte) (b & 0x0F);
474       rtn.append(HEX_CHARS[h]);
475 
476       int next = (i + 1);
477       if(formatted && (next < size))
478       {
479         if((next % NUM_BYTES_PER_LINE) == 0) {
480 
481           rtn.append("\n");
482 
483         } else {
484           
485           rtn.append(" ");
486 
487           if ((next % NUM_BYTES_PER_BLOCK) == 0) {
488             rtn.append(" ");
489           }
490         }
491       }
492     }
493 
494     buffer.position(position);
495     return rtn.toString();
496   }
497 
498   /**
499    * Convert the given number of bytes from the given database page to a
500    * hexidecimal string for display.
501    */
502   public static String toHexString(DatabaseImpl db, int pageNumber, int size)
503     throws IOException
504   {
505     ByteBuffer buffer = db.getPageChannel().createPageBuffer();
506     db.getPageChannel().readPage(buffer, pageNumber);
507     return toHexString(buffer, size);
508   }
509 
510   /**
511    * Writes a sequence of hexidecimal values into the given buffer, where
512    * every two characters represent one byte value.
513    */
514   public static void writeHexString(ByteBuffer buffer,
515                                     String hexStr)
516     throws IOException
517   {
518     char[] hexChars = hexStr.toCharArray();
519     if((hexChars.length % 2) != 0) {
520       throw new IOException("Hex string length must be even");
521     }
522     for(int i = 0; i < hexChars.length; i += 2) {
523       String tmpStr = new String(hexChars, i, 2);
524       buffer.put((byte)Integer.parseInt(tmpStr, 16));
525     }
526   }
527 
528   /**
529    * Writes a chunk of data to a file in pretty printed hexidecimal.
530    */
531   public static void toHexFile(
532       String fileName,
533       ByteBuffer buffer, 
534       int offset, int size)
535     throws IOException
536   {
537     PrintWriter writer = new PrintWriter(
538         new FileWriter(fileName));
539     try {
540       writer.println(toHexString(buffer, offset, size));
541     } finally {
542       writer.close();
543     }
544   }
545 
546   /**
547    * @return the byte value converted to an unsigned int value
548    */
549   public static int asUnsignedByte(byte b) { 
550     return b & 0xFF;
551   }
552   
553   /**
554    * @return the short value converted to an unsigned int value
555    */
556   public static int asUnsignedShort(short s) { 
557     return s & 0xFFFF;
558   }
559 
560   /**
561    * Swaps the 8 bytes (changes endianness) of the bytes at the given offset.
562    *
563    * @param bytes buffer containing bytes to swap
564    * @param offset offset of the first byte of the bytes to swap
565    */
566   public static void swap8Bytes(byte[] bytes, int offset)
567   {
568     swapBytesAt(bytes, offset + 0, offset + 7);
569     swapBytesAt(bytes, offset + 1, offset + 6);
570     swapBytesAt(bytes, offset + 2, offset + 5);
571     swapBytesAt(bytes, offset + 3, offset + 4);
572   }
573 
574   /**
575    * Swaps the 4 bytes (changes endianness) of the bytes at the given offset.
576    *
577    * @param bytes buffer containing bytes to swap
578    * @param offset offset of the first byte of the bytes to swap
579    */
580   public static void swap4Bytes(byte[] bytes, int offset)
581   {
582     swapBytesAt(bytes, offset + 0, offset + 3);
583     swapBytesAt(bytes, offset + 1, offset + 2);
584   }
585 
586   /**
587    * Swaps the 2 bytes (changes endianness) of the bytes at the given offset.
588    *
589    * @param bytes buffer containing bytes to swap
590    * @param offset offset of the first byte of the bytes to swap
591    */
592   public static void swap2Bytes(byte[] bytes, int offset)
593   {
594     swapBytesAt(bytes, offset + 0, offset + 1);
595   }
596 
597   /**
598    * Swaps the bytes at the given positions.
599    */
600   private static void swapBytesAt(byte[] bytes, int p1, int p2)
601   {
602     byte b = bytes[p1];
603     bytes[p1] = bytes[p2];
604     bytes[p2] = b;
605   }
606 
607   /**
608    * Moves the position of the given buffer the given count from the current
609    * position.
610    * @return the new buffer position
611    */
612   public static int forward(ByteBuffer buffer, int count)
613   {
614     int newPos = buffer.position() + count;
615     buffer.position(newPos);
616     return newPos;
617   }
618 
619   /**
620    * Returns a copy of the given array of the given length.
621    */
622   public static byte[] copyOf(byte[] arr, int newLength)
623   {
624     return copyOf(arr, 0, newLength, 0);
625   }
626 
627   /**
628    * Returns a copy of the given array of the given length starting at the
629    * given position.
630    */
631   public static byte[] copyOf(byte[] arr, int offset, int newLength)
632   {
633     return copyOf(arr, offset, newLength, 0);
634   }
635 
636   /**
637    * Returns a copy of the given array of the given length starting at the
638    * given position.
639    */
640   public static byte[] copyOf(byte[] arr, int offset, int newLength, 
641                               int dstOffset)
642   {
643     byte[] newArr = new byte[newLength];
644     int srcLen = arr.length - offset;
645     int dstLen = newLength - dstOffset;
646     System.arraycopy(arr, offset, newArr, dstOffset, Math.min(srcLen, dstLen));
647     return newArr;
648   }
649 
650   /**
651    * Copies the given InputStream to the given OutputStream.
652    */
653   public static void copy(InputStream in, OutputStream out) throws IOException {
654     byte[] buf = new byte[8 * 1024];
655     int read = 0;
656     while((read = in.read(buf)) > -1) {
657       out.write(buf, 0, read);
658     }
659   }
660 
661   /**
662    * Closes the given Closeable if non-null, swallows any IOExceptions.
663    */
664   public static void closeQuietly(Closeable c) {
665     if(c != null) {
666       try {
667         c.close();
668       } catch(IOException ignored) {}
669     }
670   }
671 
672   /**
673    * Utility byte stream similar to ByteArrayOutputStream but with extended
674    * accessibility to the bytes.
675    */
676   public static class ByteStream extends OutputStream
677   {
678     private byte[] _bytes;
679     private int _length;
680     private int _lastLength;
681 
682 
683     public ByteStream() {
684       this(32);
685     }
686 
687     public ByteStream(int capacity) {
688       _bytes = new byte[capacity];
689     }
690 
691     public int getLength() {
692       return _length;
693     }
694 
695     public byte[] getBytes() {
696       return _bytes;
697     }
698 
699     protected void ensureNewCapacity(int numBytes) {
700       int newLength = _length + numBytes;
701       if(newLength > _bytes.length) {
702         byte[] temp = new byte[newLength * 2];
703         System.arraycopy(_bytes, 0, temp, 0, _length);
704         _bytes = temp;
705       }
706     }
707 
708     @Override
709     public void write(int b) {
710       ensureNewCapacity(1);
711       _bytes[_length++] = (byte)b;
712     }
713 
714     @Override
715     public void write(byte[] b) {
716       write(b, 0, b.length);
717     }
718 
719     @Override
720     public void write(byte[] b, int offset, int length) {
721       ensureNewCapacity(length);
722       System.arraycopy(b, offset, _bytes, _length, length);
723       _length += length;
724     }
725 
726     public byte get(int offset) {
727       return _bytes[offset];
728     }
729 
730     public void set(int offset, byte b) {
731       _bytes[offset] = b;
732     }
733 
734     public void writeFill(int length, byte b) {
735       ensureNewCapacity(length);
736       int oldLength = _length;
737       _length += length;
738       Arrays.fill(_bytes, oldLength, _length, b);
739     }
740 
741     public void skip(int n) {
742       ensureNewCapacity(n);
743       _length += n;
744     }
745 
746     public void writeTo(ByteStream out) {
747       out.write(_bytes, 0, _length);
748     }
749 
750     public byte[] toByteArray() {
751 
752       byte[] result = null;
753       if(_length == _bytes.length) {
754         result = _bytes;
755         _bytes = null;
756       } else {
757         result = copyOf(_bytes, _length);
758         if(_lastLength == _length) {
759           // if we get the same result length bytes twice in a row, clear the
760           // _bytes so that the next _bytes will be _lastLength
761           _bytes = null;
762         }
763       }
764 
765       // save result length so we can potentially get the right length of the
766       // next byte[] in reset()
767       _lastLength = _length;
768 
769       return result;
770     }
771 
772     public void reset() {
773       _length = 0;
774       if(_bytes == null) {
775         _bytes = new byte[_lastLength];
776       }
777     }
778 
779     public void trimTrailing(byte minTrimCode, byte maxTrimCode)
780     {
781       int minTrim = ByteUtil.asUnsignedByte(minTrimCode);
782       int maxTrim = ByteUtil.asUnsignedByte(maxTrimCode);
783 
784       int idx = _length - 1;
785       while(idx >= 0) {
786         int val = asUnsignedByte(get(idx));
787         if((val >= minTrim) && (val <= maxTrim)) {
788           --idx;
789         } else {
790           break;
791         }
792       }
793 
794       _length = idx + 1;
795     }
796   }
797 
798 }