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