View Javadoc

1   // Copyright (C) 2000, 2001, 2002, 2003 Philip Aston
2   // All rights reserved.
3   //
4   // This file is part of The Grinder software distribution. Refer to
5   // the file LICENSE which is part of The Grinder distribution for
6   // licensing details. The Grinder distribution is available on the
7   // Internet at http://grinder.sourceforge.net/
8   //
9   // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
10  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
11  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
12  // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
13  // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
14  // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
15  // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
16  // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
17  // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
18  // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
19  // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
20  // OF THE POSSIBILITY OF SUCH DAMAGE.
21  
22  package net.grinder.util;
23  
24  import java.io.DataInput;
25  import java.io.DataOutput;
26  import java.io.IOException;
27  
28  
29  /**
30   * Utility methods for efficient serialisation.
31   *
32   * @author Philip Aston
33   */
34  public class Serialiser {
35  
36    private final byte[] m_byteBuffer = new byte[8];
37  
38    /**
39     * Write a <code>long</code> to a stream in such a way it can be
40     * read by {@link #readUnsignedLong}. The value of the
41     * <code>long</code> must be greater than zero. Values between 0 and
42     * 127 inclusive require only one byte. Other values require eight
43     * bytes.
44     *
45     * @param output The stream.
46     * @param l a <code>long</code> value
47     * @exception IOException If the stream raises an error.
48     */
49    public final void writeUnsignedLong(DataOutput output, long l)
50      throws IOException {
51  
52      if (l < 0) {
53        throw new IOException("Negative argument");
54      }
55  
56      if (l < 0x80) {
57        output.writeByte((byte)l);
58      }
59      else {
60        output.writeLong(-l);
61      }
62    }
63  
64    /**
65     * Read a <code>long</code> written by {@link #writeUnsignedLong}.
66     *
67     * @param input The stream.
68     * @return The value.
69     * @exception IOException If the stream raises an error.
70     */
71    public final long readUnsignedLong(DataInput input) throws IOException {
72  
73      m_byteBuffer[0] = input.readByte();
74  
75      if (m_byteBuffer[0] >= 0) {
76        return m_byteBuffer[0];
77      }
78      else {
79        input.readFully(m_byteBuffer, 1, 7);
80  
81        return -(((long)(m_byteBuffer[0] & 0xFF) << 56) |
82                 ((long)(m_byteBuffer[1] & 0xFF) << 48) |
83                 ((long)(m_byteBuffer[2] & 0xFF) << 40) |
84                 ((long)(m_byteBuffer[3] & 0xFF) << 32) |
85                 ((long)(m_byteBuffer[4] & 0xFF) << 24) |
86                 ((long)(m_byteBuffer[5] & 0xFF) << 16) |
87                 ((long)(m_byteBuffer[6] & 0xFF) << 8) |
88                 ((long)(m_byteBuffer[7] & 0xFF) << 0));
89      }
90    }
91  
92    /**
93     * Write a <code>long</code> to a stream in such a way it can be
94     * read by {@link #readLong}.
95     *
96     * <p>Efficient for small, positive longs. Values less than 16
97     * take one byte. The worst cases (including all negative values)
98     * take nine bytes.</p>
99     *
100    * @param output The stream.
101    * @param l Value to write.
102    * @exception IOException If the stream raises an error.
103    */
104   public final void writeLong(DataOutput output, long l)
105     throws IOException {
106 
107     boolean startedToWrite = false;
108 
109     for (byte i = (byte)(m_byteBuffer.length - 1); i >= 0; --i) {
110       final byte b = (byte)((l >>> i * 8) & 0xFF);
111 
112       if (startedToWrite) {
113         output.writeByte(b);
114       }
115       else {
116         if ((b & 0xFF) != 0) {
117           if ((b & 0xF0) != 0) {
118             // Write length.
119             output.writeByte((i + 1) << 4);
120             output.writeByte(b);
121           }
122           else {
123             // Special case, byte will fit in one nibble.
124             // Combine with length.
125             output.writeByte(b | (i << 4));
126           }
127 
128           startedToWrite = true;
129         }
130       }
131     }
132 
133     if (!startedToWrite) {
134       output.writeByte(0);
135     }
136   }
137 
138 
139   /**
140    * Read a <code>long</code> written by {@link #writeLong}.
141    *
142    * @param input The stream.
143    * @return The value.
144    * @exception IOException If the stream raises an error.
145    */
146   public final long readLong(DataInput input) throws IOException {
147 
148     final byte b = input.readByte();
149     long length = (b & 0xF0) >>> 4;
150     long result = b & 0x0F;
151 
152     while (length-- > 0) {
153       result = (result << 8) | input.readUnsignedByte();
154     }
155 
156     return result;
157   }
158 
159   /**
160    * Write a <code>double</code> to a stream in such a way it can be
161    * read by {@link #readDouble}.
162    *
163    * @param output The stream.
164    * @param l The value.
165    * @throws IOException If the stream raises an error.
166    **/
167   public final void writeDouble(DataOutput output, double l)
168     throws IOException {
169 
170     // One day we'll make this efficient again.
171     output.writeDouble(l);
172   }
173 
174   /**
175    * Read a <code>double</code> written by {@link #writeDouble}.
176    *
177    * @param input The stream.
178    * @return The value.
179    * @throws IOException If the stream raises an error.
180    */
181   public final double readDouble(DataInput input) throws IOException {
182 
183     return input.readDouble();
184   }
185 }