001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.msgpack;
014
015import static org.apache.juneau.msgpack.DataType.*;
016
017import java.io.*;
018import java.math.*;
019import java.util.concurrent.atomic.*;
020
021/**
022 * Specialized output stream for serializing MessagePack streams.
023 *
024 * <ul class='notes'>
025 *    <li>
026 *       This class is not intended for external use.
027 * </ul>
028 */
029public final class MsgPackOutputStream extends OutputStream {
030
031   private final OutputStream os;
032
033   /**
034    * Constructor.
035    *
036    * @param os The output stream being wrapped.
037    */
038   protected MsgPackOutputStream(OutputStream os) {
039      this.os = os;
040   }
041
042   @Override /* OutputStream */
043   public void write(int b) throws IOException {
044      os.write(b);
045   }
046
047   /**
048    * Same as {@link #write(int)}.
049    */
050   final MsgPackOutputStream append(byte b) throws IOException {
051      os.write(b);
052      return this;
053   }
054
055   /**
056    * Same as {@link #write(byte[])}.
057    */
058   final MsgPackOutputStream append(byte[] b) throws IOException {
059      os.write(b);
060      return this;
061   }
062
063   /**
064    * Appends one byte to the stream.
065    */
066   final MsgPackOutputStream append1(int i) throws IOException {
067      os.write(i);
068      return this;
069   }
070
071   /**
072    * Appends two bytes to the stream.
073    */
074   final MsgPackOutputStream append2(int i) throws IOException {
075      return append1(i>>8).append1(i);
076   }
077
078   /**
079    * Appends four bytes to the stream.
080    */
081   final MsgPackOutputStream append4(int i) throws IOException {
082      return append1(i>>24).append1(i>>16).append1(i>>8).append1(i);
083   }
084
085   /**
086    * Appends eight bytes to the stream.
087    */
088   final MsgPackOutputStream append8(long l) throws IOException {
089      return append1((int)(l>>56)).append1((int)(l>>48)).append1((int)(l>>40)).append1((int)(l>>32)).append1((int)(l>>24)).append1((int)(l>>16)).append1((int)(l>>8)).append1((int)(l));
090   }
091
092   /**
093    * Appends a NULL flag to the stream.
094    */
095   final MsgPackOutputStream appendNull() throws IOException {
096      return append1(NIL);
097   }
098
099   /**
100    * Appends a boolean to the stream.
101    */
102   final MsgPackOutputStream appendBoolean(boolean b) throws IOException {
103      return append1(b ? TRUE : FALSE);
104   }
105
106   /**
107    * Appends an integer to the stream.
108    */
109   final MsgPackOutputStream appendInt(int i) throws IOException {
110      // POSFIXINT_L  = 0x00,  //   pos fixint     0xxxxxxx     0x00 - 0x7f
111      // POSFIXINT_U  = 0x7F,
112      // UINT8        = 0xCC,  //   uint 8         11001100     0xcc
113      // UINT16       = 0xCD,  //   uint 16        11001101     0xcd
114      // UINT32       = 0xCE,  //   uint 32        11001110     0xce
115      // UINT64       = 0xCF,  //   uint 64        11001111     0xcf
116      // INT8         = 0xD0,  //   int 8          11010000     0xd0
117      // INT16        = 0xD1,  //   int 16         11010001     0xd1
118      // INT32        = 0xD2,  //   int 32         11010010     0xd2
119      // INT64        = 0xD3,  //   int 64         11010011     0xd3
120      // NEGFIXINT_L  = 0xE0,  //   neg fixint     111xxxxx     0xe0 - 0xff
121      // NEGFIXINT_U  = 0xFF;
122      if (i >= 0) {
123         if (i < (1<<7))
124            return append1(i);
125         if (i < (1<<15))
126            return append1(INT16).append2(i);
127         return append1(INT32).append4(i);
128      }
129      if (i > -(1<<6))
130         return append((byte)(0xE0 | -i));
131      if (i > -(1<<7))
132         return append1(INT8).append1(i);
133      if (i > -(1<<15))
134         return append1(INT16).append2(i);
135      return append1(INT32).append4(i);
136   }
137
138   final long L2X31 = ((long)(1<<30))*2;
139
140   /**
141    * Appends a long to the stream.
142    */
143   final MsgPackOutputStream appendLong(long l) throws IOException {
144      if (l < L2X31 && l > -(L2X31))
145         return appendInt((int)l);
146      return append1(INT64).append8(l);
147   }
148
149   /**
150    * Appends a generic Number to the stream.
151    */
152   final MsgPackOutputStream appendNumber(Number n) throws IOException {
153      Class<?> c = n.getClass();
154      if (c == Integer.class || c == Short.class || c == Byte.class || c == AtomicInteger.class)
155         return appendInt(n.intValue());
156      if (c == Long.class || c == AtomicLong.class)
157         return appendLong(n.longValue());
158      if (c == Float.class)
159         return appendFloat(n.floatValue());
160      if (c == Double.class)
161         return appendDouble(n.doubleValue());
162      if (c == BigInteger.class)
163         return appendLong(n.longValue());
164      if (c == BigDecimal.class)
165         return appendDouble(n.doubleValue());
166      return appendInt(0);
167   }
168
169   /**
170    * Appends a float to the stream.
171    */
172   final MsgPackOutputStream appendFloat(float f) throws IOException {
173      // FLOAT32      = 0xCA,  //   float 32       11001010     0xca
174      return append1(FLOAT32).append4(Float.floatToIntBits(f));
175
176   }
177
178   /**
179    * Appends a double to the stream.
180    */
181   final MsgPackOutputStream appendDouble(double d) throws IOException {
182      // FLOAT64      = 0xCB,  //   float 64       11001011     0xcb
183      return append1(FLOAT64).append8(Double.doubleToLongBits(d));
184   }
185
186   /**
187    * Appends a string to the stream.
188    */
189   final MsgPackOutputStream appendString(CharSequence cs) throws IOException {
190
191      // fixstr stores a byte array whose length is up to 31 bytes:
192      // +--------+========+
193      // |101XXXXX|  data  |
194      // +--------+========+
195      //
196      // str 8 stores a byte array whose length is up to (2^8)-1 bytes:
197      // +--------+--------+========+
198      // |  0xd9  |YYYYYYYY|  data  |
199      // +--------+--------+========+
200      //
201      // str 16 stores a byte array whose length is up to (2^16)-1 bytes:
202      // +--------+--------+--------+========+
203      // |  0xda  |ZZZZZZZZ|ZZZZZZZZ|  data  |
204      // +--------+--------+--------+========+
205      //
206      // str 32 stores a byte array whose length is up to (2^32)-1 bytes:
207      // +--------+--------+--------+--------+--------+========+
208      // |  0xdb  |AAAAAAAA|AAAAAAAA|AAAAAAAA|AAAAAAAA|  data  |
209      // +--------+--------+--------+--------+--------+========+
210      // where
211      // * XXXXX is a 5-bit unsigned integer which represents N
212      // * YYYYYYYY is a 8-bit unsigned integer which represents N
213      // * ZZZZZZZZ_ZZZZZZZZ is a 16-bit big-endian unsigned integer which represents N
214      // * AAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA is a 32-bit big-endian unsigned integer which represents N
215      // * N is the length of data
216
217      byte[] b = cs.toString().getBytes("UTF-8");
218      if (b.length < 32)
219         return append1(0xA0 + b.length).append(b);
220      if (b.length < (1<<8))
221         return append1(STR8).append1(b.length).append(b);
222      if (b.length < (1<<16))
223         return append1(STR16).append2(b.length).append(b);
224      return append1(STR32).append4(b.length).append(b);
225   }
226
227   /**
228    * Appends a binary field to the stream.
229    */
230   final MsgPackOutputStream appendBinary(byte[] b) throws IOException {
231      // bin 8 stores a byte array whose length is up to (2^8)-1 bytes:
232      // +--------+--------+========+
233      // |  0xc4  |XXXXXXXX|  data  |
234      // +--------+--------+========+
235      //
236      // bin 16 stores a byte array whose length is up to (2^16)-1 bytes:
237      // +--------+--------+--------+========+
238      // |  0xc5  |YYYYYYYY|YYYYYYYY|  data  |
239      // +--------+--------+--------+========+
240      //
241      // bin 32 stores a byte array whose length is up to (2^32)-1 bytes:
242      // +--------+--------+--------+--------+--------+========+
243      // |  0xc6  |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|  data  |
244      // +--------+--------+--------+--------+--------+========+
245      //
246      // where
247      // * XXXXXXXX is a 8-bit unsigned integer which represents N
248      // * YYYYYYYY_YYYYYYYY is a 16-bit big-endian unsigned integer which represents N
249      // * ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ is a 32-bit big-endian unsigned integer which represents N
250      // * N is the length of data
251
252      if (b.length < (1<<8))
253         return append1(BIN8).append1(b.length).append(b);
254      if (b.length < (1<<16))
255         return append1(BIN16).append2(b.length).append(b);
256      return append1(BIN32).append4(b.length).append(b);
257   }
258
259   /**
260    * Appends an array data type flag to the stream.
261    */
262   final MsgPackOutputStream startArray(int size) throws IOException {
263      // fixarray stores an array whose length is up to 15 elements:
264      // +--------+~~~~~~~~~~~~~~~~~+
265      // |1001XXXX|    N objects    |
266      // +--------+~~~~~~~~~~~~~~~~~+
267      //
268      // array 16 stores an array whose length is up to (2^16)-1 elements:
269      // +--------+--------+--------+~~~~~~~~~~~~~~~~~+
270      // |  0xdc  |YYYYYYYY|YYYYYYYY|    N objects    |
271      // +--------+--------+--------+~~~~~~~~~~~~~~~~~+
272      //
273      // array 32 stores an array whose length is up to (2^32)-1 elements:
274      // +--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+
275      // |  0xdd  |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|    N objects    |
276      // +--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+
277      //
278      // where
279      // * XXXX is a 4-bit unsigned integer which represents N
280      // * YYYYYYYY_YYYYYYYY is a 16-bit big-endian unsigned integer which represents N
281      // * ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ is a 32-bit big-endian unsigned integer which represents N
282      //     N is the size of a array
283
284      if (size < 16)
285         return append1(0x90 + size);
286      if (size < (1<<16))
287         return append1(ARRAY16).append2(size);
288      return append1(ARRAY32).append4(size);
289   }
290
291   /**
292    * Appends a map data type flag to the stream.
293    */
294   final MsgPackOutputStream startMap(int size) throws IOException {
295      // fixmap stores a map whose length is up to 15 elements
296      // +--------+~~~~~~~~~~~~~~~~~+
297      // |1000XXXX|   N*2 objects   |
298      // +--------+~~~~~~~~~~~~~~~~~+
299      //
300      // map 16 stores a map whose length is up to (2^16)-1 elements
301      // +--------+--------+--------+~~~~~~~~~~~~~~~~~+
302      // |  0xde  |YYYYYYYY|YYYYYYYY|   N*2 objects   |
303      // +--------+--------+--------+~~~~~~~~~~~~~~~~~+
304      //
305      // map 32 stores a map whose length is up to (2^32)-1 elements
306      // +--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+
307      // |  0xdf  |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|   N*2 objects   |
308      // +--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+
309      //
310      // where
311      // * XXXX is a 4-bit unsigned integer which represents N
312      // * YYYYYYYY_YYYYYYYY is a 16-bit big-endian unsigned integer which represents N
313      // * ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ is a 32-bit big-endian unsigned integer which represents N
314      // * N is the size of a map
315      // * odd elements in objects are keys of a map
316      // * the next element of a key is its associated value
317
318      if (size < 16)
319         return append1(0x80 + size);
320      if (size < (1<<16))
321         return append1(MAP16).append2(size);
322      return append1(MAP32).append4(size);
323   }
324}