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