001: package uk.org.ponder.byteutil;
002:
003: import java.text.DecimalFormat;
004:
005: /** A very useful utility class to wrap a sequence of bytes in an array, covering over to
006: * some extent the lack of pointers in the Java language.
007: */
008:
009: public class ByteWrap {
010: /** The array holding the bytes represented by this ByteWrap. */
011: public byte[] bytes;
012:
013: /** The offset within the array that the represented bytes begin. */
014: public int offset;
015:
016: /** The number of bytes starting at <code>offset</code> that form the represented
017: * sequence. Making this protected is an oddly inconistent decision, so sue me. */
018: protected int size;
019:
020: /** The default constructor performs no initialization, for those who wish to use
021: * ByteWraps in the raw.
022: */
023: public ByteWrap() {
024: }
025:
026: /** Constructs a new ByteWrap with the specified length. A new array of the correct
027: * length is constructed to represent the sequence, which begins at the beginning of
028: * the array.
029: * @param length The length of the ByteWrap to be constructed. */
030:
031: public ByteWrap(int length) {
032: bytes = new byte[length];
033: size = length;
034: }
035:
036: /** Constructs a new ByteWrap wrapping a portion of an already existing array.
037: * @param bytes The array holding the sequence to be wrapped.
038: * @param offset The index of the beginning of the sequence to be wrapped.
039: * @param size The length of the sequence to be wrapped.
040: */
041:
042: public ByteWrap(byte[] bytes, int offset, int size) {
043: this .bytes = bytes;
044: this .offset = offset;
045: this .size = size;
046: }
047:
048: /** Imbues an already existing ByteWrap to wrap a portion of another ByteWrap.
049: * @param other The other ByteWrap containing the sequence to be wrapped.
050: * @param index The index within the other ByteWrap's sequence of the sequence
051: * to be wrapped.
052: * @param length The length of the sequence to be wrapped.
053: */
054:
055: public ByteWrap imbue(ByteWrap other, int index, int length) {
056: index += other.offset;
057: bytes = other.bytes;
058: offset = index;
059: size = length;
060: return this ;
061: }
062:
063: /** Ensures that this ByteWrap has enough capacity in its array to store content
064: * of the specified size, by rebinding it to a larger-sized array if necessary.
065: * @param requiredsize The length of the sequence the ByteWrap is required to
066: * store.
067: */
068:
069: public void ensureCapacity(int requiredsize) {
070: if (bytes == null || bytes.length < requiredsize) {
071: bytes = new byte[requiredsize];
072: offset = 0; // set offset to 0, although strictly should not be called on shared array
073: }
074: size = requiredsize;
075: }
076:
077: // All methods after this point do NOT rebind the array reference.
078:
079: /** Writes the supplied <code>long</code> one byte at a time starting at the specified
080: * position in the wrapped sequence. The data is written in the normal big-endian order.
081: * @param pos The position to start writing the bytes.
082: * @param data The data to be written.
083: */
084:
085: public void write_at8(int pos, long data) {
086: pos += offset;
087: bytes[pos++] = (byte) (data >> 56);
088: bytes[pos++] = (byte) (data >> 48);
089: bytes[pos++] = (byte) (data >> 40);
090: bytes[pos++] = (byte) (data >> 32);
091: bytes[pos++] = (byte) (data >> 24);
092: bytes[pos++] = (byte) (data >> 16);
093: bytes[pos++] = (byte) (data >> 8);
094: bytes[pos] = (byte) data;
095: }
096:
097: /** Reads the bytes starting at the specified position, interpreting them as a
098: * <code>long</code> in the usual big-endian order.
099: * @param pos The position from which to read bytes.
100: * @return The byte sequence interpreted as a <code>long</code>
101: */
102:
103: public long read_at8(int pos) {
104: pos += offset;
105: // masking is necessary since widening conversions extend sign bit
106: // casting is necessary, although the langspec is ambiguous!! 4.2.2
107: return ((long) (bytes[pos] & 0xff) << 56)
108: + ((long) (bytes[pos + 1] & 0xff) << 48)
109: + ((long) (bytes[pos + 2] & 0xff) << 40)
110: + ((long) (bytes[pos + 3] & 0xff) << 32)
111: + ((bytes[pos + 4] & 0xff) << 24)
112: + ((bytes[pos + 5] & 0xff) << 16)
113: + ((bytes[pos + 6] & 0xff) << 8)
114: + (bytes[pos + 7] & 0xff);
115: }
116:
117: /** Writes the supplied <code>int</code> one byte at a time starting at the specified
118: * position in the wrapped sequence. The data is written in the normal big-endian order.
119: * @param pos The position to start writing the bytes.
120: * @param data The data to be written.
121: */
122:
123: public void write_at4(int pos, int data) {
124: pos += offset;
125: // NB narrowing conversions in Java automatically mask
126: bytes[pos++] = (byte) (data >> 24);
127: bytes[pos++] = (byte) (data >> 16);
128: bytes[pos++] = (byte) (data >> 8);
129: bytes[pos] = (byte) data;
130: }
131:
132: /** Reads the bytes starting at the specified position, interpreting them as a
133: * <code>int</code> in the usual big-endian order.
134: * @param pos The position from which to read bytes.
135: * @return The byte sequence interpreted as a <code>int</code>
136: */
137:
138: public int read_at4(int pos) {
139: pos += offset;
140: // masking is necessary since widening conversions extend sign bit
141: return ((bytes[pos] & 0xff) << 24)
142: + ((bytes[pos + 1] & 0xff) << 16)
143: + ((bytes[pos + 2] & 0xff) << 8)
144: + (bytes[pos + 3] & 0xff);
145: }
146:
147: /** Writes the three low-order bytes of the supplied <code>int</code> one
148: * byte at a time starting at the specified position in the wrapped
149: * sequence. The data is written in the normal big-endian order.
150: * @param pos The position to start writing the bytes.
151: * @param data The data to be written.
152: */
153:
154: public void write_at3(int pos, int data) {
155: pos += offset;
156: bytes[pos++] = (byte) (data >> 16);
157: bytes[pos++] = (byte) (data >> 8);
158: bytes[pos] = (byte) data;
159: }
160:
161: /** Reads three bytes starting at the specified position, interpreting
162: * them as the low-order bytes of an <code>int</code> in the usual
163: * big-endian order.
164: * @param pos The position from which to read bytes.
165: * @return The byte sequence interpreted as a <code>int</code>
166: */
167:
168: public int read_at3(int pos) {
169: pos += offset;
170: return ((bytes[pos] & 0xff) << 16)
171: + ((bytes[pos + 1] & 0xff) << 8)
172: + (bytes[pos + 2] & 0xff);
173: }
174:
175: /** Writes the two low-order bytes of the supplied <code>int</code> one
176: * byte at a time starting at the specified position in the wrapped
177: * sequence. The data is written in the normal big-endian order.
178: * @param pos The position to start writing the bytes.
179: * @param data The data to be written.
180: */
181:
182: public void write_at2(int pos, int data) {
183: pos += offset;
184: bytes[pos++] = (byte) (data >> 8);
185: bytes[pos] = (byte) data;
186: }
187:
188: /** Reads two bytes starting at the specified position, interpreting
189: * them as the low-order bytes of an <code>int</code> in the usual
190: * big-endian order.
191: * @param pos The position from which to read bytes.
192: * @return The byte sequence interpreted as a <code>int</code>
193: */
194:
195: public int read_at2(int pos) {
196: pos += offset;
197: return ((bytes[pos] & 0xff) << 8) + (bytes[pos + 1] & 0xff);
198: }
199:
200: /** Writes the low-order byte of the supplied <code>int</code> at the
201: * specified position in the wrapped sequence.
202: * @param pos The position to write the byte.
203: * @param data The data to be written.
204: */
205:
206: public void write_at1(int pos, int data) {
207: bytes[pos + offset] = (byte) data;
208: }
209:
210: /** Reads a byte from the specified position, interpreting
211: * it as the low-order byte of an <code>int</code>.
212: * @param pos The position from which to read the byte.
213: * @return The byte sequence interpreted as a <code>int</code>
214: */
215:
216: public int read_at1(int pos) {
217: return bytes[pos + offset] & 0xff;
218: }
219:
220: /** Writes the entire sequence from another ByteWrap to a specified position into
221: * this one. The target ByteWrap is assumed to have enough space to accommodate the
222: * data.
223: * @param pos The position within this ByteWrap to write the sequence.
224: * @param other The ByteWrap wrapping the sequence to be copied.
225: */
226:
227: public void write_at(int pos, ByteWrap other) {
228: pos += offset;
229: for (int i = 0; i < other.size; ++i) {
230: bytes[pos + i] = other.bytes[other.offset + i];
231: }
232: }
233:
234: /** Writes a portion of the sequence from another ByteWrap to a specified position into
235: * this one. The target ByteWrap is assumed to have enough space to accommodate the
236: * data.
237: * @param pos The position within this ByteWrap to write the sequence.
238: * @param other The ByteWrap wrapping the sequence to be copied.
239: * @param index The index within the other ByteWrap marking the start of the sequence
240: * to be copied.
241: * @param length The length of the sequence to be copied.
242: */
243:
244: public void write_at(int pos, ByteWrap other, int index, int length) {
245: pos += offset;
246: index += other.offset;
247: for (int i = 0; i < length; ++i) {
248: bytes[pos + i] = other.bytes[index + i];
249: }
250: }
251:
252: /** Returns the number of elements in the sequence wrapped by this ByteWrap.
253: * @return the number of elements in the sequence wrapped by this ByteWrap.
254: */
255:
256: public int size() {
257: return size;
258: }
259:
260: /** Shuffles part of the contents of this ByteWrap forwards, to make space for more
261: * elements at a particular position. It is assumed there is enough space at the
262: * end of the wrapped array to accommodate the resulting expansion of the sequence.
263: * @param fromposition The index at which more space is to be made.
264: * @param amount The number of positions forward the data is to be shuffled.
265: */
266:
267: public void shuffle(int fromposition, int amount) {
268: /*
269: System.out.println("shuffle: arraysize = " + bytes.length + " offset = "+offset
270: +" size = "+ size + " fromposition = "+ fromposition
271: +" amount = "+ amount);
272: */
273: fromposition += offset;
274: System.arraycopy(bytes, fromposition, bytes, fromposition
275: + amount, offset + size - fromposition);
276: size += amount;
277: }
278:
279: private static final int SPAN = 16;
280: private static DecimalFormat column;
281:
282: /** Converts the supplied integer in the range <code>[0..15]</code> into its equivalent
283: * hexadecimal digit.
284: * @param nibble The integer to be converted.
285: * @return The integer represented as a hexadecimal digit.
286: */
287:
288: public static char toHex(int nibble) {
289: if (nibble < 10)
290: return (char) ('0' + nibble);
291: else
292: return (char) ('A' + nibble - 10);
293: }
294:
295: /** Compares this ByteWrap with another object. It will compare equal to another
296: * ByteWrap wrapping the same sequence of bytes. This method is very inefficient.
297: * @param othero The ByteWrap to be compared to.
298: * @return <code>true</code> if the other object if the supplied object is a
299: * ByteWrap wrapping the same sequence of bytes.
300: */
301:
302: public boolean equals(Object othero) {
303: if (othero instanceof ByteWrap) {
304: ByteWrap other = (ByteWrap) othero;
305: if (other.size() == size()) {
306: for (int i = 0; i < size; ++i) {
307: if (read_at1(i) != other.read_at1(i))
308: return false;
309: }
310: return true;
311: }
312: }
313: return false;
314: }
315:
316: /** Represents this ByteWrap as a String for debugging purposes. If it contains
317: * 4 characters or less, it will be compactly represented as a hexadecimal number.
318: * If it is longer, it will be represented in a style similar to UNIX "od" or "dd"
319: * @return The contents of this ByteWrap as a debug string.
320: */
321:
322: public String toString() {
323: if (size <= 4) {
324: if (size == 4)
325: return intToHex(read_at4(0), 4);
326: else if (size == 3)
327: return intToHex(read_at3(0), 3);
328: else if (size == 2)
329: return intToHex(read_at2(0), 2);
330: else
331: return intToHex(read_at1(0), 1);
332: }
333: if (column == null) {
334: column = new DecimalFormat("000:");
335: }
336: StringBuffer build = new StringBuffer();
337: for (int i = 0; i < size; i += SPAN) {
338: int limit = (size - i > SPAN) ? SPAN : size - i;
339: build.append(column.format(i));
340: for (int j = 0; j < SPAN; ++j) {
341: if (j < limit) {
342: build
343: .append(toHex((bytes[i + j + offset] & 0xf0) >> 4));
344: build.append(toHex(bytes[i + j + offset] & 0xf))
345: .append(' ');
346: } else
347: build.append(" ");
348: }
349: build.append(" ");
350: for (int j = 0; j < limit; ++j) {
351: int c = bytes[i + j + offset];
352: build.append(c >= 32 && c < 127 ? (char) c : '.');
353: }
354: build.append("\n");
355: }
356: return build.toString();
357: }
358:
359: private static ByteWrap temphex = new ByteWrap(4);
360:
361: /** Converts an integer to a string containing a hexadecimal representation.
362: * @param tohex THe integer to be represented.
363: * @return A string representing the integer's value in hexadecimal.
364: */
365: public static final String intToHex(int tohex) {
366: return intToHex(tohex, 4);
367: }
368:
369: /** Converts some number of low-order bytes of an integer into a hexadecimal
370: * representation in a String.
371: * @param tohex The integer to be converted to hex.
372: * @param bytes The number of low-order bytes (between 1 and 4) to be converted
373: * into hex.
374: * @return The required hexadecimal representation.
375: */
376: public static final String intToHex(int tohex, int bytes) {
377: StringBuffer build = new StringBuffer();
378: // System.out.println("intToHex "+tohex+" "+bytes);
379: if (bytes >= 2) { // reverse switch statement!
380: if (bytes >= 3) {
381: if (bytes == 4) {
382: build.append(toHex((tohex >> 28) & 0xf));
383: build.append(toHex((tohex >> 24) & 0xf));
384: build.append(' ');
385: }
386: build.append(toHex((tohex >> 20) & 0xf));
387: build.append(toHex((tohex >> 16) & 0xf));
388: build.append(' ');
389: }
390: build.append(toHex((tohex >> 12) & 0xf));
391: build.append(toHex((tohex >> 8) & 0xf));
392: build.append(' ');
393: }
394: build.append(toHex((tohex >> 4) & 0xf));
395: build.append(toHex((tohex) & 0xf));
396: // build.append(' ');
397: return build.toString();
398: }
399:
400: }
|