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}