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}