123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760 |
- /*
- Copyright (c) 2005 Health Market Science, Inc.
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- USA
-
- You can contact Health Market Science at info@healthmarketscience.com
- or at the following address:
-
- Health Market Science
- 2700 Horizon Drive
- Suite 200
- King of Prussia, PA 19406
- */
-
- package com.healthmarketscience.jackcess.impl;
-
- import java.io.Closeable;
- import java.io.FileWriter;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.io.PrintWriter;
- import java.nio.ByteBuffer;
- import java.nio.ByteOrder;
- import java.util.Arrays;
-
- /**
- * Byte manipulation and display utilities
- * @author Tim McCune
- */
- public final class ByteUtil {
-
- private static final String[] HEX_CHARS = new String[] {
- "0", "1", "2", "3", "4", "5", "6", "7",
- "8", "9", "A", "B", "C", "D", "E", "F"};
-
- private static final int NUM_BYTES_PER_BLOCK = 4;
- private static final int NUM_BYTES_PER_LINE = 24;
-
- private ByteUtil() {}
-
- /**
- * Put an integer into the given buffer at the given offset as a 3-byte
- * integer.
- * @param buffer buffer into which to insert the int
- * @param val Int to convert
- */
- public static void put3ByteInt(ByteBuffer buffer, int val)
- {
- put3ByteInt(buffer, val, buffer.order());
- }
-
- /**
- * Put an integer into the given buffer at the given offset as a 3-byte
- * integer.
- * @param buffer buffer into which to insert the int
- * @param val Int to convert
- * @param order the order to insert the bytes of the int
- */
- public static void put3ByteInt(ByteBuffer buffer, int val, ByteOrder order)
- {
- int pos = buffer.position();
- put3ByteInt(buffer, val, pos, order);
- buffer.position(pos + 3);
- }
-
- /**
- * Put an integer into the given buffer at the given offset as a 3-byte
- * integer.
- * @param buffer buffer into which to insert the int
- * @param val Int to convert
- * @param offset offset at which to insert the int
- * @param order the order to insert the bytes of the int
- */
- public static void put3ByteInt(ByteBuffer buffer, int val, int offset,
- ByteOrder order) {
-
- int offInc = 1;
- if(order == ByteOrder.BIG_ENDIAN) {
- offInc = -1;
- offset += 2;
- }
-
- buffer.put(offset, (byte) (val & 0xFF));
- buffer.put(offset + (1 * offInc), (byte) ((val >>> 8) & 0xFF));
- buffer.put(offset + (2 * offInc), (byte) ((val >>> 16) & 0xFF));
- }
-
- /**
- * Read a 3 byte int from a buffer
- * @param buffer Buffer containing the bytes
- * @return The int
- */
- public static int get3ByteInt(ByteBuffer buffer) {
- return get3ByteInt(buffer, buffer.order());
- }
-
- /**
- * Read a 3 byte int from a buffer
- * @param buffer Buffer containing the bytes
- * @param order the order of the bytes of the int
- * @return The int
- */
- public static int get3ByteInt(ByteBuffer buffer, ByteOrder order) {
- int pos = buffer.position();
- int rtn = get3ByteInt(buffer, pos, order);
- buffer.position(pos + 3);
- return rtn;
- }
-
- /**
- * Read a 3 byte int from a buffer
- * @param buffer Buffer containing the bytes
- * @param offset Offset at which to start reading the int
- * @return The int
- */
- public static int get3ByteInt(ByteBuffer buffer, int offset) {
- return get3ByteInt(buffer, offset, buffer.order());
- }
-
- /**
- * Read a 3 byte int from a buffer
- * @param buffer Buffer containing the bytes
- * @param offset Offset at which to start reading the int
- * @param order the order of the bytes of the int
- * @return The int
- */
- public static int get3ByteInt(ByteBuffer buffer, int offset,
- ByteOrder order) {
-
- int offInc = 1;
- if(order == ByteOrder.BIG_ENDIAN) {
- offInc = -1;
- offset += 2;
- }
-
- int rtn = getUnsignedByte(buffer, offset);
- rtn += (getUnsignedByte(buffer, offset + (1 * offInc)) << 8);
- rtn += (getUnsignedByte(buffer, offset + (2 * offInc)) << 16);
- return rtn;
- }
-
- /**
- * Read an unsigned byte from a buffer
- * @param buffer Buffer containing the bytes
- * @return The unsigned byte as an int
- */
- public static int getUnsignedByte(ByteBuffer buffer) {
- int pos = buffer.position();
- int rtn = getUnsignedByte(buffer, pos);
- buffer.position(pos + 1);
- return rtn;
- }
-
- /**
- * Read an unsigned byte from a buffer
- * @param buffer Buffer containing the bytes
- * @param offset Offset at which to read the byte
- * @return The unsigned byte as an int
- */
- public static int getUnsignedByte(ByteBuffer buffer, int offset) {
- return asUnsignedByte(buffer.get(offset));
- }
-
- /**
- * Read an unsigned short from a buffer
- * @param buffer Buffer containing the short
- * @return The unsigned short as an int
- */
- public static int getUnsignedShort(ByteBuffer buffer) {
- int pos = buffer.position();
- int rtn = getUnsignedShort(buffer, pos);
- buffer.position(pos + 2);
- return rtn;
- }
-
- /**
- * Read an unsigned short from a buffer
- * @param buffer Buffer containing the short
- * @param offset Offset at which to read the short
- * @return The unsigned short as an int
- */
- public static int getUnsignedShort(ByteBuffer buffer, int offset) {
- return asUnsignedShort(buffer.getShort(offset));
- }
-
-
- /**
- * @param buffer Buffer containing the bytes
- * @param order the order of the bytes of the int
- * @return an int from the current position in the given buffer, read using
- * the given ByteOrder
- */
- public static int getInt(ByteBuffer buffer, ByteOrder order) {
- int offset = buffer.position();
- int rtn = getInt(buffer, offset, order);
- buffer.position(offset + 4);
- return rtn;
- }
-
- /**
- * @param buffer Buffer containing the bytes
- * @param offset Offset at which to start reading the int
- * @param order the order of the bytes of the int
- * @return an int from the given position in the given buffer, read using
- * the given ByteOrder
- */
- public static int getInt(ByteBuffer buffer, int offset, ByteOrder order) {
- ByteOrder origOrder = buffer.order();
- try {
- return buffer.order(order).getInt(offset);
- } finally {
- buffer.order(origOrder);
- }
- }
-
- /**
- * Writes an int at the current position in the given buffer, using the
- * given ByteOrder
- * @param buffer buffer into which to insert the int
- * @param val Int to insert
- * @param order the order to insert the bytes of the int
- */
- public static void putInt(ByteBuffer buffer, int val, ByteOrder order) {
- int offset = buffer.position();
- putInt(buffer, val, offset, order);
- buffer.position(offset + 4);
- }
-
- /**
- * Writes an int at the given position in the given buffer, using the
- * given ByteOrder
- * @param buffer buffer into which to insert the int
- * @param val Int to insert
- * @param offset offset at which to insert the int
- * @param order the order to insert the bytes of the int
- */
- public static void putInt(ByteBuffer buffer, int val, int offset,
- ByteOrder order)
- {
- ByteOrder origOrder = buffer.order();
- try {
- buffer.order(order).putInt(offset, val);
- } finally {
- buffer.order(origOrder);
- }
- }
-
- /**
- * Read an unsigned variable length int from a buffer
- * @param buffer Buffer containing the variable length int
- * @return The unsigned int
- */
- public static int getUnsignedVarInt(ByteBuffer buffer, int numBytes) {
- int pos = buffer.position();
- int rtn = getUnsignedVarInt(buffer, pos, numBytes);
- buffer.position(pos + numBytes);
- return rtn;
- }
-
- /**
- * Read an unsigned variable length int from a buffer
- * @param buffer Buffer containing the variable length int
- * @param offset Offset at which to read the value
- * @return The unsigned int
- */
- public static int getUnsignedVarInt(ByteBuffer buffer, int offset,
- int numBytes) {
- switch(numBytes) {
- case 1:
- return getUnsignedByte(buffer, offset);
- case 2:
- return getUnsignedShort(buffer, offset);
- case 3:
- return get3ByteInt(buffer, offset);
- case 4:
- return buffer.getInt(offset);
- default:
- throw new IllegalArgumentException("Invalid num bytes " + numBytes);
- }
- }
-
- /**
- * Reads an array of bytes from the given buffer
- * @param buffer Buffer containing the desired bytes
- * @param len length of the desired bytes
- * @return a new buffer with the given number of bytes from the current
- * position in the given buffer
- */
- public static byte[] getBytes(ByteBuffer buffer, int len)
- {
- byte[] bytes = new byte[len];
- buffer.get(bytes);
- return bytes;
- }
-
- /**
- * Reads an array of bytes from the given buffer at the given offset
- * @param buffer Buffer containing the desired bytes
- * @param offset Offset at which to read the bytes
- * @param len length of the desired bytes
- * @return a new buffer with the given number of bytes from the given
- * position in the given buffer
- */
- public static byte[] getBytes(ByteBuffer buffer, int offset, int len)
- {
- int origPos = buffer.position();
- try {
- buffer.position(offset);
- return getBytes(buffer, len);
- } finally {
- buffer.position(origPos);
- }
- }
-
- /**
- * Concatenates and returns the given byte arrays.
- */
- public static byte[] concat(byte[] b1, byte[] b2) {
- byte[] out = new byte[b1.length + b2.length];
- System.arraycopy(b1, 0, out, 0, b1.length);
- System.arraycopy(b2, 0, out, b1.length, b2.length);
- return out;
- }
-
- /**
- * Sets all bits in the given remaining byte range to 0.
- */
- public static void clearRemaining(ByteBuffer buffer)
- {
- if(!buffer.hasRemaining()) {
- return;
- }
- int pos = buffer.position();
- clearRange(buffer, pos, pos + buffer.remaining());
- }
-
- /**
- * Sets all bits in the given byte range to 0.
- */
- public static void clearRange(ByteBuffer buffer, int start,
- int end)
- {
- putRange(buffer, start, end, (byte)0x00);
- }
-
- /**
- * Sets all bits in the given byte range to 1.
- */
- public static void fillRange(ByteBuffer buffer, int start,
- int end)
- {
- putRange(buffer, start, end, (byte)0xff);
- }
-
- /**
- * Sets all bytes in the given byte range to the given byte value.
- */
- public static void putRange(ByteBuffer buffer, int start,
- int end, byte b)
- {
- for(int i = start; i < end; ++i) {
- buffer.put(i, b);
- }
- }
-
- /**
- * Matches a pattern of bytes against the given buffer starting at the given
- * offset.
- */
- public static boolean matchesRange(ByteBuffer buffer, int start,
- byte[] pattern)
- {
- for(int i = 0; i < pattern.length; ++i) {
- if(pattern[i] != buffer.get(start + i)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Searches for a pattern of bytes in the given buffer starting at the
- * given offset.
- * @return the offset of the pattern if a match is found, -1 otherwise
- */
- public static int findRange(ByteBuffer buffer, int start, byte[] pattern)
- {
- byte firstByte = pattern[0];
- int limit = buffer.limit() - pattern.length;
- for(int i = start; i < limit; ++i) {
- if((firstByte == buffer.get(i)) && matchesRange(buffer, i, pattern)) {
- return i;
- }
- }
- return -1;
- }
-
- /**
- * Convert a byte buffer to a hexadecimal string for display
- * @param buffer Buffer to display, starting at offset 0
- * @param size Number of bytes to read from the buffer
- * @return The display String
- */
- public static String toHexString(ByteBuffer buffer, int size) {
- return toHexString(buffer, 0, size);
- }
-
- /**
- * Convert a byte array to a hexadecimal string for display
- * @param array byte array to display, starting at offset 0
- * @return The display String
- */
- public static String toHexString(byte[] array) {
- return toHexString(ByteBuffer.wrap(array), 0, array.length);
- }
-
- /**
- * Convert a byte buffer to a hexadecimal string for display
- * @param buffer Buffer to display, starting at offset 0
- * @param offset Offset at which to start reading the buffer
- * @param size Number of bytes to read from the buffer
- * @return The display String
- */
- public static String toHexString(ByteBuffer buffer, int offset, int size) {
- return toHexString(buffer, offset, size, true);
- }
-
- /**
- * Convert a byte buffer to a hexadecimal string for display
- * @param buffer Buffer to display, starting at offset 0
- * @param offset Offset at which to start reading the buffer
- * @param size Number of bytes to read from the buffer
- * @param formatted flag indicating if formatting is required
- * @return The display String
- */
- public static String toHexString(ByteBuffer buffer,
- int offset, int size, boolean formatted) {
-
- StringBuilder rtn = new StringBuilder();
- int position = buffer.position();
- buffer.position(offset);
- size = Math.min(size, buffer.remaining());
-
- for (int i = 0; i < size; i++) {
- byte b = buffer.get();
- byte h = (byte) (b & 0xF0);
- h = (byte) (h >>> 4);
- h = (byte) (h & 0x0F);
- rtn.append(HEX_CHARS[h]);
- h = (byte) (b & 0x0F);
- rtn.append(HEX_CHARS[h]);
-
- int next = (i + 1);
- if(formatted && (next < size))
- {
- if((next % NUM_BYTES_PER_LINE) == 0) {
-
- rtn.append("\n");
-
- } else {
-
- rtn.append(" ");
-
- if ((next % NUM_BYTES_PER_BLOCK) == 0) {
- rtn.append(" ");
- }
- }
- }
- }
-
- buffer.position(position);
- return rtn.toString();
- }
-
- /**
- * Convert the given number of bytes from the given database page to a
- * hexidecimal string for display.
- */
- public static String toHexString(DatabaseImpl db, int pageNumber, int size)
- throws IOException
- {
- ByteBuffer buffer = db.getPageChannel().createPageBuffer();
- db.getPageChannel().readPage(buffer, pageNumber);
- return toHexString(buffer, size);
- }
-
- /**
- * Writes a sequence of hexidecimal values into the given buffer, where
- * every two characters represent one byte value.
- */
- public static void writeHexString(ByteBuffer buffer,
- String hexStr)
- throws IOException
- {
- char[] hexChars = hexStr.toCharArray();
- if((hexChars.length % 2) != 0) {
- throw new IOException("Hex string length must be even");
- }
- for(int i = 0; i < hexChars.length; i += 2) {
- String tmpStr = new String(hexChars, i, 2);
- buffer.put((byte)Long.parseLong(tmpStr, 16));
- }
- }
-
- /**
- * Writes a chunk of data to a file in pretty printed hexidecimal.
- */
- public static void toHexFile(
- String fileName,
- ByteBuffer buffer,
- int offset, int size)
- throws IOException
- {
- PrintWriter writer = new PrintWriter(
- new FileWriter(fileName));
- try {
- writer.println(toHexString(buffer, offset, size));
- } finally {
- writer.close();
- }
- }
-
- /**
- * @return the byte value converted to an unsigned int value
- */
- public static int asUnsignedByte(byte b) {
- return b & 0xFF;
- }
-
- /**
- * @return the short value converted to an unsigned int value
- */
- public static int asUnsignedShort(short s) {
- return s & 0xFFFF;
- }
-
- /**
- * Swaps the 4 bytes (changes endianness) of the bytes at the given offset.
- *
- * @param bytes buffer containing bytes to swap
- * @param offset offset of the first byte of the bytes to swap
- */
- public static void swap4Bytes(byte[] bytes, int offset)
- {
- byte b = bytes[offset + 0];
- bytes[offset + 0] = bytes[offset + 3];
- bytes[offset + 3] = b;
- b = bytes[offset + 1];
- bytes[offset + 1] = bytes[offset + 2];
- bytes[offset + 2] = b;
- }
-
- /**
- * Swaps the 2 bytes (changes endianness) of the bytes at the given offset.
- *
- * @param bytes buffer containing bytes to swap
- * @param offset offset of the first byte of the bytes to swap
- */
- public static void swap2Bytes(byte[] bytes, int offset)
- {
- byte b = bytes[offset + 0];
- bytes[offset + 0] = bytes[offset + 1];
- bytes[offset + 1] = b;
- }
-
- /**
- * Moves the position of the given buffer the given count from the current
- * position.
- * @return the new buffer position
- */
- public static int forward(ByteBuffer buffer, int count)
- {
- int newPos = buffer.position() + count;
- buffer.position(newPos);
- return newPos;
- }
-
- /**
- * Returns a copy of the given array of the given length.
- */
- public static byte[] copyOf(byte[] arr, int newLength)
- {
- return copyOf(arr, 0, newLength);
- }
-
- /**
- * Returns a copy of the given array of the given length starting at the
- * given position.
- */
- public static byte[] copyOf(byte[] arr, int offset, int newLength)
- {
- byte[] newArr = new byte[newLength];
- int srcLen = arr.length - offset;
- System.arraycopy(arr, offset, newArr, 0, Math.min(srcLen, newLength));
- return newArr;
- }
-
- /**
- * Copies the given InputStream to the given OutputStream.
- */
- public static void copy(InputStream in, OutputStream out) throws IOException {
- byte[] buf = new byte[8 * 1024];
- int read = 0;
- while((read = in.read(buf)) > -1) {
- out.write(buf, 0, read);
- }
- }
-
- /**
- * Closes the given Closeable if non-null, swallows any IOExceptions.
- */
- public static void closeQuietly(Closeable c) {
- if(c != null) {
- try {
- c.close();
- } catch(IOException ignored) {}
- }
- }
-
- /**
- * Utility byte stream similar to ByteArrayOutputStream but with extended
- * accessibility to the bytes.
- */
- public static class ByteStream extends OutputStream
- {
- private byte[] _bytes;
- private int _length;
- private int _lastLength;
-
-
- public ByteStream() {
- this(32);
- }
-
- public ByteStream(int capacity) {
- _bytes = new byte[capacity];
- }
-
- public int getLength() {
- return _length;
- }
-
- public byte[] getBytes() {
- return _bytes;
- }
-
- protected void ensureNewCapacity(int numBytes) {
- int newLength = _length + numBytes;
- if(newLength > _bytes.length) {
- byte[] temp = new byte[newLength * 2];
- System.arraycopy(_bytes, 0, temp, 0, _length);
- _bytes = temp;
- }
- }
-
- @Override
- public void write(int b) {
- ensureNewCapacity(1);
- _bytes[_length++] = (byte)b;
- }
-
- @Override
- public void write(byte[] b) {
- write(b, 0, b.length);
- }
-
- @Override
- public void write(byte[] b, int offset, int length) {
- ensureNewCapacity(length);
- System.arraycopy(b, offset, _bytes, _length, length);
- _length += length;
- }
-
- public byte get(int offset) {
- return _bytes[offset];
- }
-
- public void set(int offset, byte b) {
- _bytes[offset] = b;
- }
-
- public void writeFill(int length, byte b) {
- ensureNewCapacity(length);
- int oldLength = _length;
- _length += length;
- Arrays.fill(_bytes, oldLength, _length, b);
- }
-
- public void skip(int n) {
- ensureNewCapacity(n);
- _length += n;
- }
-
- public void writeTo(ByteStream out) {
- out.write(_bytes, 0, _length);
- }
-
- public byte[] toByteArray() {
-
- byte[] result = null;
- if(_length == _bytes.length) {
- result = _bytes;
- _bytes = null;
- } else {
- result = copyOf(_bytes, _length);
- if(_lastLength == _length) {
- // if we get the same result length bytes twice in a row, clear the
- // _bytes so that the next _bytes will be _lastLength
- _bytes = null;
- }
- }
-
- // save result length so we can potentially get the right length of the
- // next byte[] in reset()
- _lastLength = _length;
-
- return result;
- }
-
- public void reset() {
- _length = 0;
- if(_bytes == null) {
- _bytes = new byte[_lastLength];
- }
- }
-
- public void trimTrailing(byte minTrimCode, byte maxTrimCode)
- {
- int minTrim = ByteUtil.asUnsignedByte(minTrimCode);
- int maxTrim = ByteUtil.asUnsignedByte(maxTrimCode);
-
- int idx = _length - 1;
- while(idx >= 0) {
- int val = asUnsignedByte(get(idx));
- if((val >= minTrim) && (val <= maxTrim)) {
- --idx;
- } else {
- break;
- }
- }
-
- _length = idx + 1;
- }
- }
-
- }
|