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
021import org.apache.juneau.common.internal.*;
022import org.apache.juneau.serializer.*;
023
024/**
025 * Specialized output stream for serializing MessagePack streams.
026 *
027 * <h5 class='section'>Notes:</h5><ul>
028 *    <li class='note'>
029 *       This class is not intended for external use.
030 * </ul>
031 *
032 * <h5 class='section'>See Also:</h5><ul>
033 *    <li class='link'><a class="doclink" href="../../../../index.html#jm.MsgPackDetails">MessagePack Details</a>
034
035 * </ul>
036 */
037public final class MsgPackOutputStream extends OutputStream {
038
039   private final OutputStream os;
040
041   /**
042    * Constructor.
043    *
044    * @param os The output stream being wrapped.
045    */
046   protected MsgPackOutputStream(OutputStream os) {
047      this.os = os;
048   }
049
050   @Override /* OutputStream */
051   public void write(int b) {
052      try {
053         os.write(b);
054      } catch (IOException e) {
055         throw new SerializeException(e);
056      }
057   }
058
059   /**
060    * Same as {@link #write(int)}.
061    */
062   final MsgPackOutputStream append(byte b) {
063      try {
064         os.write(b);
065      } catch (IOException e) {
066         throw new SerializeException(e);
067      }
068      return this;
069   }
070
071   /**
072    * Same as {@link #write(byte[])}.
073    */
074   final MsgPackOutputStream append(byte[] b) {
075      try {
076         os.write(b);
077      } catch (IOException e) {
078         throw new SerializeException(e);
079      }
080      return this;
081   }
082
083   /**
084    * Appends one byte to the stream.
085    */
086   final MsgPackOutputStream append1(int i) {
087      try {
088         os.write(i);
089      } catch (IOException e) {
090         throw new SerializeException(e);
091      }
092      return this;
093   }
094
095   /**
096    * Appends two bytes to the stream.
097    */
098   final MsgPackOutputStream append2(int i) {
099      return append1(i>>8).append1(i);
100   }
101
102   /**
103    * Appends four bytes to the stream.
104    */
105   final MsgPackOutputStream append4(int i) {
106      return append1(i>>24).append1(i>>16).append1(i>>8).append1(i);
107   }
108
109   /**
110    * Appends eight bytes to the stream.
111    */
112   final MsgPackOutputStream append8(long l) {
113      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));
114   }
115
116   /**
117    * Appends a NULL flag to the stream.
118    */
119   final MsgPackOutputStream appendNull() {
120      return append1(NIL);
121   }
122
123   /**
124    * Appends a boolean to the stream.
125    */
126   final MsgPackOutputStream appendBoolean(boolean b) {
127      return append1(b ? TRUE : FALSE);
128   }
129
130   /**
131    * Appends an integer to the stream.
132    */
133   final MsgPackOutputStream appendInt(int i) {
134      // POSFIXINT_L  = 0x00,  //   pos fixint     0xxxxxxx     0x00 - 0x7f
135      // POSFIXINT_U  = 0x7F,
136      // UINT8        = 0xCC,  //   uint 8         11001100     0xcc
137      // UINT16       = 0xCD,  //   uint 16        11001101     0xcd
138      // UINT32       = 0xCE,  //   uint 32        11001110     0xce
139      // UINT64       = 0xCF,  //   uint 64        11001111     0xcf
140      // INT8         = 0xD0,  //   int 8          11010000     0xd0
141      // INT16        = 0xD1,  //   int 16         11010001     0xd1
142      // INT32        = 0xD2,  //   int 32         11010010     0xd2
143      // INT64        = 0xD3,  //   int 64         11010011     0xd3
144      // NEGFIXINT_L  = 0xE0,  //   neg fixint     111xxxxx     0xe0 - 0xff
145      // NEGFIXINT_U  = 0xFF;
146      if (i >= 0) {
147         if (i < (1<<7))
148            return append1(i);
149         if (i < (1<<15))
150            return append1(INT16).append2(i);
151         return append1(INT32).append4(i);
152      }
153      if (i > -(1<<6))
154         return append((byte)(0xE0 | -i));
155      if (i > -(1<<7))
156         return append1(INT8).append1(i);
157      if (i > -(1<<15))
158         return append1(INT16).append2(i);
159      return append1(INT32).append4(i);
160   }
161
162   final long L2X31 = ((long)(1<<30))*2;
163
164   /**
165    * Appends a long to the stream.
166    */
167   final MsgPackOutputStream appendLong(long l) {
168      if (l < L2X31 && l > -(L2X31))
169         return appendInt((int)l);
170      return append1(INT64).append8(l);
171   }
172
173   /**
174    * Appends a generic Number to the stream.
175    */
176   final MsgPackOutputStream appendNumber(Number n) {
177      Class<?> c = n.getClass();
178      if (c == Integer.class || c == Short.class || c == Byte.class || c == AtomicInteger.class)
179         return appendInt(n.intValue());
180      if (c == Long.class || c == AtomicLong.class)
181         return appendLong(n.longValue());
182      if (c == Float.class)
183         return appendFloat(n.floatValue());
184      if (c == Double.class)
185         return appendDouble(n.doubleValue());
186      if (c == BigInteger.class)
187         return appendLong(n.longValue());
188      if (c == BigDecimal.class)
189         return appendDouble(n.doubleValue());
190      return appendInt(0);
191   }
192
193   /**
194    * Appends a float to the stream.
195    */
196   final MsgPackOutputStream appendFloat(float f) {
197      // FLOAT32      = 0xCA,  //   float 32       11001010     0xca
198      return append1(FLOAT32).append4(Float.floatToIntBits(f));
199
200   }
201
202   /**
203    * Appends a double to the stream.
204    */
205   final MsgPackOutputStream appendDouble(double d) {
206      // FLOAT64      = 0xCB,  //   float 64       11001011     0xcb
207      return append1(FLOAT64).append8(Double.doubleToLongBits(d));
208   }
209
210   /**
211    * Appends a string to the stream.
212    */
213   final MsgPackOutputStream appendString(CharSequence cs) {
214
215      // fixstr stores a byte array whose length is up to 31 bytes:
216      // +--------+========+
217      // |101XXXXX|  data  |
218      // +--------+========+
219      //
220      // str 8 stores a byte array whose length is up to (2^8)-1 bytes:
221      // +--------+--------+========+
222      // |  0xd9  |YYYYYYYY|  data  |
223      // +--------+--------+========+
224      //
225      // str 16 stores a byte array whose length is up to (2^16)-1 bytes:
226      // +--------+--------+--------+========+
227      // |  0xda  |ZZZZZZZZ|ZZZZZZZZ|  data  |
228      // +--------+--------+--------+========+
229      //
230      // str 32 stores a byte array whose length is up to (2^32)-1 bytes:
231      // +--------+--------+--------+--------+--------+========+
232      // |  0xdb  |AAAAAAAA|AAAAAAAA|AAAAAAAA|AAAAAAAA|  data  |
233      // +--------+--------+--------+--------+--------+========+
234      // where
235      // * XXXXX is a 5-bit unsigned integer which represents N
236      // * YYYYYYYY is a 8-bit unsigned integer which represents N
237      // * ZZZZZZZZ_ZZZZZZZZ is a 16-bit big-endian unsigned integer which represents N
238      // * AAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA is a 32-bit big-endian unsigned integer which represents N
239      // * N is the length of data
240
241      int length = getUtf8ByteLength(cs);
242      if (length < 32)
243         append1(0xA0 + length);
244      else if (length < (1<<8))
245         append1(STR8).append1(length);
246      else if (length < (1<<16))
247         append1(STR16).append2(length);
248      else
249         append1(STR32).append4(length);
250
251      int length2 = writeUtf8To(cs, os);
252
253      if (length != length2)
254         throw new SerializeException("Unexpected length.  Expected={0}, Actual={1}", length, length2);
255
256      return this;
257   }
258
259   /**
260    * Appends a binary field to the stream.
261    */
262   final MsgPackOutputStream appendBinary(byte[] b) {
263      // bin 8 stores a byte array whose length is up to (2^8)-1 bytes:
264      // +--------+--------+========+
265      // |  0xc4  |XXXXXXXX|  data  |
266      // +--------+--------+========+
267      //
268      // bin 16 stores a byte array whose length is up to (2^16)-1 bytes:
269      // +--------+--------+--------+========+
270      // |  0xc5  |YYYYYYYY|YYYYYYYY|  data  |
271      // +--------+--------+--------+========+
272      //
273      // bin 32 stores a byte array whose length is up to (2^32)-1 bytes:
274      // +--------+--------+--------+--------+--------+========+
275      // |  0xc6  |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|  data  |
276      // +--------+--------+--------+--------+--------+========+
277      //
278      // where
279      // * XXXXXXXX is a 8-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 length of data
283
284      if (b.length < (1<<8))
285         return append1(BIN8).append1(b.length).append(b);
286      if (b.length < (1<<16))
287         return append1(BIN16).append2(b.length).append(b);
288      return append1(BIN32).append4(b.length).append(b);
289   }
290
291   /**
292    * Appends a binary field to the stream.
293    */
294   final MsgPackOutputStream appendBinary(InputStream is) {
295
296      ByteArrayOutputStream baos = new ByteArrayOutputStream();
297      IOUtils.pipe(is, baos, x -> { throw new SerializeException(x); });
298
299      byte[] b = baos.toByteArray();
300
301      // bin 8 stores a byte array whose length is up to (2^8)-1 bytes:
302      // +--------+--------+========+
303      // | 0xc4   |XXXXXXXX|  data  |
304      // +--------+--------+========+
305      //
306      // bin 16 stores a byte array whose length is up to (2^16)-1 bytes:
307      // +--------+--------+--------+========+
308      // | 0xc5   |YYYYYYYY|YYYYYYYY|  data  |
309      // +--------+--------+--------+========+
310      //
311      // bin 32 stores a byte array whose length is up to (2^32)-1 bytes:
312      // +--------+--------+--------+--------+--------+========+
313      // | 0xc6   |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|  data  |
314      // +--------+--------+--------+--------+--------+========+
315      //
316      // where
317      // * XXXXXXXX is a 8-bit unsigned integer which represents N
318      // * YYYYYYYY_YYYYYYYY is a 16-bit big-endian unsigned integer which represents N
319      // * ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ is a 32-bit big-endian unsigned integer which represents N
320      // * N is the length of data
321
322      if (b.length < (1<<8))
323         return append1(BIN8).append1(b.length).append(b);
324      if (b.length < (1<<16))
325         return append1(BIN16).append2(b.length).append(b);
326      return append1(BIN32).append4(b.length).append(b);
327   }
328
329   /**
330    * Appends an array data type flag to the stream.
331    */
332   final MsgPackOutputStream startArray(int size) {
333      // fixarray stores an array whose length is up to 15 elements:
334      // +--------+~~~~~~~~~~~~~~~~~+
335      // |1001XXXX|    N objects    |
336      // +--------+~~~~~~~~~~~~~~~~~+
337      //
338      // array 16 stores an array whose length is up to (2^16)-1 elements:
339      // +--------+--------+--------+~~~~~~~~~~~~~~~~~+
340      // |  0xdc  |YYYYYYYY|YYYYYYYY|    N objects    |
341      // +--------+--------+--------+~~~~~~~~~~~~~~~~~+
342      //
343      // array 32 stores an array whose length is up to (2^32)-1 elements:
344      // +--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+
345      // |  0xdd  |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|    N objects    |
346      // +--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+
347      //
348      // where
349      // * XXXX is a 4-bit unsigned integer which represents N
350      // * YYYYYYYY_YYYYYYYY is a 16-bit big-endian unsigned integer which represents N
351      // * ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ is a 32-bit big-endian unsigned integer which represents N
352      //     N is the size of a array
353
354      if (size < 16)
355         return append1(0x90 + size);
356      if (size < (1<<16))
357         return append1(ARRAY16).append2(size);
358      return append1(ARRAY32).append4(size);
359   }
360
361   /**
362    * Appends a map data type flag to the stream.
363    */
364   final MsgPackOutputStream startMap(int size) {
365      // fixmap stores a map whose length is up to 15 elements
366      // +--------+~~~~~~~~~~~~~~~~~+
367      // |1000XXXX|   N*2 objects   |
368      // +--------+~~~~~~~~~~~~~~~~~+
369      //
370      // map 16 stores a map whose length is up to (2^16)-1 elements
371      // +--------+--------+--------+~~~~~~~~~~~~~~~~~+
372      // |  0xde  |YYYYYYYY|YYYYYYYY|   N*2 objects   |
373      // +--------+--------+--------+~~~~~~~~~~~~~~~~~+
374      //
375      // map 32 stores a map whose length is up to (2^32)-1 elements
376      // +--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+
377      // |  0xdf  |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|   N*2 objects   |
378      // +--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+
379      //
380      // where
381      // * XXXX is a 4-bit unsigned integer which represents N
382      // * YYYYYYYY_YYYYYYYY is a 16-bit big-endian unsigned integer which represents N
383      // * ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ_ZZZZZZZZ is a 32-bit big-endian unsigned integer which represents N
384      // * N is the size of a map
385      // * odd elements in objects are keys of a map
386      // * the next element of a key is its associated value
387
388      if (size < 16)
389         return append1(0x80 + size);
390      if (size < (1<<16))
391         return append1(MAP16).append2(size);
392      return append1(MAP32).append4(size);
393   }
394
395   private int writeUtf8To(CharSequence in, OutputStream out) {
396      int count = 0;
397      for (int i = 0, len = in.length(); i < len; i++) {
398         int c = (in.charAt(i) & 0xFFFF);
399         if (c <= 0x7F) {
400            write((byte) (c & 0xFF));
401            count++;
402         } else if (c <= 0x7FF) {
403            write((byte) (0xC0 + ((c>>6) & 0x1F)));
404            write((byte) (0x80 + (c & 0x3F)));
405            count += 2;
406         } else if (c >= 0xD800 && c <= 0xDFFF) {
407            int jchar2 = in.charAt(++i) & 0xFFFF;
408            int n = (c<<10) + jchar2 + 0xFCA02400;
409            write((byte) (0xF0 + ((n>>18) & 0x07)));
410            write((byte) (0x80 + ((n>>12) & 0x3F)));
411            write((byte) (0x80 + ((n>>6) & 0x3F)));
412            write((byte) (0x80 + (n & 0x3F)));
413            count += 4;
414         } else {
415            write((byte) (0xE0 + ((c>>12) & 0x0F)));
416            write((byte) (0x80 + ((c>>6) & 0x3F)));
417            write((byte) (0x80 + (c & 0x3F)));
418            count += 3;
419         }
420      }
421      return count;
422   }
423
424   private int getUtf8ByteLength(CharSequence cs) {
425      int count = 0;
426      for (int i = 0, len = cs.length(); i < len; i++) {
427         char ch = cs.charAt(i);
428         if (ch <= 0x7F) {
429            count++;
430         } else if (ch <= 0x7FF) {
431            count += 2;
432         } else if (Character.isHighSurrogate(ch)) {
433            count += 4;
434            ++i;
435         } else {
436            count += 3;
437         }
438      }
439      return count;
440   }
441
442}