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