1/*
2 * Copyright (c) 2009-2010 jMonkeyEngine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 *   notice, this list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright
13 *   notice, this list of conditions and the following disclaimer in the
14 *   documentation and/or other materials provided with the distribution.
15 *
16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17 *   may be used to endorse or promote products derived from this software
18 *   without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33package com.jme3.export.binary;
34
35import java.io.ByteArrayOutputStream;
36import java.io.IOException;
37import java.io.InputStream;
38import java.io.OutputStream;
39
40/**
41 * <code>ByteUtils</code> is a helper class for converting numeric primitives
42 * to and from byte representations.
43 *
44 * @author Joshua Slack
45 */
46public class ByteUtils {
47
48    /**
49     * Takes an InputStream and returns the complete byte content of it
50     *
51     * @param inputStream
52     *            The input stream to read from
53     * @return The byte array containing the data from the input stream
54     * @throws java.io.IOException
55     *             thrown if there is a problem reading from the input stream
56     *             provided
57     */
58    public static byte[] getByteContent(InputStream inputStream)
59            throws IOException {
60        ByteArrayOutputStream outputStream = new ByteArrayOutputStream(
61                16 * 1024);
62        byte[] buffer = new byte[1024];
63        int byteCount = -1;
64        byte[] data = null;
65
66        // Read the byte content into the output stream first
67        while ((byteCount = inputStream.read(buffer)) > 0) {
68            outputStream.write(buffer, 0, byteCount);
69        }
70
71        // Set data with byte content from stream
72        data = outputStream.toByteArray();
73
74        // Release resources
75        outputStream.close();
76
77        return data;
78    }
79
80
81    // **********  byte <> short  METHODS  **********
82
83    /**
84     * Writes a short out to an OutputStream.
85     *
86     * @param outputStream
87     *            The OutputStream the short will be written to
88     * @param value
89     *            The short to write
90     * @throws IOException
91     *             Thrown if there is a problem writing to the OutputStream
92     */
93    public static void writeShort(OutputStream outputStream, short value)
94            throws IOException {
95        byte[] byteArray = convertToBytes(value);
96
97        outputStream.write(byteArray);
98
99        return;
100    }
101
102    public static byte[] convertToBytes(short value) {
103        byte[] byteArray = new byte[2];
104
105        byteArray[0] = (byte) (value >> 8);
106        byteArray[1] = (byte) value;
107        return byteArray;
108    }
109
110    /**
111     * Read in a short from an InputStream
112     *
113     * @param inputStream
114     *            The InputStream used to read the short
115     * @return A short, which is the next 2 bytes converted from the InputStream
116     * @throws IOException
117     *             Thrown if there is a problem reading from the InputStream
118     */
119    public static short readShort(InputStream inputStream) throws IOException {
120        byte[] byteArray = new byte[2];
121
122        // Read in the next 2 bytes
123        inputStream.read(byteArray);
124
125        short number = convertShortFromBytes(byteArray);
126
127        return number;
128    }
129
130    public static short convertShortFromBytes(byte[] byteArray) {
131        return convertShortFromBytes(byteArray, 0);
132    }
133
134    public static short convertShortFromBytes(byte[] byteArray, int offset) {
135        // Convert it to a short
136        short number = (short) ((byteArray[offset+1] & 0xFF) + ((byteArray[offset+0] & 0xFF) << 8));
137        return number;
138    }
139
140
141    // **********  byte <> int  METHODS  **********
142
143    /**
144     * Writes an integer out to an OutputStream.
145     *
146     * @param outputStream
147     *            The OutputStream the integer will be written to
148     * @param integer
149     *            The integer to write
150     * @throws IOException
151     *             Thrown if there is a problem writing to the OutputStream
152     */
153    public static void writeInt(OutputStream outputStream, int integer)
154            throws IOException {
155        byte[] byteArray = convertToBytes(integer);
156
157        outputStream.write(byteArray);
158
159        return;
160    }
161
162    public static byte[] convertToBytes(int integer) {
163        byte[] byteArray = new byte[4];
164
165        byteArray[0] = (byte) (integer >> 24);
166        byteArray[1] = (byte) (integer >> 16);
167        byteArray[2] = (byte) (integer >> 8);
168        byteArray[3] = (byte) integer;
169        return byteArray;
170    }
171
172    /**
173     * Read in an integer from an InputStream
174     *
175     * @param inputStream
176     *            The InputStream used to read the integer
177     * @return An int, which is the next 4 bytes converted from the InputStream
178     * @throws IOException
179     *             Thrown if there is a problem reading from the InputStream
180     */
181    public static int readInt(InputStream inputStream) throws IOException {
182        byte[] byteArray = new byte[4];
183
184        // Read in the next 4 bytes
185        inputStream.read(byteArray);
186
187        int number = convertIntFromBytes(byteArray);
188
189        return number;
190    }
191
192    public static int convertIntFromBytes(byte[] byteArray) {
193        return convertIntFromBytes(byteArray, 0);
194    }
195
196    public static int convertIntFromBytes(byte[] byteArray, int offset) {
197        // Convert it to an int
198        int number = ((byteArray[offset] & 0xFF) << 24)
199                + ((byteArray[offset+1] & 0xFF) << 16) + ((byteArray[offset+2] & 0xFF) << 8)
200                + (byteArray[offset+3] & 0xFF);
201        return number;
202    }
203
204
205    // **********  byte <> long  METHODS  **********
206
207    /**
208     * Writes a long out to an OutputStream.
209     *
210     * @param outputStream
211     *            The OutputStream the long will be written to
212     * @param value
213     *            The long to write
214     * @throws IOException
215     *             Thrown if there is a problem writing to the OutputStream
216     */
217    public static void writeLong(OutputStream outputStream, long value)
218            throws IOException {
219        byte[] byteArray = convertToBytes(value);
220
221        outputStream.write(byteArray);
222
223        return;
224    }
225
226    public static byte[] convertToBytes(long n) {
227        byte[] bytes = new byte[8];
228
229        bytes[7] = (byte) (n);
230        n >>>= 8;
231        bytes[6] = (byte) (n);
232        n >>>= 8;
233        bytes[5] = (byte) (n);
234        n >>>= 8;
235        bytes[4] = (byte) (n);
236        n >>>= 8;
237        bytes[3] = (byte) (n);
238        n >>>= 8;
239        bytes[2] = (byte) (n);
240        n >>>= 8;
241        bytes[1] = (byte) (n);
242        n >>>= 8;
243        bytes[0] = (byte) (n);
244
245        return bytes;
246    }
247
248    /**
249     * Read in a long from an InputStream
250     *
251     * @param inputStream
252     *            The InputStream used to read the long
253     * @return A long, which is the next 8 bytes converted from the InputStream
254     * @throws IOException
255     *             Thrown if there is a problem reading from the InputStream
256     */
257    public static long readLong(InputStream inputStream) throws IOException {
258        byte[] byteArray = new byte[8];
259
260        // Read in the next 8 bytes
261        inputStream.read(byteArray);
262
263        long number = convertLongFromBytes(byteArray);
264
265        return number;
266    }
267
268    public static long convertLongFromBytes(byte[] bytes) {
269        return convertLongFromBytes(bytes, 0);
270    }
271
272    public static long convertLongFromBytes(byte[] bytes, int offset) {
273        // Convert it to an long
274        return    ((((long) bytes[offset+7]) & 0xFF)
275                + ((((long) bytes[offset+6]) & 0xFF) << 8)
276                + ((((long) bytes[offset+5]) & 0xFF) << 16)
277                + ((((long) bytes[offset+4]) & 0xFF) << 24)
278                + ((((long) bytes[offset+3]) & 0xFF) << 32)
279                + ((((long) bytes[offset+2]) & 0xFF) << 40)
280                + ((((long) bytes[offset+1]) & 0xFF) << 48)
281                + ((((long) bytes[offset+0]) & 0xFF) << 56));
282    }
283
284
285    // **********  byte <> double  METHODS  **********
286
287    /**
288     * Writes a double out to an OutputStream.
289     *
290     * @param outputStream
291     *            The OutputStream the double will be written to
292     * @param value
293     *            The double to write
294     * @throws IOException
295     *             Thrown if there is a problem writing to the OutputStream
296     */
297    public static void writeDouble(OutputStream outputStream, double value)
298            throws IOException {
299        byte[] byteArray = convertToBytes(value);
300
301        outputStream.write(byteArray);
302
303        return;
304    }
305
306    public static byte[] convertToBytes(double n) {
307        long bits = Double.doubleToLongBits(n);
308        return convertToBytes(bits);
309    }
310
311    /**
312     * Read in a double from an InputStream
313     *
314     * @param inputStream
315     *            The InputStream used to read the double
316     * @return A double, which is the next 8 bytes converted from the InputStream
317     * @throws IOException
318     *             Thrown if there is a problem reading from the InputStream
319     */
320    public static double readDouble(InputStream inputStream) throws IOException {
321        byte[] byteArray = new byte[8];
322
323        // Read in the next 8 bytes
324        inputStream.read(byteArray);
325
326        double number = convertDoubleFromBytes(byteArray);
327
328        return number;
329    }
330
331    public static double convertDoubleFromBytes(byte[] bytes) {
332        return convertDoubleFromBytes(bytes, 0);
333    }
334
335    public static double convertDoubleFromBytes(byte[] bytes, int offset) {
336        // Convert it to a double
337        long bits = convertLongFromBytes(bytes, offset);
338        return Double.longBitsToDouble(bits);
339    }
340
341    //  **********  byte <> float  METHODS  **********
342
343    /**
344     * Writes an float out to an OutputStream.
345     *
346     * @param outputStream
347     *            The OutputStream the float will be written to
348     * @param fVal
349     *            The float to write
350     * @throws IOException
351     *             Thrown if there is a problem writing to the OutputStream
352     */
353    public static void writeFloat(OutputStream outputStream, float fVal)
354            throws IOException {
355        byte[] byteArray = convertToBytes(fVal);
356
357        outputStream.write(byteArray);
358
359        return;
360    }
361
362    public static byte[] convertToBytes(float f) {
363        int temp = Float.floatToIntBits(f);
364        return convertToBytes(temp);
365    }
366
367    /**
368     * Read in a float from an InputStream
369     *
370     * @param inputStream
371     *            The InputStream used to read the float
372     * @return A float, which is the next 4 bytes converted from the InputStream
373     * @throws IOException
374     *             Thrown if there is a problem reading from the InputStream
375     */
376    public static float readFloat(InputStream inputStream) throws IOException {
377        byte[] byteArray = new byte[4];
378
379        // Read in the next 4 bytes
380        inputStream.read(byteArray);
381
382        float number = convertFloatFromBytes(byteArray);
383
384        return number;
385    }
386
387    public static float convertFloatFromBytes(byte[] byteArray) {
388        return convertFloatFromBytes(byteArray, 0);
389    }
390    public static float convertFloatFromBytes(byte[] byteArray, int offset) {
391        // Convert it to an int
392        int number = convertIntFromBytes(byteArray, offset);
393        return Float.intBitsToFloat(number);
394    }
395
396
397
398    //  **********  byte <> boolean  METHODS  **********
399
400    /**
401     * Writes a boolean out to an OutputStream.
402     *
403     * @param outputStream
404     *            The OutputStream the boolean will be written to
405     * @param bVal
406     *            The boolean to write
407     * @throws IOException
408     *             Thrown if there is a problem writing to the OutputStream
409     */
410    public static void writeBoolean(OutputStream outputStream, boolean bVal)
411            throws IOException {
412        byte[] byteArray = convertToBytes(bVal);
413
414        outputStream.write(byteArray);
415
416        return;
417    }
418
419    public static byte[] convertToBytes(boolean b) {
420        byte[] rVal = new byte[1];
421        rVal[0] = b ? (byte)1 : (byte)0;
422        return rVal;
423    }
424
425    /**
426     * Read in a boolean from an InputStream
427     *
428     * @param inputStream
429     *            The InputStream used to read the boolean
430     * @return A boolean, which is the next byte converted from the InputStream (iow, byte != 0)
431     * @throws IOException
432     *             Thrown if there is a problem reading from the InputStream
433     */
434    public static boolean readBoolean(InputStream inputStream) throws IOException {
435        byte[] byteArray = new byte[1];
436
437        // Read in the next byte
438        inputStream.read(byteArray);
439
440        return convertBooleanFromBytes(byteArray);
441    }
442
443    public static boolean convertBooleanFromBytes(byte[] byteArray) {
444        return convertBooleanFromBytes(byteArray, 0);
445    }
446    public static boolean convertBooleanFromBytes(byte[] byteArray, int offset) {
447        return byteArray[offset] != 0;
448    }
449
450
451    /**
452     * Properly reads in data from the given stream until the specified number
453     * of bytes have been read.
454     *
455     * @param store
456     *            the byte array to store in. Should have a length > bytes
457     * @param bytes
458     *            the number of bytes to read.
459     * @param is
460     *            the stream to read from
461     * @return the store array for chaining purposes
462     * @throws IOException
463     *             if an error occurs while reading from the stream
464     * @throws ArrayIndexOutOfBoundsException
465     *             if bytes greater than the length of the store.
466     */
467    public static byte[] readData(byte[] store, int bytes, InputStream is) throws IOException {
468        for (int i = 0; i < bytes; i++) {
469            store[i] = (byte)is.read();
470        }
471        return store;
472    }
473
474    public static byte[] rightAlignBytes(byte[] bytes, int width) {
475        if (bytes.length != width) {
476            byte[] rVal = new byte[width];
477            for (int x = width - bytes.length; x < width; x++) {
478                rVal[x] = bytes[x - (width - bytes.length)];
479            }
480            return rVal;
481        }
482
483        return bytes;
484    }
485
486}
487