├── .gitattributes ├── .gitignore ├── README.md ├── pom.xml ├── release-notes ├── CREDITS └── VERSION └── src ├── main ├── java │ └── com │ │ └── fasterxml │ │ └── jackson │ │ └── dataformat │ │ └── cbor │ │ ├── CBORConstants.java │ │ ├── CBORFactory.java │ │ ├── CBORGenerator.java │ │ ├── CBORParser.java │ │ ├── CBORParserBootstrapper.java │ │ ├── CBORReadContext.java │ │ └── PackageVersion.java.in └── resources │ └── META-INF │ ├── LICENSE │ └── services │ └── com.fasterxml.jackson.core.JsonFactory └── test ├── java └── com │ └── fasterxml │ └── jackson │ └── dataformat │ └── cbor │ ├── CBORTestBase.java │ ├── GeneratorBinaryTest.java │ ├── GeneratorInteropTest.java │ ├── GeneratorLongStringTest.java │ ├── GeneratorSimpleTest.java │ ├── NumberBeanTest.java │ ├── ParserBinaryTest.java │ ├── ParserInputStreamTest.java │ ├── ParserInteropTest.java │ ├── ParserNextXxxTest.java │ ├── ParserSimpleTest.java │ ├── ParserWithJsonOrgSampleTest.java │ └── TestBiggerData.java └── resources └── data └── citm_catalog.json /.gitattributes: -------------------------------------------------------------------------------- 1 | # Do not merge `pom.xml` from older version, as it will typically conflict 2 | 3 | pom.xml merge=ours 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # use glob syntax. 2 | syntax: glob 3 | *.class 4 | *~ 5 | *.bak 6 | *.off 7 | *.old 8 | .DS_Store 9 | 10 | # building 11 | target 12 | 13 | # Eclipse 14 | .classpath 15 | .project 16 | .settings 17 | 18 | # IDEA 19 | *.iml 20 | *.ipr 21 | *.iws 22 | /target 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **NOTE**: This module has become part of [Jackson Binary Dataformats](../../../jackson-dataformats-binary) 2 | as of Jackson 2.8 3 | 4 | This repo still exists to allow release of patch versions of older versions; it will be hidden (made private) 5 | in near future. 6 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.fasterxml.jackson 6 | jackson-parent 7 | 2.7 8 | 9 | com.fasterxml.jackson.dataformat 10 | jackson-dataformat-cbor 11 | 2.7.9-SNAPSHOT 12 | Jackson-dataformat-CBOR 13 | bundle 14 | Support for reading and writing Concise Binary Object Representation 15 | ([CBOR](https://www.rfc-editor.org/info/rfc7049) 16 | encoded data using Jackson abstractions (streaming API, data binding, tree model) 17 | 18 | http://github.com/FasterXML/jackson-dataformat-cbor 19 | 20 | 21 | scm:git:git@github.com:FasterXML/jackson-dataformat-cbor.git 22 | scm:git:git@github.com:FasterXML/jackson-dataformat-cbor.git 23 | http://github.com/FasterXML/jackson-dataformat-cbor 24 | HEAD 25 | 26 | 27 | 28 | 2.7.8 29 | 30 | 31 | 1.6 32 | 1.6 33 | 34 | 35 | com/fasterxml/jackson/dataformat/cbor 36 | ${project.groupId}.cbor 37 | 40 | 41 | 42 | 43 | 44 | 45 | com.fasterxml.jackson.core 46 | jackson-core 47 | ${version.jackson.core} 48 | 49 | 50 | 51 | 52 | com.fasterxml.jackson.core 53 | jackson-databind 54 | ${version.jackson.core} 55 | test 56 | 57 | 58 | com.fasterxml.jackson.core 59 | jackson-annotations 60 | test 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | com.google.code.maven-replacer-plugin 69 | replacer 70 | 71 | 72 | process-packageVersion 73 | generate-sources 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /release-notes/CREDITS: -------------------------------------------------------------------------------- 1 | Here are people who have contributed to development of this project 2 | (version numbers in brackets indicate release in which the problem was fixed) 3 | 4 | Tatu Saloranta, tatu.saloranta@iki.fi: author 5 | 6 | Clinton Gormley (clintongormley@github) 7 | 8 | * Suggested [#5]: Support binary (byte[]) Object keys (assuming UTF-8 encoding) 9 | (2.4.3) 10 | * Suggested [#6]: Support 'self-describe' CBOR tag 11 | (2.4.3) 12 | 13 | mbaril@github) 14 | 15 | * Reported #9, suggested fix, contributed unit test: Infinite loop when trying 16 | to write binary data using CBORGenerator 17 | (2.5.1) 18 | 19 | Steve Gury (stevegury@github) 20 | 21 | * Reported #13, suggested fix: Bug in boundary checking in the CBORParser 22 | (2.6.2) 23 | 24 | Adrien Grand (jpountz@github) 25 | 26 | * Reported #15: CBORParser.getNumberType returns DOUBLE even if the generator 27 | has been fed with a float 28 | (2.6.5) 29 | 30 | philipa@githubL 31 | 32 | * Requested #20: Add a public `finishToken()` 33 | (2.7.2) 34 | * Requested #22: CBORGenerator.copyCurrentStructure() and copyCurrentEvent() do not copy tags 35 | (2.7.2) 36 | -------------------------------------------------------------------------------- /release-notes/VERSION: -------------------------------------------------------------------------------- 1 | Project: jackson-dataformat-cbor 2 | 3 | ------------------------------------------------------------------------ 4 | === Releases === 5 | ------------------------------------------------------------------------ 6 | 7 | 2.7.9 (04-Feb-2017) 8 | 2.7.8 (26-Sep-2016) 9 | 2.7.7 (27-Aug-2016) 10 | 11 | No changes since 2.7.6 12 | 13 | 2.7.6 (23-Jul-2016) 14 | 15 | base-modules#24: [cbor] Incorrect coercion for int-valued Map keys to String 16 | 17 | 2.7.5 (11-Jun-2016) 18 | 2.7.4 (29-Apr-2016) 19 | 2.7.3 (16-Mar-2016) 20 | 21 | No changes since 2.7.2 22 | 23 | 2.7.2 (27-Feb-2016) 24 | 25 | #20: Add a public `finishToken()` 26 | (requested by philipa@github) 27 | #22: CBORGenerator.copyCurrentStructure() and copyCurrentEvent() do not copy tags 28 | (requested by philipa@github) 29 | - Change build to produce JDK6-compatible jar, to allow use on JDK6 runtime 30 | 31 | 2.7.1 (02-Feb-2016) 32 | 33 | #19: Fix reported location after non-stream input has been parsed. 34 | (contributed by philipa@github) 35 | 36 | 2.7.0 (10-Jan-2016) 37 | 38 | #14: Add support for dynamically changing `CBORGenerator.Feature`s 39 | 40 | 2.6.6 (05-Apr-2016) 41 | 42 | #18: Correct parsing of zero length byte strings 43 | (reported, fix suggested by philipa@github) 44 | 45 | 2.6.5 (19-Jan-2016) 46 | 47 | #15: CBORParser.getNumberType returns DOUBLE even if the generator has been fed with a float 48 | (reported by Adrien G) 49 | 50 | 2.6.4 (07-Dec-2015) 51 | 2.6.3 (12-Oct-2015) 52 | 53 | No changes since 2.6.2 54 | 55 | 2.6.2 (15-Sep-2015) 56 | 57 | #13: Bug in boundary checking in the CBORParser 58 | (reported by Steve G) 59 | 60 | 2.6.1 (09-Aug-2015) 61 | 2.6.0 (19-Jul-2015) 62 | 63 | No changes since 2.5 64 | 65 | 2.5.5 (07-Dec-2015) 66 | 2.5.4 (09-Jun-2015) 67 | 2.5.3 (24-Apr-2015) 68 | 2.5.2 (29-Mar-2015) 69 | 70 | No changes since 2.5.1. 71 | 72 | 2.5.1 (06-Feb-2015) 73 | 74 | #9: Infinite loop when trying to write binary data using CBORGenerator 75 | (reported by mbaril@github) 76 | 77 | 2.5.0 (01-Jan-2015) 78 | 79 | #4: Implement `nextFieldName()` efficiently 80 | 81 | 2.4.6 (not yet released) 82 | 83 | 2.4.5 (13-Jan-2015) 84 | 2.4.4 (24-Nov-2014) 85 | 2.4.3 (04-Oct-2014) 86 | 87 | No changes. 88 | 89 | 2.4.2 (15-Aug-2014) 90 | 91 | #5: Support binary (byte[]) Object keys (assuming UTF-8 encoding) 92 | (suggested by Clinton G, clintongormley@github) 93 | #6: Support 'self-describe' CBOR tag 94 | (suggested by Clinton G, clintongormley@github) 95 | 96 | 2.4.1 (17-Jun-2014) 97 | 98 | - Minor performance improvement wrt `writeFieldName(SerializableString)` 99 | 100 | 2.4.0 (02-Jun-2014) 101 | 102 | No functional changes since 2.3.x. 103 | 104 | 2.3.3 (10-Apr-2014) 105 | 106 | #2: Negative Long values written as zero 107 | (reported by gjesse@github) 108 | 109 | 2.3.2 (01-Mar-2014) 110 | 111 | The very first public version! 112 | -------------------------------------------------------------------------------- /src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORConstants.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.dataformat.cbor; 2 | 3 | /** 4 | * Constants used by {@link CBORGenerator} and {@link CBORParser} 5 | * 6 | * @author Tatu Saloranta 7 | */ 8 | public final class CBORConstants 9 | { 10 | /* 11 | /********************************************************** 12 | /* Major type constants, matching prefixes 13 | /********************************************************** 14 | */ 15 | 16 | public final static int MAJOR_TYPE_INT_POS = 0; 17 | public final static int MAJOR_TYPE_INT_NEG = 1; 18 | public final static int MAJOR_TYPE_BYTES = 2; 19 | public final static int MAJOR_TYPE_TEXT = 3; 20 | public final static int MAJOR_TYPE_ARRAY = 4; 21 | public final static int MAJOR_TYPE_OBJECT = 5; 22 | public final static int MAJOR_TYPE_TAG = 6; 23 | public final static int MAJOR_TYPE_MISC = 7; 24 | 25 | public final static int PREFIX_TYPE_INT_POS = (MAJOR_TYPE_INT_POS << 5); 26 | public final static int PREFIX_TYPE_INT_NEG = (MAJOR_TYPE_INT_NEG << 5); 27 | public final static int PREFIX_TYPE_BYTES = (MAJOR_TYPE_BYTES << 5); 28 | public final static int PREFIX_TYPE_TEXT = (MAJOR_TYPE_TEXT << 5); 29 | public final static int PREFIX_TYPE_ARRAY = (MAJOR_TYPE_ARRAY << 5); 30 | public final static int PREFIX_TYPE_OBJECT = (MAJOR_TYPE_OBJECT << 5); 31 | public final static int PREFIX_TYPE_TAG = (MAJOR_TYPE_TAG << 5); 32 | public final static int PREFIX_TYPE_MISC = (MAJOR_TYPE_MISC << 5); 33 | 34 | /* 35 | /********************************************************** 36 | /* Other marker values 37 | /********************************************************** 38 | */ 39 | 40 | public final static int SUFFIX_INDEFINITE = 0x1F; 41 | 42 | public final static int MASK_MAJOR_TYPE = 0xE0; 43 | 44 | /* 45 | /********************************************************** 46 | /* Well-known Tag Ids 47 | /********************************************************** 48 | */ 49 | 50 | /** 51 | * As per spec, this is a sort of "nop" tag, useful as marker 52 | * for the very first root-level data item. 53 | */ 54 | public final static int TAG_ID_SELF_DESCRIBE = 55799; 55 | 56 | /* 57 | /********************************************************** 58 | /* Actual type and marker bytes 59 | /********************************************************** 60 | */ 61 | 62 | public final static byte BYTE_ARRAY_INDEFINITE = (byte) (PREFIX_TYPE_ARRAY + SUFFIX_INDEFINITE); 63 | public final static byte BYTE_OBJECT_INDEFINITE = (byte) (PREFIX_TYPE_OBJECT + SUFFIX_INDEFINITE); 64 | 65 | // 2-element array commonly used (for big float, f.ex.) 66 | public final static byte BYTE_ARRAY_2_ELEMENTS = (byte) (PREFIX_TYPE_ARRAY + 2); 67 | 68 | public final static byte BYTE_FALSE = (byte) (PREFIX_TYPE_MISC + 20); 69 | public final static byte BYTE_TRUE = (byte) (PREFIX_TYPE_MISC + 21); 70 | public final static byte BYTE_NULL = (byte) (PREFIX_TYPE_MISC + 22); 71 | 72 | public final static byte BYTE_EMPTY_STRING = (byte) (PREFIX_TYPE_TEXT); 73 | 74 | /** 75 | * String that is chunked 76 | */ 77 | public final static byte BYTE_STRING_INDEFINITE = (byte) (PREFIX_TYPE_TEXT + SUFFIX_INDEFINITE); 78 | 79 | public final static byte BYTE_STRING_1BYTE_LEN = (byte) (PREFIX_TYPE_TEXT + 24); 80 | public final static byte BYTE_STRING_2BYTE_LEN = (byte) (PREFIX_TYPE_TEXT + 25); 81 | 82 | public final static byte BYTE_FLOAT16 = (byte) (PREFIX_TYPE_MISC + 25); 83 | public final static byte BYTE_FLOAT32 = (byte) (PREFIX_TYPE_MISC + 26); 84 | public final static byte BYTE_FLOAT64 = (byte) (PREFIX_TYPE_MISC + 27); 85 | 86 | public final static byte BYTE_TAG_BIGNUM_POS = (byte) (PREFIX_TYPE_TAG + 2); 87 | public final static byte BYTE_TAG_BIGNUM_NEG = (byte) (PREFIX_TYPE_TAG + 3); 88 | public final static byte BYTE_TAG_DECIMAL_FRACTION = (byte) (PREFIX_TYPE_TAG + 4); 89 | public final static byte BYTE_TAG_BIGFLOAT = (byte) (PREFIX_TYPE_TAG + 5); 90 | 91 | public final static byte BYTE_BREAK = (byte) 0xFF; 92 | 93 | public final static int INT_BREAK = 0xFF; 94 | 95 | /* 96 | /********************************************************** 97 | /* Basic UTF-8 decode/encode table 98 | /********************************************************** 99 | */ 100 | 101 | /** 102 | * Additionally we can combine UTF-8 decoding info into similar 103 | * data table. 104 | * Values indicate "byte length - 1"; meaning -1 is used for 105 | * invalid bytes, 0 for single-byte codes, 1 for 2-byte codes 106 | * and 2 for 3-byte codes. 107 | */ 108 | public final static int[] sUtf8UnitLengths; 109 | static { 110 | int[] table = new int[256]; 111 | for (int c = 128; c < 256; ++c) { 112 | int code; 113 | 114 | // We'll add number of bytes needed for decoding 115 | if ((c & 0xE0) == 0xC0) { // 2 bytes (0x0080 - 0x07FF) 116 | code = 1; 117 | } else if ((c & 0xF0) == 0xE0) { // 3 bytes (0x0800 - 0xFFFF) 118 | code = 2; 119 | } else if ((c & 0xF8) == 0xF0) { 120 | // 4 bytes; double-char with surrogates and all... 121 | code = 3; 122 | } else { 123 | // And -1 seems like a good "universal" error marker... 124 | code = -1; 125 | } 126 | table[c] = code; 127 | } 128 | sUtf8UnitLengths = table; 129 | } 130 | 131 | public static boolean hasMajorType(int expType, byte encoded) { 132 | int actual = (encoded & MASK_MAJOR_TYPE) >> 5; 133 | return (actual == expType); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORFactory.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.dataformat.cbor; 2 | 3 | import java.io.*; 4 | import java.net.URL; 5 | 6 | import com.fasterxml.jackson.core.*; 7 | import com.fasterxml.jackson.core.format.InputAccessor; 8 | import com.fasterxml.jackson.core.format.MatchStrength; 9 | import com.fasterxml.jackson.core.io.IOContext; 10 | 11 | /** 12 | * Factory used for constructing {@link CBORParser} and {@link CBORGenerator} 13 | * instances; both of which handle 14 | * CBOR 15 | * encoded data. 16 | *

17 | * Extends {@link JsonFactory} mostly so that users can actually use it in place 18 | * of regular non-CBOR factory instances. 19 | *

20 | * Note on using non-byte-based sources/targets (char based, like 21 | * {@link java.io.Reader} and {@link java.io.Writer}): these can not be 22 | * used for CBOR documents; attempt will throw exception. 23 | * 24 | * @author Tatu Saloranta 25 | */ 26 | public class CBORFactory extends JsonFactory 27 | { 28 | private static final long serialVersionUID = 1; // 2.6 29 | 30 | /* 31 | /********************************************************** 32 | /* Constants 33 | /********************************************************** 34 | */ 35 | 36 | /** 37 | * Name used to identify CBOR format. 38 | * (and returned by {@link #getFormatName()} 39 | */ 40 | public final static String FORMAT_NAME = "CBOR"; 41 | 42 | /** 43 | * Bitfield (set of flags) of all parser features that are enabled 44 | * by default. 45 | */ 46 | final static int DEFAULT_CBOR_PARSER_FEATURE_FLAGS = CBORParser.Feature.collectDefaults(); 47 | 48 | /** 49 | * Bitfield (set of flags) of all generator features that are enabled 50 | * by default. 51 | */ 52 | final static int DEFAULT_CBOR_GENERATOR_FEATURE_FLAGS = CBORGenerator.Feature.collectDefaults(); 53 | 54 | /* 55 | /********************************************************** 56 | /* Configuration 57 | /********************************************************** 58 | */ 59 | 60 | protected int _formatParserFeatures; 61 | protected int _formatGeneratorFeatures; 62 | 63 | /* 64 | /********************************************************** 65 | /* Factory construction, configuration 66 | /********************************************************** 67 | */ 68 | 69 | /** 70 | * Default constructor used to create factory instances. 71 | * Creation of a factory instance is a light-weight operation, 72 | * but it is still a good idea to reuse limited number of 73 | * factory instances (and quite often just a single instance): 74 | * factories are used as context for storing some reused 75 | * processing objects (such as symbol tables parsers use) 76 | * and this reuse only works within context of a single 77 | * factory instance. 78 | */ 79 | public CBORFactory() { this(null); } 80 | 81 | public CBORFactory(ObjectCodec oc) { 82 | super(oc); 83 | _formatParserFeatures = DEFAULT_CBOR_PARSER_FEATURE_FLAGS; 84 | _formatGeneratorFeatures = DEFAULT_CBOR_GENERATOR_FEATURE_FLAGS; 85 | } 86 | 87 | /** 88 | * Note: REQUIRES at least 2.2.1 -- unfortunate intra-patch dep but seems 89 | * preferable to just leaving bug be as is 90 | * 91 | * @since 2.2.1 92 | */ 93 | public CBORFactory(CBORFactory src, ObjectCodec oc) 94 | { 95 | super(src, oc); 96 | _formatParserFeatures = src._formatParserFeatures; 97 | _formatGeneratorFeatures = src._formatGeneratorFeatures; 98 | } 99 | 100 | @Override 101 | public CBORFactory copy() 102 | { 103 | _checkInvalidCopy(CBORFactory.class); 104 | // note: as with base class, must NOT copy mapper reference 105 | return new CBORFactory(this, null); 106 | } 107 | 108 | /* 109 | /********************************************************** 110 | /* Serializable overrides 111 | /********************************************************** 112 | */ 113 | 114 | /** 115 | * Method that we need to override to actually make restoration go 116 | * through constructors etc. 117 | * Also: must be overridden by sub-classes as well. 118 | */ 119 | @Override 120 | protected Object readResolve() { 121 | return new CBORFactory(this, _objectCodec); 122 | } 123 | 124 | /* 125 | /********************************************************** 126 | /* Versioned 127 | /********************************************************** 128 | */ 129 | 130 | @Override 131 | public Version version() { 132 | return PackageVersion.VERSION; 133 | } 134 | 135 | /* 136 | /********************************************************** 137 | /* Format detection functionality 138 | /********************************************************** 139 | */ 140 | 141 | @Override 142 | public String getFormatName() { 143 | return FORMAT_NAME; 144 | } 145 | 146 | // Defaults work fine for this: 147 | // public boolean canUseSchema(FormatSchema schema) { } 148 | 149 | @Override 150 | public MatchStrength hasFormat(InputAccessor acc) throws IOException { 151 | return CBORParserBootstrapper.hasCBORFormat(acc); 152 | } 153 | 154 | /* 155 | /********************************************************** 156 | /* Capability introspection 157 | /********************************************************** 158 | */ 159 | 160 | @Override 161 | public boolean canHandleBinaryNatively() { 162 | return true; 163 | } 164 | 165 | @Override // since 2.6 166 | public Class getFormatReadFeatureType() { 167 | return CBORParser.Feature.class; 168 | } 169 | 170 | @Override // since 2.6 171 | public Class getFormatWriteFeatureType() { 172 | return CBORGenerator.Feature.class; 173 | } 174 | 175 | /* 176 | /********************************************************** 177 | /* Configuration, parser settings 178 | /********************************************************** 179 | */ 180 | 181 | /** 182 | * Method for enabling or disabling specified parser feature 183 | * (check {@link CBORParser.Feature} for list of features) 184 | */ 185 | public final CBORFactory configure(CBORParser.Feature f, boolean state) 186 | { 187 | if (state) { 188 | enable(f); 189 | } else { 190 | disable(f); 191 | } 192 | return this; 193 | } 194 | 195 | /** 196 | * Method for enabling specified parser feature 197 | * (check {@link CBORParser.Feature} for list of features) 198 | */ 199 | public CBORFactory enable(CBORParser.Feature f) { 200 | _formatParserFeatures |= f.getMask(); 201 | return this; 202 | } 203 | 204 | /** 205 | * Method for disabling specified parser features 206 | * (check {@link CBORParser.Feature} for list of features) 207 | */ 208 | public CBORFactory disable(CBORParser.Feature f) { 209 | _formatParserFeatures &= ~f.getMask(); 210 | return this; 211 | } 212 | 213 | /** 214 | * Checked whether specified parser feature is enabled. 215 | */ 216 | public final boolean isEnabled(CBORParser.Feature f) { 217 | return (_formatParserFeatures & f.getMask()) != 0; 218 | } 219 | 220 | /* 221 | /********************************************************** 222 | /* Configuration, generator settings 223 | /********************************************************** 224 | */ 225 | 226 | /** 227 | * Method for enabling or disabling specified generator feature 228 | * (check {@link CBORGenerator.Feature} for list of features) 229 | */ 230 | public final CBORFactory configure(CBORGenerator.Feature f, boolean state) { 231 | if (state) { 232 | enable(f); 233 | } else { 234 | disable(f); 235 | } 236 | return this; 237 | } 238 | 239 | 240 | /** 241 | * Method for enabling specified generator features 242 | * (check {@link CBORGenerator.Feature} for list of features) 243 | */ 244 | public CBORFactory enable(CBORGenerator.Feature f) { 245 | _formatGeneratorFeatures |= f.getMask(); 246 | return this; 247 | } 248 | 249 | /** 250 | * Method for disabling specified generator feature 251 | * (check {@link CBORGenerator.Feature} for list of features) 252 | */ 253 | public CBORFactory disable(CBORGenerator.Feature f) { 254 | _formatGeneratorFeatures &= ~f.getMask(); 255 | return this; 256 | } 257 | 258 | /** 259 | * Check whether specified generator feature is enabled. 260 | */ 261 | public final boolean isEnabled(CBORGenerator.Feature f) { 262 | return (_formatGeneratorFeatures & f.getMask()) != 0; 263 | } 264 | 265 | /* 266 | /********************************************************** 267 | /* Overridden parser factory methods, new (2.1) 268 | /********************************************************** 269 | */ 270 | 271 | @SuppressWarnings("resource") 272 | @Override 273 | public CBORParser createParser(File f) throws IOException { 274 | return _createParser(new FileInputStream(f), _createContext(f, true)); 275 | } 276 | 277 | @Override 278 | public CBORParser createParser(URL url) throws IOException { 279 | return _createParser(_optimizedStreamFromURL(url), _createContext(url, true)); 280 | } 281 | 282 | @Override 283 | public CBORParser createParser(InputStream in) throws IOException { 284 | return _createParser(in, _createContext(in, false)); 285 | } 286 | 287 | @Override 288 | public CBORParser createParser(byte[] data) throws IOException { 289 | return _createParser(data, 0, data.length, _createContext(data, true)); 290 | } 291 | 292 | @Override 293 | public CBORParser createParser(byte[] data, int offset, int len) throws IOException { 294 | return _createParser(data, offset, len, _createContext(data, true)); 295 | } 296 | 297 | /* 298 | /********************************************************** 299 | /* Overridden generator factory methods 300 | /********************************************************** 301 | */ 302 | 303 | /** 304 | * Method for constructing {@link JsonGenerator} for generating 305 | * CBOR-encoded output. 306 | *

307 | * Since CBOR format always uses UTF-8 internally, enc 308 | * argument is ignored. 309 | */ 310 | @Override 311 | public CBORGenerator createGenerator(OutputStream out, JsonEncoding enc) throws IOException { 312 | return _createCBORGenerator(_createContext(out, false), 313 | _generatorFeatures, _formatGeneratorFeatures, _objectCodec, out); 314 | } 315 | 316 | /** 317 | * Method for constructing {@link JsonGenerator} for generating 318 | * CBOR-encoded output. 319 | *

320 | * Since CBOR format always uses UTF-8 internally, no encoding need 321 | * to be passed to this method. 322 | */ 323 | @Override 324 | public CBORGenerator createGenerator(OutputStream out) throws IOException { 325 | return _createCBORGenerator(_createContext(out, false), 326 | _generatorFeatures, _formatGeneratorFeatures, _objectCodec, out); 327 | } 328 | 329 | /* 330 | /****************************************************** 331 | /* Overridden internal factory methods 332 | /****************************************************** 333 | */ 334 | 335 | @Override 336 | protected IOContext _createContext(Object srcRef, boolean resourceManaged) { 337 | return super._createContext(srcRef, resourceManaged); 338 | } 339 | 340 | /** 341 | * Overridable factory method that actually instantiates desired 342 | * parser. 343 | */ 344 | @Override 345 | protected CBORParser _createParser(InputStream in, IOContext ctxt) throws IOException 346 | { 347 | return new CBORParserBootstrapper(ctxt, in).constructParser(_factoryFeatures, 348 | _parserFeatures, _formatParserFeatures, 349 | _objectCodec, _byteSymbolCanonicalizer); 350 | } 351 | 352 | /** 353 | * Overridable factory method that actually instantiates desired 354 | * parser. 355 | */ 356 | @Override 357 | protected JsonParser _createParser(Reader r, IOContext ctxt) throws IOException { 358 | return _nonByteSource(); 359 | } 360 | 361 | @Override 362 | protected JsonParser _createParser(char[] data, int offset, int len, IOContext ctxt, 363 | boolean recyclable) throws IOException { 364 | return _nonByteSource(); 365 | } 366 | 367 | /** 368 | * Overridable factory method that actually instantiates desired 369 | * parser. 370 | */ 371 | @Override 372 | protected CBORParser _createParser(byte[] data, int offset, int len, IOContext ctxt) throws IOException 373 | { 374 | return new CBORParserBootstrapper(ctxt, data, offset, len).constructParser( 375 | _factoryFeatures, _parserFeatures, _formatParserFeatures, 376 | _objectCodec, _byteSymbolCanonicalizer); 377 | } 378 | 379 | @Override 380 | protected CBORGenerator _createGenerator(Writer out, IOContext ctxt) throws IOException { 381 | return _nonByteTarget(); 382 | } 383 | 384 | @Override 385 | protected CBORGenerator _createUTF8Generator(OutputStream out, IOContext ctxt) throws IOException { 386 | return _createCBORGenerator(ctxt, 387 | _generatorFeatures, _formatGeneratorFeatures, _objectCodec, out); 388 | } 389 | 390 | @Override 391 | protected Writer _createWriter(OutputStream out, JsonEncoding enc, IOContext ctxt) throws IOException { 392 | return _nonByteTarget(); 393 | } 394 | 395 | private final CBORGenerator _createCBORGenerator(IOContext ctxt, 396 | int stdFeat, int formatFeat, ObjectCodec codec, OutputStream out) throws IOException 397 | { 398 | // false -> we won't manage the stream unless explicitly directed to 399 | CBORGenerator gen = new CBORGenerator(ctxt, stdFeat, formatFeat, _objectCodec, out); 400 | if (CBORGenerator.Feature.WRITE_TYPE_HEADER.enabledIn(formatFeat)) { 401 | gen.writeTag(CBORConstants.TAG_ID_SELF_DESCRIBE); 402 | } 403 | return gen; 404 | } 405 | 406 | protected T _nonByteTarget() { 407 | throw new UnsupportedOperationException("Can not create generator for non-byte-based target"); 408 | } 409 | 410 | protected T _nonByteSource() { 411 | throw new UnsupportedOperationException("Can not create generator for non-byte-based source"); 412 | } 413 | } 414 | -------------------------------------------------------------------------------- /src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORGenerator.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.dataformat.cbor; 2 | 3 | import java.io.*; 4 | import java.math.BigDecimal; 5 | import java.math.BigInteger; 6 | 7 | import com.fasterxml.jackson.core.*; 8 | import com.fasterxml.jackson.core.io.*; 9 | import com.fasterxml.jackson.core.json.JsonWriteContext; 10 | import com.fasterxml.jackson.core.base.GeneratorBase; 11 | 12 | import static com.fasterxml.jackson.dataformat.cbor.CBORConstants.*; 13 | 14 | /** 15 | * {@link JsonGenerator} implementation that writes CBOR encoded content. 16 | * 17 | * @author Tatu Saloranta 18 | */ 19 | public class CBORGenerator extends GeneratorBase 20 | { 21 | /** 22 | * Let's ensure that we have big enough output buffer because of 23 | * safety margins we need for UTF-8 encoding. 24 | */ 25 | final static int BYTE_BUFFER_FOR_OUTPUT = 16000; 26 | 27 | /** 28 | * Longest char chunk we will output is chosen so that it is guaranteed to fit 29 | * in an empty buffer even if everything encoded in 3-byte sequences; but also 30 | * fit two full chunks in case of single-byte (ascii) output. 31 | */ 32 | private final static int MAX_LONG_STRING_CHARS = (BYTE_BUFFER_FOR_OUTPUT / 4) - 4; 33 | 34 | /** 35 | * This is the worst case length (in bytes) of maximum chunk we ever write. 36 | */ 37 | private final static int MAX_LONG_STRING_BYTES = (MAX_LONG_STRING_CHARS * 3) + 3; 38 | 39 | /** 40 | * Enumeration that defines all togglable features for CBOR generator. 41 | */ 42 | public enum Feature implements FormatFeature 43 | { 44 | /** 45 | * Feature that determines whether generator should try to use smallest (size-wise) 46 | * integer representation: if true, will use smallest representation that is enough 47 | * to retain value; if false, will use length indicated by argument type (4-byte 48 | * for int, 8-byte for long and so on). 49 | */ 50 | WRITE_MINIMAL_INTS(true), 51 | 52 | /** 53 | * Feature that determines whether CBOR "Self-Describe Tag" (value 55799, 54 | * encoded as 3-byte sequence of 0xD9, 0xD9, 0xF7) should be written 55 | * at the beginning of document or not. 56 | *

57 | * Default value is false meaning that type tag will not be written 58 | * at the beginning of a new document. 59 | * 60 | * @since 2.5 61 | */ 62 | WRITE_TYPE_HEADER(false), 63 | ; 64 | 65 | protected final boolean _defaultState; 66 | protected final int _mask; 67 | 68 | /** 69 | * Method that calculates bit set (flags) of all features that 70 | * are enabled by default. 71 | */ 72 | public static int collectDefaults() { 73 | int flags = 0; 74 | for (Feature f : values()) { 75 | if (f.enabledByDefault()) { 76 | flags |= f.getMask(); 77 | } 78 | } 79 | return flags; 80 | } 81 | 82 | private Feature(boolean defaultState) { 83 | _defaultState = defaultState; 84 | _mask = (1 << ordinal()); 85 | } 86 | 87 | @Override public boolean enabledByDefault() { return _defaultState; } 88 | @Override public boolean enabledIn(int flags) { return (flags & getMask()) != 0; } 89 | @Override public int getMask() { return _mask; } 90 | } 91 | 92 | /** 93 | * To simplify certain operations, we require output buffer length 94 | * to allow outputting of contiguous 256 character UTF-8 encoded String 95 | * value. Length of the longest UTF-8 code point (from Java char) is 3 bytes, 96 | * and we need both initial token byte and single-byte end marker 97 | * so we get following value. 98 | *

99 | * Note: actually we could live with shorter one; absolute minimum would 100 | * be for encoding 64-character Strings. 101 | */ 102 | private final static int MIN_BUFFER_LENGTH = (3 * 256) + 2; 103 | 104 | private final static long MIN_INT_AS_LONG = (long) Integer.MIN_VALUE; 105 | private final static long MAX_INT_AS_LONG = (long) Integer.MAX_VALUE; 106 | 107 | /* 108 | /********************************************************** 109 | /* Configuration 110 | /********************************************************** 111 | */ 112 | 113 | final protected IOContext _ioContext; 114 | 115 | final protected OutputStream _out; 116 | 117 | /** 118 | * Bit flag composed of bits that indicate which 119 | * {@link CBORGenerator.Feature}s 120 | * are enabled. 121 | */ 122 | protected int _formatFeatures; 123 | 124 | protected boolean _cfgMinimalInts; 125 | 126 | /* 127 | /********************************************************** 128 | /* Output buffering 129 | /********************************************************** 130 | */ 131 | 132 | /** 133 | * Intermediate buffer in which contents are buffered before 134 | * being written using {@link #_out}. 135 | */ 136 | protected byte[] _outputBuffer; 137 | 138 | /** 139 | * Pointer to the next available byte in {@link #_outputBuffer} 140 | */ 141 | protected int _outputTail = 0; 142 | 143 | /** 144 | * Offset to index after the last valid index in {@link #_outputBuffer}. 145 | * Typically same as length of the buffer. 146 | */ 147 | protected final int _outputEnd; 148 | 149 | /** 150 | * Intermediate buffer in which characters of a String are copied 151 | * before being encoded. 152 | */ 153 | protected char[] _charBuffer; 154 | 155 | protected final int _charBufferLength; 156 | 157 | /** 158 | * Let's keep track of how many bytes have been output, may prove useful 159 | * when debugging. This does not include bytes buffered in 160 | * the output buffer, just bytes that have been written using underlying 161 | * stream writer. 162 | */ 163 | protected int _bytesWritten; 164 | 165 | /* 166 | /********************************************************** 167 | /* Shared String detection 168 | /********************************************************** 169 | */ 170 | 171 | /** 172 | * Flag that indicates whether the output buffer is recycable (and 173 | * needs to be returned to recycler once we are done) or not. 174 | */ 175 | protected boolean _bufferRecyclable; 176 | 177 | /* 178 | /********************************************************** 179 | /* Life-cycle 180 | /********************************************************** 181 | */ 182 | 183 | public CBORGenerator(IOContext ctxt, int stdFeatures, int formatFeatures, 184 | ObjectCodec codec, OutputStream out) 185 | { 186 | super(stdFeatures, codec); 187 | _formatFeatures = formatFeatures; 188 | _cfgMinimalInts = Feature.WRITE_MINIMAL_INTS.enabledIn(formatFeatures); 189 | _ioContext = ctxt; 190 | _out = out; 191 | _bufferRecyclable = true; 192 | _outputBuffer = ctxt.allocWriteEncodingBuffer(BYTE_BUFFER_FOR_OUTPUT); 193 | _outputEnd = _outputBuffer.length; 194 | _charBuffer = ctxt.allocConcatBuffer(); 195 | _charBufferLength = _charBuffer.length; 196 | // let's just sanity check to prevent nasty odd errors 197 | if (_outputEnd < MIN_BUFFER_LENGTH) { 198 | throw new IllegalStateException("Internal encoding buffer length ("+_outputEnd 199 | +") too short, must be at least "+MIN_BUFFER_LENGTH); 200 | } 201 | } 202 | 203 | /** 204 | * Alternative constructor that may be used to feed partially initialized content. 205 | * 206 | * @param outputBuffer Buffer to use for output before flushing to the underlying stream 207 | * @param offset Offset pointing past already buffered content; that is, number of bytes of valid content 208 | * to output, within buffer. 209 | */ 210 | public CBORGenerator(IOContext ctxt, int stdFeatures, int formatFeatures, 211 | ObjectCodec codec, OutputStream out, byte[] outputBuffer, int offset, boolean bufferRecyclable) 212 | { 213 | super(stdFeatures, codec); 214 | _formatFeatures = formatFeatures; 215 | _cfgMinimalInts = Feature.WRITE_MINIMAL_INTS.enabledIn(formatFeatures); 216 | _ioContext = ctxt; 217 | _out = out; 218 | _bufferRecyclable = bufferRecyclable; 219 | _outputTail = offset; 220 | _outputBuffer = outputBuffer; 221 | _outputEnd = _outputBuffer.length; 222 | _charBuffer = ctxt.allocConcatBuffer(); 223 | _charBufferLength = _charBuffer.length; 224 | // let's just sanity check to prevent nasty odd errors 225 | if (_outputEnd < MIN_BUFFER_LENGTH) { 226 | throw new IllegalStateException("Internal encoding buffer length ("+_outputEnd 227 | +") too short, must be at least "+MIN_BUFFER_LENGTH); 228 | } 229 | } 230 | 231 | /* 232 | /********************************************************** 233 | /* Versioned 234 | /********************************************************** 235 | */ 236 | 237 | @Override 238 | public Version version() { 239 | return PackageVersion.VERSION; 240 | } 241 | 242 | /* 243 | /********************************************************** 244 | /* Capability introspection 245 | /********************************************************** 246 | */ 247 | 248 | @Override 249 | public boolean canWriteBinaryNatively() { 250 | return true; 251 | } 252 | 253 | /* 254 | /********************************************************** 255 | /* Overridden methods, configuration 256 | /********************************************************** 257 | */ 258 | 259 | /** 260 | * No way (or need) to indent anything, so let's block any attempts. 261 | * (should we throw an exception instead?) 262 | */ 263 | @Override 264 | public JsonGenerator useDefaultPrettyPrinter() { 265 | return this; 266 | } 267 | 268 | /** 269 | * No way (or need) to indent anything, so let's block any attempts. 270 | * (should we throw an exception instead?) 271 | */ 272 | @Override 273 | public JsonGenerator setPrettyPrinter(PrettyPrinter pp) { 274 | return this; 275 | } 276 | 277 | @Override 278 | public Object getOutputTarget() { 279 | return _out; 280 | } 281 | 282 | @Override 283 | public int getOutputBuffered() { 284 | return _outputTail; 285 | } 286 | 287 | // public JsonParser overrideStdFeatures(int values, int mask) 288 | 289 | @Override 290 | public int getFormatFeatures() { 291 | return _formatFeatures; 292 | } 293 | 294 | @Override 295 | public JsonGenerator overrideStdFeatures(int values, int mask) { 296 | int oldState = _features; 297 | int newState = (oldState & ~mask) | (values & mask); 298 | if (oldState != newState) { 299 | _features = newState; 300 | } 301 | return this; 302 | } 303 | 304 | @Override 305 | public JsonGenerator overrideFormatFeatures(int values, int mask) { 306 | int oldState = _formatFeatures; 307 | int newState = (_formatFeatures & ~mask) | (values & mask); 308 | if (oldState != newState) { 309 | _formatFeatures = newState; 310 | _cfgMinimalInts = Feature.WRITE_MINIMAL_INTS.enabledIn(newState); 311 | } 312 | return this; 313 | } 314 | 315 | /* 316 | /********************************************************** 317 | /* Overridden methods, write methods 318 | /********************************************************** 319 | */ 320 | 321 | /* And then methods overridden to make final, streamline some 322 | * aspects... 323 | */ 324 | 325 | @Override 326 | public final void writeFieldName(String name) throws IOException 327 | { 328 | if (_writeContext.writeFieldName(name) == JsonWriteContext.STATUS_EXPECT_VALUE) { 329 | _reportError("Can not write a field name, expecting a value"); 330 | } 331 | _writeString(name); 332 | } 333 | 334 | @Override 335 | public final void writeFieldName(SerializableString name) throws IOException 336 | { 337 | // Object is a value, need to verify it's allowed 338 | if (_writeContext.writeFieldName(name.getValue()) == JsonWriteContext.STATUS_EXPECT_VALUE) { 339 | _reportError("Can not write a field name, expecting a value"); 340 | } 341 | byte[] raw = name.asUnquotedUTF8(); 342 | final int len = raw.length; 343 | if (len == 0) { 344 | _writeByte(BYTE_EMPTY_STRING); 345 | return; 346 | } 347 | _writeLengthMarker(PREFIX_TYPE_TEXT, len); 348 | _writeBytes(raw, 0, len); 349 | } 350 | 351 | @Override 352 | public final void writeStringField(String fieldName, String value) throws IOException 353 | { 354 | if (_writeContext.writeFieldName(fieldName) == JsonWriteContext.STATUS_EXPECT_VALUE) { 355 | _reportError("Can not write a field name, expecting a value"); 356 | } 357 | _writeString(fieldName); 358 | // inlined from 'writeString()' 359 | if (value == null) { 360 | writeNull(); 361 | return; 362 | } 363 | _verifyValueWrite("write String value"); 364 | _writeString(value); 365 | } 366 | 367 | /* 368 | /********************************************************** 369 | /* Overridden methods, copying with tag-awareness 370 | /********************************************************** 371 | */ 372 | 373 | /** 374 | * Specialize {@link JsonGenerator#copyCurrentEvent} to handle tags. 375 | */ 376 | @Override 377 | public void copyCurrentEvent(JsonParser p) throws IOException { 378 | maybeCopyTag(p); 379 | super.copyCurrentEvent(p); 380 | } 381 | 382 | /** 383 | * Specialize {@link JsonGenerator#copyCurrentStructure} to handle tags. 384 | */ 385 | @Override 386 | public void copyCurrentStructure(JsonParser p) throws IOException { 387 | maybeCopyTag(p); 388 | super.copyCurrentStructure(p); 389 | } 390 | 391 | protected void maybeCopyTag(JsonParser p) throws IOException 392 | { 393 | if (p instanceof CBORParser) { 394 | if (p.hasCurrentToken()) { 395 | final int currentTag = ((CBORParser)p).getCurrentTag(); 396 | 397 | if (currentTag != -1) { 398 | writeTag(currentTag); 399 | } 400 | } 401 | } 402 | } 403 | 404 | /* 405 | /********************************************************** 406 | /* Extended API, configuration 407 | /********************************************************** 408 | */ 409 | 410 | public CBORGenerator enable(Feature f) { 411 | _formatFeatures |= f.getMask(); 412 | if (f == Feature.WRITE_MINIMAL_INTS) { 413 | _cfgMinimalInts = true; 414 | } 415 | return this; 416 | } 417 | 418 | public CBORGenerator disable(Feature f) { 419 | _formatFeatures &= ~f.getMask(); 420 | if (f == Feature.WRITE_MINIMAL_INTS) { 421 | _cfgMinimalInts = false; 422 | } 423 | return this; 424 | } 425 | 426 | public final boolean isEnabled(Feature f) { 427 | return (_formatFeatures & f.getMask()) != 0; 428 | } 429 | 430 | public CBORGenerator configure(Feature f, boolean state) { 431 | if (state) { 432 | enable(f); 433 | } else { 434 | disable(f); 435 | } 436 | return this; 437 | } 438 | 439 | /* 440 | /********************************************************** 441 | /* Extended API, CBOR-specific encoded output 442 | /********************************************************** 443 | */ 444 | 445 | /** 446 | * Method for writing out an explicit CBOR Tag. 447 | * 448 | * @param tagId Positive integer (0 or higher) 449 | * 450 | * @since 2.5 451 | */ 452 | public void writeTag(int tagId) throws IOException 453 | { 454 | if (tagId < 0) { 455 | throw new IllegalArgumentException("Can not write negative tag ids ("+tagId+")"); 456 | } 457 | _writeLengthMarker(PREFIX_TYPE_TAG, tagId); 458 | } 459 | 460 | /* 461 | /********************************************************** 462 | /* Extended API, raw bytes (by-passing encoder) 463 | /********************************************************** 464 | */ 465 | 466 | /** 467 | * Method for directly inserting specified byte in output at 468 | * current position. 469 | *

470 | * NOTE: only use this method if you really know what you are doing. 471 | */ 472 | public void writeRaw(byte b) throws IOException { 473 | _writeByte(b); 474 | } 475 | 476 | /** 477 | * Method for directly inserting specified bytes in output at 478 | * current position. 479 | *

480 | * NOTE: only use this method if you really know what you are doing. 481 | */ 482 | public void writeBytes(byte[] data, int offset, int len) throws IOException { 483 | _writeBytes(data, offset, len); 484 | } 485 | 486 | /* 487 | /********************************************************** 488 | /* Output method implementations, structural 489 | /********************************************************** 490 | */ 491 | 492 | @Override 493 | public final void writeStartArray() throws IOException 494 | { 495 | _verifyValueWrite("start an array"); 496 | _writeContext = _writeContext.createChildArrayContext(); 497 | _writeByte(BYTE_ARRAY_INDEFINITE); 498 | } 499 | 500 | // TODO: implement this for CBOR 501 | /* 502 | * Unlike with JSON, this method can use slightly optimized version 503 | * since CBOR has a variant that allows embedding length in array 504 | * start marker. But it mostly (or only?) makes sense for small 505 | * arrays, cases where length marker fits within type marker byte; 506 | * otherwise we might as well just use "indefinite" notation. 507 | */ 508 | @Override 509 | public void writeStartArray(int size) throws IOException { 510 | _verifyValueWrite("start an array"); 511 | _writeContext = _writeContext.createChildArrayContext(); 512 | /* 513 | if (size >= 31 || size < 0) { 514 | _writeByte(BYTE_ARRAY_INDEFINITE); 515 | } else { 516 | } 517 | */ 518 | _writeByte(BYTE_ARRAY_INDEFINITE); 519 | } 520 | 521 | @Override 522 | public final void writeEndArray() throws IOException 523 | { 524 | if (!_writeContext.inArray()) { 525 | _reportError("Current context not an ARRAY but "+_writeContext.getTypeDesc()); 526 | } 527 | _writeByte(BYTE_BREAK); 528 | _writeContext = _writeContext.getParent(); 529 | } 530 | 531 | @Override 532 | public final void writeStartObject() throws IOException 533 | { 534 | _verifyValueWrite("start an object"); 535 | _writeContext = _writeContext.createChildObjectContext(); 536 | _writeByte(BYTE_OBJECT_INDEFINITE); 537 | } 538 | 539 | @Override 540 | public final void writeEndObject() throws IOException 541 | { 542 | if (!_writeContext.inObject()) { 543 | _reportError("Current context not an object but "+_writeContext.getTypeDesc()); 544 | } 545 | _writeContext = _writeContext.getParent(); 546 | _writeByte(BYTE_BREAK); 547 | } 548 | 549 | /* 550 | /********************************************************** 551 | /* Output method implementations, textual 552 | /********************************************************** 553 | */ 554 | 555 | @Override 556 | public void writeString(String text) throws IOException,JsonGenerationException 557 | { 558 | if (text == null) { 559 | writeNull(); 560 | return; 561 | } 562 | _verifyValueWrite("write String value"); 563 | _writeString(text); 564 | } 565 | 566 | @Override 567 | public final void writeString(SerializableString sstr) throws IOException 568 | { 569 | _verifyValueWrite("write String value"); 570 | byte[] raw = sstr.asUnquotedUTF8(); 571 | final int len = raw.length; 572 | if (len == 0) { 573 | _writeByte(BYTE_EMPTY_STRING); 574 | return; 575 | } 576 | _writeLengthMarker(PREFIX_TYPE_TEXT, len); 577 | _writeBytes(raw, 0, len); 578 | } 579 | 580 | @Override 581 | public void writeString(char[] text, int offset, int len) throws IOException 582 | { 583 | _verifyValueWrite("write String value"); 584 | if (len == 0) { 585 | _writeByte(BYTE_EMPTY_STRING); 586 | return; 587 | } 588 | _writeString(text, offset, len); 589 | } 590 | 591 | @Override 592 | public void writeRawUTF8String(byte[] raw, int offset, int len) throws IOException 593 | { 594 | _verifyValueWrite("write String value"); 595 | if (len == 0) { 596 | _writeByte(BYTE_EMPTY_STRING); 597 | return; 598 | } 599 | _writeLengthMarker(PREFIX_TYPE_TEXT, len); 600 | _writeBytes(raw, 0, len); 601 | } 602 | 603 | @Override 604 | public final void writeUTF8String(byte[] text, int offset, int len) throws IOException 605 | { 606 | // Since no escaping is needed, same as 'writeRawUTF8String' 607 | writeRawUTF8String(text, offset, len); 608 | } 609 | 610 | /* 611 | /********************************************************** 612 | /* Output method implementations, unprocessed ("raw") 613 | /********************************************************** 614 | */ 615 | 616 | @Override 617 | public void writeRaw(String text) throws IOException { 618 | throw _notSupported(); 619 | } 620 | 621 | @Override 622 | public void writeRaw(String text, int offset, int len) throws IOException { 623 | throw _notSupported(); 624 | } 625 | 626 | @Override 627 | public void writeRaw(char[] text, int offset, int len) throws IOException { 628 | throw _notSupported(); 629 | } 630 | 631 | @Override 632 | public void writeRaw(char c) throws IOException { 633 | throw _notSupported(); 634 | } 635 | 636 | @Override 637 | public void writeRawValue(String text) throws IOException { 638 | throw _notSupported(); 639 | } 640 | 641 | @Override 642 | public void writeRawValue(String text, int offset, int len) throws IOException { 643 | throw _notSupported(); 644 | } 645 | 646 | @Override 647 | public void writeRawValue(char[] text, int offset, int len) throws IOException { 648 | throw _notSupported(); 649 | } 650 | 651 | /* 652 | /********************************************************** 653 | /* Output method implementations, base64-encoded binary 654 | /********************************************************** 655 | */ 656 | 657 | @Override 658 | public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len) throws IOException 659 | { 660 | if (data == null) { 661 | writeNull(); 662 | return; 663 | } 664 | _verifyValueWrite("write Binary value"); 665 | _writeLengthMarker(PREFIX_TYPE_BYTES, len); 666 | _writeBytes(data, offset, len); 667 | } 668 | 669 | @Override 670 | public int writeBinary(InputStream data, int dataLength) 671 | throws IOException 672 | { 673 | /* 28-Mar-2014, tatu: Theoretically we could implement encoder that uses 674 | * chunking to output binary content of unknown (a priori) length. 675 | * But for no let's require knowledge of length, for simplicity: may be 676 | * revisited in future. 677 | */ 678 | if (dataLength < 0) { 679 | throw new UnsupportedOperationException("Must pass actual length for CBOR encoded data"); 680 | } 681 | _verifyValueWrite("write Binary value"); 682 | int missing; 683 | 684 | _writeLengthMarker(PREFIX_TYPE_BYTES, dataLength); 685 | missing = _writeBytes(data, dataLength); 686 | if (missing > 0) { 687 | _reportError("Too few bytes available: missing "+missing+" bytes (out of "+dataLength+")"); 688 | } 689 | return dataLength; 690 | } 691 | 692 | @Override 693 | public int writeBinary(Base64Variant b64variant, InputStream data, int dataLength) 694 | throws IOException 695 | { 696 | return writeBinary(data, dataLength); 697 | } 698 | 699 | /* 700 | /********************************************************** 701 | /* Output method implementations, primitive 702 | /********************************************************** 703 | */ 704 | 705 | @Override 706 | public void writeBoolean(boolean state) throws IOException 707 | { 708 | _verifyValueWrite("write boolean value"); 709 | if (state) { 710 | _writeByte(BYTE_TRUE); 711 | } else { 712 | _writeByte(BYTE_FALSE); 713 | } 714 | } 715 | 716 | @Override 717 | public void writeNull() throws IOException 718 | { 719 | _verifyValueWrite("write null value"); 720 | _writeByte(BYTE_NULL); 721 | } 722 | 723 | @Override 724 | public void writeNumber(int i) throws IOException 725 | { 726 | _verifyValueWrite("write number"); 727 | int marker; 728 | if (i < 0) { 729 | i = -i - 1; 730 | marker = PREFIX_TYPE_INT_NEG; 731 | } else { 732 | marker = PREFIX_TYPE_INT_POS; 733 | } 734 | 735 | _ensureRoomForOutput(5); 736 | byte b0; 737 | if (_cfgMinimalInts) { 738 | if (i < 24) { 739 | _outputBuffer[_outputTail++] = (byte) (marker + i); 740 | return; 741 | } 742 | if (i <= 0xFF) { 743 | _outputBuffer[_outputTail++] = (byte) (marker + 24); 744 | _outputBuffer[_outputTail++] = (byte) i; 745 | return; 746 | } 747 | b0 = (byte) i; 748 | i >>= 8; 749 | if (i <= 0xFF) { 750 | _outputBuffer[_outputTail++] = (byte) (marker + 25); 751 | _outputBuffer[_outputTail++] = (byte) i; 752 | _outputBuffer[_outputTail++] = b0; 753 | return; 754 | } 755 | } else { 756 | b0 = (byte) i; 757 | i >>= 8; 758 | } 759 | _outputBuffer[_outputTail++] = (byte) (marker + 26); 760 | _outputBuffer[_outputTail++] = (byte) (i >> 16); 761 | _outputBuffer[_outputTail++] = (byte) (i >> 8); 762 | _outputBuffer[_outputTail++] = (byte) i; 763 | _outputBuffer[_outputTail++] = b0; 764 | } 765 | 766 | @Override 767 | public void writeNumber(long l) throws IOException 768 | { 769 | if (_cfgMinimalInts) { 770 | // First: maybe 32 bits is enough? 771 | if (l <= MAX_INT_AS_LONG && l >= MIN_INT_AS_LONG) { 772 | writeNumber((int) l); 773 | return; 774 | } 775 | } 776 | _verifyValueWrite("write number"); 777 | _ensureRoomForOutput(9); 778 | if (l < 0L) { 779 | l += 1; 780 | l = -l; 781 | _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_NEG + 27); 782 | } else { 783 | _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_POS + 27); 784 | } 785 | int i = (int) (l >> 32); 786 | _outputBuffer[_outputTail++] = (byte) (i >> 24); 787 | _outputBuffer[_outputTail++] = (byte) (i >> 16); 788 | _outputBuffer[_outputTail++] = (byte) (i >> 8); 789 | _outputBuffer[_outputTail++] = (byte) i; 790 | i = (int) l; 791 | _outputBuffer[_outputTail++] = (byte) (i >> 24); 792 | _outputBuffer[_outputTail++] = (byte) (i >> 16); 793 | _outputBuffer[_outputTail++] = (byte) (i >> 8); 794 | _outputBuffer[_outputTail++] = (byte) i; 795 | } 796 | 797 | @Override 798 | public void writeNumber(BigInteger v) throws IOException 799 | { 800 | if (v == null) { 801 | writeNull(); 802 | return; 803 | } 804 | _verifyValueWrite("write number"); 805 | 806 | /* Supported by using type tags, as per spec: major type for tag '6'; 807 | * 5 LSB either 2 for positive bignum or 3 for negative bignum. 808 | * And then byte sequence that encode variable length integer. 809 | */ 810 | if (v.signum() < 0) { 811 | _writeByte(BYTE_TAG_BIGNUM_NEG); 812 | v = v.negate(); 813 | } else { 814 | _writeByte(BYTE_TAG_BIGNUM_POS); 815 | } 816 | byte[] data = v.toByteArray(); 817 | final int len = data.length; 818 | _writeLengthMarker(PREFIX_TYPE_BYTES, len); 819 | _writeBytes(data, 0, len); 820 | } 821 | 822 | @Override 823 | public void writeNumber(double d) throws IOException 824 | { 825 | _verifyValueWrite("write number"); 826 | _ensureRoomForOutput(11); 827 | /* 17-Apr-2010, tatu: could also use 'doubleToIntBits', but it seems more accurate to use 828 | * exact representation; and possibly faster. However, if there are cases 829 | * where collapsing of NaN was needed (for non-Java clients), this can 830 | * be changed 831 | */ 832 | long l = Double.doubleToRawLongBits(d); 833 | _outputBuffer[_outputTail++] = BYTE_FLOAT64; 834 | 835 | int i = (int) (l >> 32); 836 | _outputBuffer[_outputTail++] = (byte) (i >> 24); 837 | _outputBuffer[_outputTail++] = (byte) (i >> 16); 838 | _outputBuffer[_outputTail++] = (byte) (i >> 8); 839 | _outputBuffer[_outputTail++] = (byte) i; 840 | i = (int) l; 841 | _outputBuffer[_outputTail++] = (byte) (i >> 24); 842 | _outputBuffer[_outputTail++] = (byte) (i >> 16); 843 | _outputBuffer[_outputTail++] = (byte) (i >> 8); 844 | _outputBuffer[_outputTail++] = (byte) i; 845 | } 846 | 847 | @Override 848 | public void writeNumber(float f) throws IOException 849 | { 850 | // Ok, now, we needed token type byte plus 5 data bytes (7 bits each) 851 | _ensureRoomForOutput(6); 852 | _verifyValueWrite("write number"); 853 | 854 | /* 17-Apr-2010, tatu: could also use 'floatToIntBits', but it seems more accurate to use 855 | * exact representation; and possibly faster. However, if there are cases 856 | * where collapsing of NaN was needed (for non-Java clients), this can 857 | * be changed 858 | */ 859 | int i = Float.floatToRawIntBits(f); 860 | _outputBuffer[_outputTail++] = BYTE_FLOAT32; 861 | _outputBuffer[_outputTail++] = (byte) (i >> 24); 862 | _outputBuffer[_outputTail++] = (byte) (i >> 16); 863 | _outputBuffer[_outputTail++] = (byte) (i >> 8); 864 | _outputBuffer[_outputTail++] = (byte) i; 865 | } 866 | 867 | @Override 868 | public void writeNumber(BigDecimal dec) throws IOException 869 | { 870 | if (dec == null) { 871 | writeNull(); 872 | return; 873 | } 874 | _verifyValueWrite("write number"); 875 | /* Supported by using type tags, as per spec: major type for tag '6'; 876 | * 5 LSB 4. 877 | * And then a two-int array, with mantissa and exponent 878 | */ 879 | _writeByte(BYTE_TAG_BIGFLOAT); 880 | _writeByte(BYTE_ARRAY_2_ELEMENTS); 881 | 882 | int scale = dec.scale(); 883 | _writeIntValue(scale); 884 | 885 | /* Hmmmh. Specification suggest use of regular integer for mantissa. 886 | * But... it may or may not fit. Let's try to do that, if it works; 887 | * if not, use byte array. 888 | */ 889 | BigInteger unscaled = dec.unscaledValue(); 890 | byte[] data = unscaled.toByteArray(); 891 | if (data.length <= 4) { 892 | int v = data[0]; // let it be sign extended on purpose 893 | for (int i = 1; i < data.length; ++i) { 894 | v = (v << 8) + (data[i] & 0xFF); 895 | } 896 | _writeIntValue(v); 897 | } else if (data.length <= 8) { 898 | long v = data[0]; // let it be sign extended on purpose 899 | for (int i = 1; i < data.length; ++i) { 900 | v = (v << 8) + (data[i] & 0xFF); 901 | } 902 | _writeLongValue(v); 903 | } else { 904 | final int len = data.length; 905 | _writeLengthMarker(PREFIX_TYPE_BYTES, len); 906 | _writeBytes(data, 0, len); 907 | } 908 | } 909 | 910 | @Override 911 | public void writeNumber(String encodedValue) throws IOException,JsonGenerationException, UnsupportedOperationException 912 | { 913 | // just write as a String -- CBOR does not require schema, so databinding 914 | // on receiving end should be able to coerce it appropriately 915 | writeString(encodedValue); 916 | } 917 | 918 | /* 919 | /********************************************************** 920 | /* Implementations for other methods 921 | /********************************************************** 922 | */ 923 | 924 | @Override 925 | protected final void _verifyValueWrite(String typeMsg) throws IOException 926 | { 927 | int status = _writeContext.writeValue(); 928 | if (status == JsonWriteContext.STATUS_EXPECT_NAME) { 929 | _reportError("Can not "+typeMsg+", expecting field name"); 930 | } 931 | } 932 | 933 | /* 934 | /********************************************************** 935 | /* Low-level output handling 936 | /********************************************************** 937 | */ 938 | 939 | @Override 940 | public final void flush() throws IOException 941 | { 942 | _flushBuffer(); 943 | if (isEnabled(JsonGenerator.Feature.FLUSH_PASSED_TO_STREAM)) { 944 | _out.flush(); 945 | } 946 | } 947 | 948 | @Override 949 | public void close() throws IOException 950 | { 951 | // First: let's see that we still have buffers... 952 | if ((_outputBuffer != null) 953 | && isEnabled(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT)) { 954 | while (true) { 955 | /* 28-May-2016, tatu: To work around incompatibility introduced by 956 | * `jackson-core` 2.8 where return type of `getOutputContext()` 957 | * changed, let's do direct access here. Not a good idea generally, 958 | * but where saves us from some binary incompatibility. 959 | */ 960 | // JsonStreamContext ctxt = getOutputContext(); 961 | JsonStreamContext ctxt = _writeContext; 962 | if (ctxt.inArray()) { 963 | writeEndArray(); 964 | } else if (ctxt.inObject()) { 965 | writeEndObject(); 966 | } else { 967 | break; 968 | } 969 | } 970 | } 971 | // boolean wasClosed = _closed; 972 | super.close(); 973 | _flushBuffer(); 974 | 975 | if (_ioContext.isResourceManaged() || isEnabled(JsonGenerator.Feature.AUTO_CLOSE_TARGET)) { 976 | _out.close(); 977 | } else { 978 | // If we can't close it, we should at least flush 979 | _out.flush(); 980 | } 981 | // Internal buffer(s) generator has can now be released as well 982 | _releaseBuffers(); 983 | } 984 | 985 | 986 | /* 987 | /********************************************************** 988 | /* Internal methods: low-level text output 989 | /********************************************************** 990 | */ 991 | 992 | protected final void _writeString(String name) throws IOException 993 | { 994 | int len = name.length(); 995 | if (len == 0) { 996 | _writeByte(BYTE_EMPTY_STRING); 997 | return; 998 | } 999 | // Actually, let's not bother with copy for shortest strings 1000 | if (len <= MAX_SHORT_STRING_CHARS) { 1001 | _ensureSpace(MAX_SHORT_STRING_BYTES); // can afford approximate length 1002 | int actual = _encode(_outputTail+1, name, len); 1003 | final byte[] buf = _outputBuffer; 1004 | int ix = _outputTail; 1005 | if (actual < MAX_SHORT_STRING_CHARS) { // fits in prefix byte 1006 | buf[ix++] = (byte) (PREFIX_TYPE_TEXT + actual); 1007 | _outputTail = ix + actual; 1008 | return; 1009 | } 1010 | // no, have to move. Blah. 1011 | System.arraycopy(buf, ix+1, buf, ix+2, actual); 1012 | buf[ix++] = BYTE_STRING_1BYTE_LEN; 1013 | buf[ix++] = (byte) actual; 1014 | _outputTail = ix+actual; 1015 | return; 1016 | } 1017 | 1018 | char[] cbuf = _charBuffer; 1019 | if (len > cbuf.length) { 1020 | _charBuffer = cbuf = new char[Math.max(_charBuffer.length + 32, len)]; 1021 | } 1022 | name.getChars(0, len, cbuf, 0); 1023 | _writeString(cbuf, 0, len); 1024 | } 1025 | 1026 | private final static int MAX_SHORT_STRING_CHARS = 23; 1027 | private final static int MAX_SHORT_STRING_BYTES = 23 * 3 + 2; // in case it's > 23 bytes 1028 | 1029 | private final static int MAX_MEDIUM_STRING_CHARS = 255; 1030 | private final static int MAX_MEDIUM_STRING_BYTES = 255 * 3 + 3; // in case it's > 255 bytes 1031 | 1032 | protected final void _ensureSpace(int needed) throws IOException { 1033 | if ((_outputTail + needed + 3) > _outputEnd) { 1034 | _flushBuffer(); 1035 | } 1036 | } 1037 | 1038 | protected final void _writeString(char[] text, int offset, int len) throws IOException 1039 | { 1040 | if (len <= MAX_SHORT_STRING_CHARS) { // possibly short strings (not necessarily) 1041 | _ensureSpace(MAX_SHORT_STRING_BYTES); // can afford approximate length 1042 | int actual = _encode(_outputTail+1, text, offset, offset+len); 1043 | final byte[] buf = _outputBuffer; 1044 | int ix = _outputTail; 1045 | if (actual < MAX_SHORT_STRING_CHARS) { // fits in prefix byte 1046 | buf[ix++] = (byte) (PREFIX_TYPE_TEXT + actual); 1047 | _outputTail = ix + actual; 1048 | return; 1049 | } 1050 | // no, have to move. Blah. 1051 | System.arraycopy(buf, ix+1, buf, ix+2, actual); 1052 | buf[ix++] = BYTE_STRING_1BYTE_LEN; 1053 | buf[ix++] = (byte) actual; 1054 | _outputTail = ix+actual; 1055 | return; 1056 | } 1057 | if (len <= MAX_MEDIUM_STRING_CHARS) { 1058 | _ensureSpace(MAX_MEDIUM_STRING_BYTES); // short enough, can approximate 1059 | int actual = _encode(_outputTail+2, text, offset, offset+len); 1060 | final byte[] buf = _outputBuffer; 1061 | int ix = _outputTail; 1062 | if (actual < MAX_MEDIUM_STRING_CHARS) { // fits as expected 1063 | buf[ix++] = BYTE_STRING_1BYTE_LEN; 1064 | buf[ix++] = (byte) actual; 1065 | _outputTail = ix + actual; 1066 | return; 1067 | } 1068 | // no, have to move. Blah. 1069 | System.arraycopy(buf, ix+2, buf, ix+3, actual); 1070 | buf[ix++] = BYTE_STRING_2BYTE_LEN; 1071 | buf[ix++] = (byte) (actual >> 8); 1072 | buf[ix++] = (byte) actual; 1073 | _outputTail = ix+actual; 1074 | return; 1075 | } 1076 | if (len <= MAX_LONG_STRING_CHARS) { // no need to chunk yet 1077 | // otherwise, long but single chunk 1078 | _ensureSpace(MAX_LONG_STRING_BYTES); // calculate accurate length to avoid extra flushing 1079 | int ix = _outputTail; 1080 | int actual = _encode(ix+3, text, offset, offset+len); 1081 | final byte[] buf = _outputBuffer; 1082 | buf[ix++] = BYTE_STRING_2BYTE_LEN; 1083 | buf[ix++] = (byte) (actual >> 8); 1084 | buf[ix++] = (byte) actual; 1085 | _outputTail = ix+actual; 1086 | return; 1087 | } 1088 | _writeChunkedString(text, offset, len); 1089 | } 1090 | 1091 | protected final void _writeChunkedString(char[] text, int offset, int len) throws IOException 1092 | { 1093 | // need to use a marker first 1094 | _writeByte(BYTE_STRING_INDEFINITE); 1095 | 1096 | while (len > MAX_LONG_STRING_CHARS) { 1097 | _ensureSpace(MAX_LONG_STRING_BYTES); // marker and single-byte length? 1098 | int ix = _outputTail; 1099 | int actual = _encode(_outputTail+3, text, offset, offset+MAX_LONG_STRING_CHARS); 1100 | final byte[] buf = _outputBuffer; 1101 | buf[ix++] = BYTE_STRING_2BYTE_LEN; 1102 | buf[ix++] = (byte) (actual >> 8); 1103 | buf[ix++] = (byte) actual; 1104 | _outputTail = ix+actual; 1105 | offset += MAX_LONG_STRING_CHARS; 1106 | len -= MAX_LONG_STRING_CHARS; 1107 | } 1108 | // and for the last chunk, just use recursion 1109 | if (len > 0) { 1110 | _writeString(text, offset, len); 1111 | } 1112 | // plus end marker 1113 | _writeByte(BYTE_BREAK); 1114 | } 1115 | 1116 | /* 1117 | /********************************************************** 1118 | /* Internal methods, UTF-8 encoding 1119 | /********************************************************** 1120 | */ 1121 | 1122 | /** 1123 | * Helper method called when the whole character sequence is known to 1124 | * fit in the output buffer regardless of UTF-8 expansion. 1125 | */ 1126 | private final int _encode(int outputPtr, char[] str, int i, int end) 1127 | { 1128 | // First: let's see if it's all ASCII: that's rather fast 1129 | final byte[] outBuf = _outputBuffer; 1130 | final int outputStart = outputPtr; 1131 | do { 1132 | int c = str[i]; 1133 | if (c > 0x7F) { 1134 | return _shortUTF8Encode2(str, i, end, outputPtr, outputStart); 1135 | } 1136 | outBuf[outputPtr++] = (byte) c; 1137 | } while (++i < end); 1138 | return outputPtr - outputStart; 1139 | } 1140 | 1141 | /** 1142 | * Helper method called when the whole character sequence is known to 1143 | * fit in the output buffer, but not all characters are single-byte (ASCII) 1144 | * characters. 1145 | */ 1146 | private final int _shortUTF8Encode2(char[] str, int i, int end, 1147 | int outputPtr, int outputStart) 1148 | { 1149 | final byte[] outBuf = _outputBuffer; 1150 | while (i < end) { 1151 | int c = str[i++]; 1152 | if (c <= 0x7F) { 1153 | outBuf[outputPtr++] = (byte) c; 1154 | continue; 1155 | } 1156 | // Nope, multi-byte: 1157 | if (c < 0x800) { // 2-byte 1158 | outBuf[outputPtr++] = (byte) (0xc0 | (c >> 6)); 1159 | outBuf[outputPtr++] = (byte) (0x80 | (c & 0x3f)); 1160 | continue; 1161 | } 1162 | // 3 or 4 bytes (surrogate) 1163 | // Surrogates? 1164 | if (c < SURR1_FIRST || c > SURR2_LAST) { // nope, regular 3-byte character 1165 | outBuf[outputPtr++] = (byte) (0xe0 | (c >> 12)); 1166 | outBuf[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); 1167 | outBuf[outputPtr++] = (byte) (0x80 | (c & 0x3f)); 1168 | continue; 1169 | } 1170 | // Yup, a surrogate pair 1171 | if (c > SURR1_LAST) { // must be from first range; second won't do 1172 | _throwIllegalSurrogate(c); 1173 | } 1174 | // ... meaning it must have a pair 1175 | if (i >= end) { 1176 | _throwIllegalSurrogate(c); 1177 | } 1178 | c = _convertSurrogate(c, str[i++]); 1179 | if (c > 0x10FFFF) { // illegal in JSON as well as in XML 1180 | _throwIllegalSurrogate(c); 1181 | } 1182 | outBuf[outputPtr++] = (byte) (0xf0 | (c >> 18)); 1183 | outBuf[outputPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f)); 1184 | outBuf[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); 1185 | outBuf[outputPtr++] = (byte) (0x80 | (c & 0x3f)); 1186 | } 1187 | return (outputPtr - outputStart); 1188 | } 1189 | 1190 | private final int _encode(int outputPtr, String str, int len) 1191 | { 1192 | final byte[] outBuf = _outputBuffer; 1193 | final int outputStart = outputPtr; 1194 | 1195 | for (int i = 0; i < len; ++i) { 1196 | int c = str.charAt(i); 1197 | if (c > 0x7F) { 1198 | return _encode2(i, outputPtr, str, len, outputStart); 1199 | } 1200 | outBuf[outputPtr++] = (byte) c; 1201 | } 1202 | return (outputPtr - outputStart); 1203 | } 1204 | 1205 | private final int _encode2(int i, int outputPtr, String str, int len, 1206 | int outputStart) 1207 | { 1208 | final byte[] outBuf = _outputBuffer; 1209 | // no; non-ASCII stuff, slower loop 1210 | while (i < len) { 1211 | int c = str.charAt(i++); 1212 | if (c <= 0x7F) { 1213 | outBuf[outputPtr++] = (byte) c; 1214 | continue; 1215 | } 1216 | // Nope, multi-byte: 1217 | if (c < 0x800) { // 2-byte 1218 | outBuf[outputPtr++] = (byte) (0xc0 | (c >> 6)); 1219 | outBuf[outputPtr++] = (byte) (0x80 | (c & 0x3f)); 1220 | continue; 1221 | } 1222 | // 3 or 4 bytes (surrogate) 1223 | // Surrogates? 1224 | if (c < SURR1_FIRST || c > SURR2_LAST) { // nope, regular 3-byte character 1225 | outBuf[outputPtr++] = (byte) (0xe0 | (c >> 12)); 1226 | outBuf[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); 1227 | outBuf[outputPtr++] = (byte) (0x80 | (c & 0x3f)); 1228 | continue; 1229 | } 1230 | // Yup, a surrogate pair 1231 | if (c > SURR1_LAST) { // must be from first range; second won't do 1232 | _throwIllegalSurrogate(c); 1233 | } 1234 | // ... meaning it must have a pair 1235 | if (i >= len) { 1236 | _throwIllegalSurrogate(c); 1237 | } 1238 | c = _convertSurrogate(c, str.charAt(i++)); 1239 | if (c > 0x10FFFF) { // illegal in JSON as well as in XML 1240 | _throwIllegalSurrogate(c); 1241 | } 1242 | outBuf[outputPtr++] = (byte) (0xf0 | (c >> 18)); 1243 | outBuf[outputPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f)); 1244 | outBuf[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); 1245 | outBuf[outputPtr++] = (byte) (0x80 | (c & 0x3f)); 1246 | } 1247 | return (outputPtr - outputStart); 1248 | } 1249 | 1250 | /** 1251 | * Method called to calculate UTF codepoint, from a surrogate pair. 1252 | */ 1253 | private int _convertSurrogate(int firstPart, int secondPart) 1254 | { 1255 | // Ok, then, is the second part valid? 1256 | if (secondPart < SURR2_FIRST || secondPart > SURR2_LAST) { 1257 | throw new IllegalArgumentException("Broken surrogate pair: first char 0x"+Integer.toHexString(firstPart)+", second 0x"+Integer.toHexString(secondPart)+"; illegal combination"); 1258 | } 1259 | return 0x10000 + ((firstPart - SURR1_FIRST) << 10) + (secondPart - SURR2_FIRST); 1260 | } 1261 | 1262 | private void _throwIllegalSurrogate(int code) 1263 | { 1264 | if (code > 0x10FFFF) { // over max? 1265 | throw new IllegalArgumentException("Illegal character point (0x"+Integer.toHexString(code)+") to output; max is 0x10FFFF as per RFC 4627"); 1266 | } 1267 | if (code >= SURR1_FIRST) { 1268 | if (code <= SURR1_LAST) { // Unmatched first part (closing without second part?) 1269 | throw new IllegalArgumentException("Unmatched first part of surrogate pair (0x"+Integer.toHexString(code)+")"); 1270 | } 1271 | throw new IllegalArgumentException("Unmatched second part of surrogate pair (0x"+Integer.toHexString(code)+")"); 1272 | } 1273 | // should we ever get this? 1274 | throw new IllegalArgumentException("Illegal character point (0x"+Integer.toHexString(code)+") to output"); 1275 | } 1276 | 1277 | /* 1278 | /********************************************************** 1279 | /* Internal methods, writing bytes 1280 | /********************************************************** 1281 | */ 1282 | 1283 | private final void _ensureRoomForOutput(int needed) throws IOException { 1284 | if ((_outputTail + needed) >= _outputEnd) { 1285 | _flushBuffer(); 1286 | } 1287 | } 1288 | 1289 | private final void _writeIntValue(int i) throws IOException 1290 | { 1291 | int marker; 1292 | if (i < 0) { 1293 | i += 1; 1294 | i = -1; 1295 | marker = PREFIX_TYPE_INT_NEG; 1296 | } else { 1297 | marker = PREFIX_TYPE_INT_POS; 1298 | } 1299 | _writeLengthMarker(marker, i); 1300 | } 1301 | 1302 | private final void _writeLongValue(long l) throws IOException 1303 | { 1304 | _ensureRoomForOutput(9); 1305 | if (l < 0) { 1306 | l += 1; 1307 | l = -1; 1308 | _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_NEG + 27); 1309 | } else { 1310 | _outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_POS + 27); 1311 | } 1312 | int i = (int) (l >> 32); 1313 | _outputBuffer[_outputTail++] = (byte) (i >> 24); 1314 | _outputBuffer[_outputTail++] = (byte) (i >> 16); 1315 | _outputBuffer[_outputTail++] = (byte) (i >> 8); 1316 | _outputBuffer[_outputTail++] = (byte) i; 1317 | i = (int) l; 1318 | _outputBuffer[_outputTail++] = (byte) (i >> 24); 1319 | _outputBuffer[_outputTail++] = (byte) (i >> 16); 1320 | _outputBuffer[_outputTail++] = (byte) (i >> 8); 1321 | _outputBuffer[_outputTail++] = (byte) i; 1322 | } 1323 | 1324 | private final void _writeLengthMarker(int majorType, int i) throws IOException 1325 | { 1326 | _ensureRoomForOutput(5); 1327 | if (i < 24) { 1328 | _outputBuffer[_outputTail++] = (byte) (majorType + i); 1329 | return; 1330 | } 1331 | if (i <= 0xFF) { 1332 | _outputBuffer[_outputTail++] = (byte) (majorType + 24); 1333 | _outputBuffer[_outputTail++] = (byte) i; 1334 | return; 1335 | } 1336 | final byte b0 = (byte) i; 1337 | i >>= 8; 1338 | if (i <= 0xFF) { 1339 | _outputBuffer[_outputTail++] = (byte) (majorType + 25); 1340 | _outputBuffer[_outputTail++] = (byte) i; 1341 | _outputBuffer[_outputTail++] = b0; 1342 | return; 1343 | } 1344 | _outputBuffer[_outputTail++] = (byte) (majorType + 26); 1345 | _outputBuffer[_outputTail++] = (byte) (i >> 16); 1346 | _outputBuffer[_outputTail++] = (byte) (i >> 8); 1347 | _outputBuffer[_outputTail++] = (byte) i; 1348 | _outputBuffer[_outputTail++] = b0; 1349 | } 1350 | 1351 | private final void _writeByte(byte b) throws IOException 1352 | { 1353 | if (_outputTail >= _outputEnd) { 1354 | _flushBuffer(); 1355 | } 1356 | _outputBuffer[_outputTail++] = b; 1357 | } 1358 | 1359 | /* 1360 | private final void _writeBytes(byte b1, byte b2) throws IOException 1361 | { 1362 | if ((_outputTail + 1) >= _outputEnd) { 1363 | _flushBuffer(); 1364 | } 1365 | _outputBuffer[_outputTail++] = b1; 1366 | _outputBuffer[_outputTail++] = b2; 1367 | } 1368 | */ 1369 | 1370 | private final void _writeBytes(byte[] data, int offset, int len) throws IOException 1371 | { 1372 | if (len == 0) { 1373 | return; 1374 | } 1375 | if ((_outputTail + len) >= _outputEnd) { 1376 | _writeBytesLong(data, offset, len); 1377 | return; 1378 | } 1379 | // common case, non-empty, fits in just fine: 1380 | System.arraycopy(data, offset, _outputBuffer, _outputTail, len); 1381 | _outputTail += len; 1382 | } 1383 | 1384 | private final int _writeBytes(InputStream in, int bytesLeft) throws IOException 1385 | { 1386 | while (bytesLeft > 0) { 1387 | int room = _outputEnd - _outputTail; 1388 | if (room <= 0) { 1389 | _flushBuffer(); 1390 | room = _outputEnd - _outputTail; 1391 | } 1392 | int count = in.read(_outputBuffer, _outputTail, room); 1393 | if (count < 0) { 1394 | break; 1395 | } 1396 | _outputTail += count; 1397 | bytesLeft -= count; 1398 | } 1399 | return bytesLeft; 1400 | } 1401 | 1402 | private final void _writeBytesLong(byte[] data, int offset, int len) throws IOException 1403 | { 1404 | if (_outputTail >= _outputEnd) { 1405 | _flushBuffer(); 1406 | } 1407 | while (true) { 1408 | int currLen = Math.min(len, (_outputEnd - _outputTail)); 1409 | System.arraycopy(data, offset, _outputBuffer, _outputTail, currLen); 1410 | _outputTail += currLen; 1411 | if ((len -= currLen) == 0) { 1412 | break; 1413 | } 1414 | offset += currLen; 1415 | _flushBuffer(); 1416 | } 1417 | } 1418 | 1419 | /* 1420 | /********************************************************** 1421 | /* Internal methods, buffer handling 1422 | /********************************************************** 1423 | */ 1424 | 1425 | @Override 1426 | protected void _releaseBuffers() 1427 | { 1428 | byte[] buf = _outputBuffer; 1429 | if (buf != null && _bufferRecyclable) { 1430 | _outputBuffer = null; 1431 | _ioContext.releaseWriteEncodingBuffer(buf); 1432 | } 1433 | char[] cbuf = _charBuffer; 1434 | if (cbuf != null) { 1435 | _charBuffer = null; 1436 | _ioContext.releaseConcatBuffer(cbuf); 1437 | } 1438 | } 1439 | 1440 | protected final void _flushBuffer() throws IOException 1441 | { 1442 | if (_outputTail > 0) { 1443 | _bytesWritten += _outputTail; 1444 | _out.write(_outputBuffer, 0, _outputTail); 1445 | _outputTail = 0; 1446 | } 1447 | } 1448 | 1449 | /* 1450 | /********************************************************** 1451 | /* Internal methods, error reporting 1452 | /********************************************************** 1453 | */ 1454 | 1455 | protected UnsupportedOperationException _notSupported() { 1456 | return new UnsupportedOperationException(); 1457 | } 1458 | } 1459 | -------------------------------------------------------------------------------- /src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORParserBootstrapper.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.dataformat.cbor; 2 | 3 | import java.io.*; 4 | 5 | import com.fasterxml.jackson.core.*; 6 | import com.fasterxml.jackson.core.format.InputAccessor; 7 | import com.fasterxml.jackson.core.format.MatchStrength; 8 | import com.fasterxml.jackson.core.io.IOContext; 9 | import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer; 10 | 11 | /** 12 | * Simple bootstrapper version used with CBOR format parser. 13 | */ 14 | public class CBORParserBootstrapper 15 | { 16 | /* 17 | /********************************************************** 18 | /* Configuration 19 | /********************************************************** 20 | */ 21 | 22 | protected final IOContext _context; 23 | protected final InputStream _in; 24 | 25 | /* 26 | /********************************************************** 27 | /* Input buffering 28 | /********************************************************** 29 | */ 30 | 31 | protected final byte[] _inputBuffer; 32 | protected int _inputPtr, _inputEnd; 33 | 34 | /** 35 | * Flag that indicates whether buffer above is to be recycled 36 | * after being used or not. 37 | */ 38 | protected final boolean _bufferRecyclable; 39 | 40 | /* 41 | /********************************************************** 42 | /* Input location 43 | /********************************************************** 44 | */ 45 | 46 | /** 47 | * Current number of input units (bytes or chars) that were processed in 48 | * previous blocks, 49 | * before contents of current input buffer. 50 | *

51 | * Note: includes possible BOMs, if those were part of the input. 52 | */ 53 | protected int _inputProcessed; 54 | 55 | /* 56 | /********************************************************** 57 | /* Life-cycle 58 | /********************************************************** 59 | */ 60 | 61 | public CBORParserBootstrapper(IOContext ctxt, InputStream in) 62 | { 63 | _context = ctxt; 64 | _in = in; 65 | _inputBuffer = ctxt.allocReadIOBuffer(); 66 | _inputEnd = _inputPtr = 0; 67 | _inputProcessed = 0; 68 | _bufferRecyclable = true; 69 | } 70 | 71 | public CBORParserBootstrapper(IOContext ctxt, byte[] inputBuffer, int inputStart, int inputLen) 72 | { 73 | _context = ctxt; 74 | _in = null; 75 | _inputBuffer = inputBuffer; 76 | _inputPtr = inputStart; 77 | _inputEnd = (inputStart + inputLen); 78 | // Need to offset this for correct location info 79 | _inputProcessed = -inputStart; 80 | _bufferRecyclable = false; 81 | } 82 | 83 | public CBORParser constructParser(int factoryFeatures, 84 | int generalParserFeatures, int formatFeatures, 85 | ObjectCodec codec, ByteQuadsCanonicalizer rootByteSymbols) 86 | throws IOException, JsonParseException 87 | { 88 | ByteQuadsCanonicalizer can = rootByteSymbols.makeChild(factoryFeatures); 89 | // We just need a single byte to recognize possible "empty" document. 90 | ensureLoaded(1); 91 | CBORParser p = new CBORParser(_context, generalParserFeatures, formatFeatures, 92 | codec, can, 93 | _in, _inputBuffer, _inputPtr, _inputEnd, _bufferRecyclable); 94 | if (_inputPtr < _inputEnd) { // only false for empty doc 95 | ; // anything we should verify? In future, could verify 96 | } else { 97 | /* 13-Jan-2014, tatu: Actually, let's allow empty documents even if 98 | * header signature would otherwise be needed. This is useful for 99 | * JAX-RS provider, empty PUT/POST payloads? 100 | */ 101 | ; 102 | } 103 | return p; 104 | } 105 | 106 | /* 107 | /********************************************************** 108 | /* Encoding detection for data format auto-detection 109 | /********************************************************** 110 | */ 111 | 112 | public static MatchStrength hasCBORFormat(InputAccessor acc) throws IOException 113 | { 114 | // Ok: ideally we start with the header -- if so, we are golden 115 | if (!acc.hasMoreBytes()) { 116 | return MatchStrength.INCONCLUSIVE; 117 | } 118 | // We always need at least two bytes to determine, so 119 | byte b = acc.nextByte(); 120 | 121 | /* 13-Jan-2014, tatu: Let's actually consider indefine-length Objects 122 | * as conclusive matches if empty, or start with a text key. 123 | */ 124 | if (b == CBORConstants.BYTE_OBJECT_INDEFINITE) { 125 | if (acc.hasMoreBytes()) { 126 | b = acc.nextByte(); 127 | if (b == CBORConstants.BYTE_BREAK) { 128 | return MatchStrength.SOLID_MATCH; 129 | } 130 | if (CBORConstants.hasMajorType(CBORConstants.MAJOR_TYPE_TEXT, b)) { 131 | return MatchStrength.SOLID_MATCH; 132 | } 133 | // other types; unlikely but can't exactly rule out 134 | return MatchStrength.INCONCLUSIVE; 135 | } 136 | } else if (b == CBORConstants.BYTE_ARRAY_INDEFINITE) { 137 | if (acc.hasMoreBytes()) { 138 | b = acc.nextByte(); 139 | if (b == CBORConstants.BYTE_BREAK) { 140 | return MatchStrength.SOLID_MATCH; 141 | } 142 | // all kinds of types are possible, so let's just acknowledge it as possible: 143 | return MatchStrength.WEAK_MATCH; 144 | } 145 | } else if (CBORConstants.hasMajorType(CBORConstants.MAJOR_TYPE_TAG, b)) { 146 | 147 | // Actually, specific "self-describe tag" is a very good indicator 148 | // (see [Issue#6] 149 | if (b == (byte) 0xD9) { 150 | if (acc.hasMoreBytes()) { 151 | b = acc.nextByte(); 152 | if (b == (byte) 0xD9) { 153 | if (acc.hasMoreBytes()) { 154 | b = acc.nextByte(); 155 | if (b == (byte) 0xF7) { 156 | return MatchStrength.FULL_MATCH; 157 | } 158 | } 159 | } 160 | } 161 | } 162 | // As to other tags, possible. May want to add other "well-known" (commonly 163 | // used ones for root value) tags for 'solid' match in future. 164 | return MatchStrength.WEAK_MATCH; 165 | 166 | // Other types; the only one where there's significant checking possibility 167 | // is in last, "misc" category 168 | } else if (CBORConstants.hasMajorType(CBORConstants.MAJOR_TYPE_MISC, b)) { 169 | if ((b == CBORConstants.BYTE_FALSE) 170 | || (b == CBORConstants.BYTE_TRUE) 171 | || (b == CBORConstants.BYTE_NULL)) { 172 | return MatchStrength.SOLID_MATCH; 173 | } 174 | return MatchStrength.NO_MATCH; 175 | } 176 | return MatchStrength.INCONCLUSIVE; 177 | } 178 | 179 | /* 180 | /********************************************************** 181 | /* Internal methods, raw input access 182 | /********************************************************** 183 | */ 184 | 185 | protected boolean ensureLoaded(int minimum) throws IOException 186 | { 187 | if (_in == null) { // block source; nothing more to load 188 | return false; 189 | } 190 | 191 | /* Let's assume here buffer has enough room -- this will always 192 | * be true for the limited used this method gets 193 | */ 194 | int gotten = (_inputEnd - _inputPtr); 195 | while (gotten < minimum) { 196 | int count = _in.read(_inputBuffer, _inputEnd, _inputBuffer.length - _inputEnd); 197 | if (count < 1) { 198 | return false; 199 | } 200 | _inputEnd += count; 201 | gotten += count; 202 | } 203 | return true; 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /src/main/java/com/fasterxml/jackson/dataformat/cbor/CBORReadContext.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.dataformat.cbor; 2 | 3 | import com.fasterxml.jackson.core.*; 4 | import com.fasterxml.jackson.core.io.CharTypes; 5 | import com.fasterxml.jackson.core.json.DupDetector; 6 | 7 | /** 8 | * Replacement of {@link com.fasterxml.jackson.core.json.JsonReadContext} 9 | * to support features needed by CBOR format. 10 | */ 11 | public final class CBORReadContext 12 | extends JsonStreamContext 13 | { 14 | /** 15 | * Parent context for this context; null for root context. 16 | */ 17 | protected final CBORReadContext _parent; 18 | 19 | // // // Optional duplicate detection 20 | 21 | protected final DupDetector _dups; 22 | 23 | /** 24 | * For fixed-size Arrays, Objects, this indicates expected number of entries. 25 | */ 26 | protected int _expEntryCount; 27 | 28 | // // // Location information (minus source reference) 29 | 30 | protected String _currentName; 31 | 32 | /* 33 | /********************************************************** 34 | /* Simple instance reuse slots 35 | /********************************************************** 36 | */ 37 | 38 | protected CBORReadContext _child = null; 39 | 40 | /* 41 | /********************************************************** 42 | /* Instance construction, reuse 43 | /********************************************************** 44 | */ 45 | 46 | public CBORReadContext(CBORReadContext parent, DupDetector dups, 47 | int type, int expEntryCount) 48 | { 49 | super(); 50 | _parent = parent; 51 | _dups = dups; 52 | _type = type; 53 | _expEntryCount = expEntryCount; 54 | _index = -1; 55 | } 56 | 57 | protected void reset(int type, int expEntryCount) 58 | { 59 | _type = type; 60 | _expEntryCount = expEntryCount; 61 | _index = -1; 62 | _currentName = null; 63 | if (_dups != null) { 64 | _dups.reset(); 65 | } 66 | } 67 | 68 | // // // Factory methods 69 | 70 | public static CBORReadContext createRootContext(DupDetector dups) { 71 | return new CBORReadContext(null, dups, TYPE_ROOT, -1); 72 | } 73 | 74 | public CBORReadContext createChildArrayContext(int expEntryCount) 75 | { 76 | CBORReadContext ctxt = _child; 77 | if (ctxt == null) { 78 | _child = ctxt = new CBORReadContext(this, 79 | (_dups == null) ? null : _dups.child(), 80 | TYPE_ARRAY, expEntryCount); 81 | } else { 82 | ctxt.reset(TYPE_ARRAY, expEntryCount); 83 | } 84 | return ctxt; 85 | } 86 | 87 | public CBORReadContext createChildObjectContext(int expEntryCount) 88 | { 89 | CBORReadContext ctxt = _child; 90 | if (ctxt == null) { 91 | _child = ctxt = new CBORReadContext(this, 92 | (_dups == null) ? null : _dups.child(), 93 | TYPE_OBJECT, expEntryCount); 94 | return ctxt; 95 | } 96 | ctxt.reset(TYPE_OBJECT, expEntryCount); 97 | return ctxt; 98 | } 99 | 100 | /* 101 | /********************************************************** 102 | /* Abstract method implementation 103 | /********************************************************** 104 | */ 105 | 106 | @Override 107 | public String getCurrentName() { return _currentName; } 108 | 109 | @Override 110 | public CBORReadContext getParent() { return _parent; } 111 | 112 | /* 113 | /********************************************************** 114 | /* Extended API 115 | /********************************************************** 116 | */ 117 | 118 | public boolean hasExpectedLength() { return (_expEntryCount >= 0); } 119 | public int getExpectedLength() { return _expEntryCount; } 120 | 121 | public boolean acceptsBreakMarker() { 122 | return (_expEntryCount < 0) && _type != TYPE_ROOT; 123 | } 124 | 125 | /** 126 | * Method called to see if a new value is expected for this 127 | * Array or Object. Checks against expected length, if one known, 128 | * updating count of current entries if limit not yet reached. 129 | */ 130 | public boolean expectMoreValues() { 131 | if (++_index == _expEntryCount) { 132 | return false; 133 | } 134 | return true; 135 | } 136 | 137 | /** 138 | * @return Location pointing to the point where the context 139 | * start marker was found 140 | */ 141 | public JsonLocation getStartLocation(Object srcRef) { 142 | // not much we can tell 143 | return new JsonLocation(srcRef, 1L, -1, -1); 144 | } 145 | 146 | /* 147 | /********************************************************** 148 | /* State changes 149 | /********************************************************** 150 | */ 151 | 152 | public void setCurrentName(String name) throws JsonProcessingException 153 | { 154 | _currentName = name; 155 | if (_dups != null) { 156 | _checkDup(_dups, name); 157 | } 158 | } 159 | 160 | private void _checkDup(DupDetector dd, String name) throws JsonProcessingException 161 | { 162 | if (dd.isDup(name)) { 163 | throw new JsonParseException("Duplicate field '"+name+"'", dd.findLocation()); 164 | } 165 | } 166 | 167 | /* 168 | /********************************************************** 169 | /* Overridden standard methods 170 | /********************************************************** 171 | */ 172 | 173 | /** 174 | * Overridden to provide developer readable "JsonPath" representation 175 | * of the context. 176 | */ 177 | @Override 178 | public String toString() 179 | { 180 | StringBuilder sb = new StringBuilder(64); 181 | switch (_type) { 182 | case TYPE_ROOT: 183 | sb.append("/"); 184 | break; 185 | case TYPE_ARRAY: 186 | sb.append('['); 187 | sb.append(getCurrentIndex()); 188 | sb.append(']'); 189 | break; 190 | case TYPE_OBJECT: 191 | sb.append('{'); 192 | if (_currentName != null) { 193 | sb.append('"'); 194 | CharTypes.appendQuoted(sb, _currentName); 195 | sb.append('"'); 196 | } else { 197 | sb.append('?'); 198 | } 199 | sb.append('}'); 200 | break; 201 | } 202 | return sb.toString(); 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/main/java/com/fasterxml/jackson/dataformat/cbor/PackageVersion.java.in: -------------------------------------------------------------------------------- 1 | package @package@; 2 | 3 | import com.fasterxml.jackson.core.Version; 4 | import com.fasterxml.jackson.core.Versioned; 5 | import com.fasterxml.jackson.core.util.VersionUtil; 6 | 7 | /** 8 | * Automatically generated from PackageVersion.java.in during 9 | * packageVersion-generate execution of maven-replacer-plugin in 10 | * pom.xml. 11 | */ 12 | public final class PackageVersion implements Versioned { 13 | public final static Version VERSION = VersionUtil.parseVersion( 14 | "@projectversion@", "@projectgroupid@", "@projectartifactid@"); 15 | 16 | @Override 17 | public Version version() { 18 | return VERSION; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/LICENSE: -------------------------------------------------------------------------------- 1 | This copy of Jackson JSON processor databind module is licensed under the 2 | Apache (Software) License, version 2.0 ("the License"). 3 | See the License for details about distribution rights, and the 4 | specific rights regarding derivate works. 5 | 6 | You may obtain a copy of the License at: 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/com.fasterxml.jackson.core.JsonFactory: -------------------------------------------------------------------------------- 1 | com.fasterxml.jackson.dataformat.cbor.CBORFactory 2 | -------------------------------------------------------------------------------- /src/test/java/com/fasterxml/jackson/dataformat/cbor/CBORTestBase.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.dataformat.cbor; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.util.Arrays; 7 | import java.util.Random; 8 | 9 | import org.junit.Assert; 10 | 11 | import com.fasterxml.jackson.core.*; 12 | import com.fasterxml.jackson.databind.ObjectMapper; 13 | 14 | abstract class CBORTestBase 15 | extends junit.framework.TestCase 16 | { 17 | 18 | /* 19 | /********************************************************** 20 | /* Factory methods 21 | /********************************************************** 22 | */ 23 | 24 | protected CBORParser cborParser(ByteArrayOutputStream bytes) throws IOException { 25 | return cborParser(bytes.toByteArray()); 26 | } 27 | 28 | protected CBORParser cborParser(byte[] input) throws IOException { 29 | return cborParser(cborFactory(), input); 30 | } 31 | 32 | protected CBORParser cborParser(InputStream in) throws IOException { 33 | CBORFactory f = cborFactory(); 34 | return cborParser(f, in); 35 | } 36 | 37 | protected CBORParser cborParser(CBORFactory f, byte[] input) throws IOException { 38 | return f.createParser(input); 39 | } 40 | 41 | protected CBORParser cborParser(CBORFactory f, InputStream in) throws IOException { 42 | return f.createParser(in); 43 | } 44 | 45 | protected ObjectMapper cborMapper() { 46 | return new ObjectMapper(cborFactory()); 47 | } 48 | 49 | protected CBORFactory cborFactory() { 50 | CBORFactory f = new CBORFactory(); 51 | return f; 52 | } 53 | 54 | protected byte[] cborDoc(String json) throws IOException { 55 | return cborDoc(cborFactory(), json); 56 | } 57 | 58 | protected byte[] cborDoc(CBORFactory cborF, String json) throws IOException 59 | { 60 | JsonFactory jf = new JsonFactory(); 61 | JsonParser jp = jf.createParser(json); 62 | ByteArrayOutputStream out = new ByteArrayOutputStream(json.length()); 63 | JsonGenerator dest = cborF.createGenerator(out); 64 | 65 | while (jp.nextToken() != null) { 66 | dest.copyCurrentEvent(jp); 67 | } 68 | jp.close(); 69 | dest.close(); 70 | return out.toByteArray(); 71 | } 72 | 73 | protected CBORGenerator cborGenerator(ByteArrayOutputStream result) 74 | throws IOException 75 | { 76 | return cborGenerator(cborFactory(), result); 77 | } 78 | 79 | protected CBORGenerator cborGenerator(CBORFactory f, 80 | ByteArrayOutputStream result) 81 | throws IOException 82 | { 83 | return f.createGenerator(result, null); 84 | } 85 | 86 | /* 87 | /********************************************************** 88 | /* Additional assertion methods 89 | /********************************************************** 90 | */ 91 | 92 | protected void assertToken(JsonToken expToken, JsonToken actToken) 93 | { 94 | if (actToken != expToken) { 95 | fail("Expected token "+expToken+", current token "+actToken); 96 | } 97 | } 98 | 99 | protected void assertToken(JsonToken expToken, JsonParser jp) 100 | { 101 | assertToken(expToken, jp.getCurrentToken()); 102 | } 103 | 104 | protected void assertType(Object ob, Class expType) 105 | { 106 | if (ob == null) { 107 | fail("Expected an object of type "+expType.getName()+", got null"); 108 | } 109 | Class cls = ob.getClass(); 110 | if (!expType.isAssignableFrom(cls)) { 111 | fail("Expected type "+expType.getName()+", got "+cls.getName()); 112 | } 113 | } 114 | 115 | protected void verifyException(Throwable e, String... matches) 116 | { 117 | String msg = e.getMessage(); 118 | String lmsg = (msg == null) ? "" : msg.toLowerCase(); 119 | for (String match : matches) { 120 | String lmatch = match.toLowerCase(); 121 | if (lmsg.indexOf(lmatch) >= 0) { 122 | return; 123 | } 124 | } 125 | fail("Expected an exception with one of substrings ("+Arrays.asList(matches)+"): got one with message \""+msg+"\""); 126 | } 127 | 128 | protected void _verifyBytes(byte[] actBytes, byte... expBytes) { 129 | Assert.assertArrayEquals(expBytes, actBytes); 130 | } 131 | 132 | protected void _verifyBytes(byte[] actBytes, byte exp1, byte[] expRest) { 133 | byte[] expBytes = new byte[expRest.length+1]; 134 | System.arraycopy(expRest, 0, expBytes, 1, expRest.length); 135 | expBytes[0] = exp1; 136 | Assert.assertArrayEquals(expBytes, actBytes); 137 | } 138 | 139 | protected void _verifyBytes(byte[] actBytes, byte exp1, byte exp2, byte[] expRest) { 140 | byte[] expBytes = new byte[expRest.length+2]; 141 | System.arraycopy(expRest, 0, expBytes, 2, expRest.length); 142 | expBytes[0] = exp1; 143 | expBytes[1] = exp2; 144 | Assert.assertArrayEquals(expBytes, actBytes); 145 | } 146 | 147 | protected void _verifyBytes(byte[] actBytes, byte exp1, byte exp2, byte exp3, byte[] expRest) { 148 | byte[] expBytes = new byte[expRest.length+3]; 149 | System.arraycopy(expRest, 0, expBytes, 3, expRest.length); 150 | expBytes[0] = exp1; 151 | expBytes[1] = exp2; 152 | expBytes[2] = exp3; 153 | Assert.assertArrayEquals(expBytes, actBytes); 154 | } 155 | 156 | /** 157 | * Method that gets textual contents of the current token using 158 | * available methods, and ensures results are consistent, before 159 | * returning them 160 | */ 161 | protected String getAndVerifyText(JsonParser jp) throws IOException 162 | { 163 | // Ok, let's verify other accessors 164 | int actLen = jp.getTextLength(); 165 | char[] ch = jp.getTextCharacters(); 166 | String str2 = new String(ch, jp.getTextOffset(), actLen); 167 | String str = jp.getText(); 168 | 169 | if (str.length() != actLen) { 170 | fail("Internal problem (jp.token == "+jp.getCurrentToken()+"): jp.getText().length() ['"+str+"'] == "+str.length()+"; jp.getTextLength() == "+actLen); 171 | } 172 | assertEquals("String access via getText(), getTextXxx() must be the same", str, str2); 173 | 174 | return str; 175 | } 176 | 177 | /* 178 | /********************************************************** 179 | /* Text generation 180 | /********************************************************** 181 | */ 182 | 183 | protected static String generateUnicodeString(int length) { 184 | return generateUnicodeString(length, new Random(length)); 185 | } 186 | 187 | protected static String generateUnicodeString(int length, Random rnd) 188 | { 189 | StringBuilder sw = new StringBuilder(length+10); 190 | do { 191 | // First, add 7 ascii characters 192 | int num = 4 + (rnd.nextInt() & 7); 193 | while (--num >= 0) { 194 | sw.append((char) ('A' + num)); 195 | } 196 | // Then a unicode char of 2, 3 or 4 bytes long 197 | switch (rnd.nextInt() % 3) { 198 | case 0: 199 | sw.append((char) (256 + rnd.nextInt() & 511)); 200 | break; 201 | case 1: 202 | sw.append((char) (2048 + rnd.nextInt() & 4095)); 203 | break; 204 | default: 205 | sw.append((char) (65536 + rnd.nextInt() & 0x3FFF)); 206 | break; 207 | } 208 | } while (sw.length() < length); 209 | return sw.toString(); 210 | } 211 | 212 | protected static String generateAsciiString(int length) { 213 | return generateAsciiString(length, new Random(length)); 214 | } 215 | 216 | protected static String generateAsciiString(int length, Random rnd) 217 | { 218 | StringBuilder sw = new StringBuilder(length+10); 219 | do { 220 | // First, add 7 ascii characters 221 | int num = 4 + (rnd.nextInt() & 7); 222 | while (--num >= 0) { 223 | sw.append((char) ('A' + num)); 224 | } 225 | // and space 226 | sw.append(' '); 227 | } while (sw.length() < length); 228 | return sw.toString(); 229 | } 230 | 231 | /* 232 | /********************************************************** 233 | /* Other helper methods 234 | /********************************************************** 235 | */ 236 | 237 | protected static String aposToQuotes(String str) { 238 | return str.replace("'", "\""); 239 | } 240 | 241 | protected static String quote(String str) { 242 | return '"'+str+'"'; 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /src/test/java/com/fasterxml/jackson/dataformat/cbor/GeneratorBinaryTest.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.dataformat.cbor; 2 | 3 | import java.io.*; 4 | import java.security.NoSuchAlgorithmException; 5 | import java.security.SecureRandom; 6 | 7 | import org.junit.*; 8 | import org.junit.rules.TemporaryFolder; 9 | 10 | import com.fasterxml.jackson.core.JsonGenerator; 11 | 12 | public class GeneratorBinaryTest //extends CBORTestBase 13 | { 14 | final static int SMALL_LENGTH = 100; 15 | final static int LARGE_LENGTH = CBORGenerator.BYTE_BUFFER_FOR_OUTPUT + 500; 16 | 17 | @Rule 18 | public TemporaryFolder tempFolder = new TemporaryFolder(); 19 | 20 | private File binaryInputFile; 21 | private File cborFile; 22 | private File binaryOutputFile; 23 | 24 | @Before 25 | public void before() throws IOException 26 | { 27 | binaryInputFile = tempFolder.newFile("sourceData.bin"); 28 | cborFile = tempFolder.newFile("cbor.bin"); 29 | binaryOutputFile = tempFolder.newFile("outputData.bin"); 30 | } 31 | 32 | @Test 33 | public void testSmallByteArray() throws Exception 34 | { 35 | testEncodeAndDecodeBytes(SMALL_LENGTH); 36 | } 37 | 38 | @Test 39 | public void testLargeByteArray() throws Exception 40 | { 41 | testEncodeAndDecodeBytes(LARGE_LENGTH); 42 | } 43 | 44 | private void generateInputFile(File input, int fileSize) throws NoSuchAlgorithmException, IOException 45 | { 46 | OutputStream os = new BufferedOutputStream(new FileOutputStream(input)); 47 | SecureRandom sr = SecureRandom.getInstance("SHA1PRNG"); 48 | byte[] temp = new byte[1024]; 49 | int remaining = fileSize; 50 | while (remaining > 0) { 51 | sr.nextBytes(temp); 52 | os.write(temp, 0, Math.min(temp.length, remaining)); 53 | remaining -= temp.length; 54 | } 55 | os.close(); 56 | } 57 | 58 | private void testEncodeAndDecodeBytes(int length) throws NoSuchAlgorithmException, IOException 59 | { 60 | generateInputFile(binaryInputFile, length); 61 | encodeInCBOR(binaryInputFile, cborFile); 62 | decodeFromCborInFile(this.cborFile, this.binaryOutputFile); 63 | assertFileEquals(this.binaryInputFile, this.binaryOutputFile); 64 | } 65 | 66 | private void encodeInCBOR(File inputFile, File outputFile) throws NoSuchAlgorithmException, IOException 67 | { 68 | CBORFactory f = new CBORFactory(); 69 | OutputStream os = new BufferedOutputStream(new FileOutputStream(outputFile)); 70 | InputStream is = new BufferedInputStream(new FileInputStream(inputFile)); 71 | 72 | JsonGenerator gen = f.createGenerator(os); 73 | gen.writeBinary(is, (int) inputFile.length()); 74 | 75 | gen.close(); 76 | is.close(); 77 | os.close(); 78 | } 79 | 80 | private void decodeFromCborInFile(File input, File output) throws NoSuchAlgorithmException, IOException 81 | { 82 | CBORFactory f = new CBORFactory(); 83 | OutputStream os = new FileOutputStream(output); 84 | 85 | InputStream is = new FileInputStream(input); 86 | CBORParser parser = f.createParser(is); 87 | parser.nextToken(); 88 | parser.readBinaryValue(null, os); 89 | 90 | parser.close(); 91 | is.close(); 92 | os.close(); 93 | } 94 | 95 | private void assertFileEquals(File file1, File file2) throws IOException 96 | { 97 | FileInputStream fis1 = new FileInputStream(file1); 98 | FileInputStream fis2 = new FileInputStream(file2); 99 | 100 | Assert.assertEquals(file1.length(), file2.length()); 101 | 102 | int ch; 103 | 104 | while ((ch = fis1.read()) >= 0) { 105 | Assert.assertEquals(ch, fis2.read()); 106 | } 107 | fis1.close(); 108 | fis2.close(); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/test/java/com/fasterxml/jackson/dataformat/cbor/GeneratorInteropTest.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.dataformat.cbor; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | 5 | /** 6 | * Unit tests geared at testing issues that were raised due to 7 | * inter-operability with other CBOR codec implementations 8 | */ 9 | public class GeneratorInteropTest extends CBORTestBase 10 | { 11 | private final static byte[] TYPE_DESC_AND_TRUE = new byte[] { 12 | (byte) 0xD9, 13 | (byte) 0xD9, 14 | (byte) 0xF7, 15 | CBORConstants.BYTE_TRUE 16 | }; 17 | 18 | // Test for [Issue#6], for optional writing of CBOR Type Description Tag 19 | public void testTypeDescriptionTag() throws Exception 20 | { 21 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 22 | CBORGenerator gen = cborGenerator(out); 23 | // as per spec, Type Desc Tag has value 24 | gen.writeTag(CBORConstants.TAG_ID_SELF_DESCRIBE); 25 | gen.writeBoolean(true); 26 | gen.close(); 27 | 28 | _verifyBytes(out.toByteArray(), TYPE_DESC_AND_TRUE); 29 | } 30 | 31 | public void testAutoTypeDescription() throws Exception 32 | { 33 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 34 | CBORFactory f = cborFactory(); 35 | assertFalse(f.isEnabled(CBORGenerator.Feature.WRITE_TYPE_HEADER)); 36 | 37 | CBORGenerator gen = f.createGenerator(out); 38 | // First, without feature, we get just a single byte doc 39 | gen.writeBoolean(true); 40 | gen.close(); 41 | 42 | _verifyBytes(out.toByteArray(), new byte[] { 43 | CBORConstants.BYTE_TRUE 44 | }); 45 | 46 | f.enable(CBORGenerator.Feature.WRITE_TYPE_HEADER); 47 | // but with auto-write 48 | out = new ByteArrayOutputStream(); 49 | gen = f.createGenerator(out); 50 | // First, without feature, we get just a single byte doc 51 | gen.writeBoolean(true); 52 | gen.close(); 53 | 54 | 55 | 56 | _verifyBytes(out.toByteArray(), TYPE_DESC_AND_TRUE); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/test/java/com/fasterxml/jackson/dataformat/cbor/GeneratorLongStringTest.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.dataformat.cbor; 2 | 3 | import java.io.*; 4 | import java.util.*; 5 | 6 | import com.fasterxml.jackson.core.*; 7 | 8 | public class GeneratorLongStringTest extends CBORTestBase 9 | { 10 | final static int DOC_LEN = 2000000; // 2 meg test doc 11 | 12 | public void testLongWithMultiBytes() throws Exception 13 | { 14 | CBORFactory f = cborFactory(); 15 | ArrayList strings = new ArrayList(); 16 | Random rnd = new Random(123); 17 | 18 | ByteArrayOutputStream out = new ByteArrayOutputStream(DOC_LEN); 19 | JsonGenerator gen = f.createGenerator(out); 20 | gen.writeStartArray(); 21 | 22 | // Let's create 1M doc, first using Strings 23 | while (out.size() < (DOC_LEN - 10000)) { 24 | String str = generateUnicodeString(5000, rnd); 25 | strings.add(str); 26 | gen.writeString(str); 27 | } 28 | gen.writeEndArray(); 29 | gen.close(); 30 | // Written ok; let's try parsing then 31 | _verifyStrings(f, out.toByteArray(), strings); 32 | 33 | // Then same with char[] 34 | out = new ByteArrayOutputStream(DOC_LEN); 35 | gen = f.createGenerator(out); 36 | gen.writeStartArray(); 37 | 38 | // Let's create 1M doc, first using Strings 39 | for (int i = 0, len = strings.size(); i < len; ++i) { 40 | char[] ch = strings.get(i).toCharArray(); 41 | gen.writeString(ch, 0, ch.length); 42 | } 43 | gen.writeEndArray(); 44 | gen.close(); 45 | _verifyStrings(f, out.toByteArray(), strings); 46 | } 47 | 48 | /* 49 | /********************************************************** 50 | /* Helper methods 51 | /********************************************************** 52 | */ 53 | 54 | private void _verifyStrings(JsonFactory f, byte[] input, List strings) 55 | throws IOException 56 | { 57 | /* 58 | JsonParser jp = f.createParser(input); 59 | assertToken(JsonToken.START_ARRAY, jp.nextToken()); 60 | for (int i = 0, len = strings.size(); i < len; ++i) { 61 | assertToken(JsonToken.VALUE_STRING, jp.nextToken()); 62 | assertEquals(strings.get(i), jp.getText()); 63 | } 64 | assertToken(JsonToken.END_ARRAY, jp.nextToken()); 65 | jp.close(); 66 | */ 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/test/java/com/fasterxml/jackson/dataformat/cbor/GeneratorSimpleTest.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.dataformat.cbor; 2 | 3 | import java.io.*; 4 | import java.math.BigDecimal; 5 | import java.util.*; 6 | 7 | import org.junit.Assert; 8 | 9 | import com.fasterxml.jackson.core.JsonGenerationException; 10 | import com.fasterxml.jackson.databind.ObjectMapper; 11 | 12 | public class GeneratorSimpleTest extends CBORTestBase 13 | { 14 | private final ObjectMapper MAPPER = cborMapper(); 15 | 16 | /** 17 | * Test for verifying handling of 'true', 'false' and 'null' literals 18 | */ 19 | public void testSimpleLiterals() throws Exception 20 | { 21 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 22 | CBORGenerator gen = cborGenerator(out); 23 | 24 | assertEquals(0, gen.getOutputBuffered()); 25 | gen.writeBoolean(true); 26 | assertEquals(1, gen.getOutputBuffered()); 27 | 28 | gen.close(); 29 | assertEquals(0, gen.getOutputBuffered()); 30 | _verifyBytes(out.toByteArray(), CBORConstants.BYTE_TRUE); 31 | 32 | out = new ByteArrayOutputStream(); 33 | gen = cborGenerator(out); 34 | gen.writeBoolean(false); 35 | gen.close(); 36 | _verifyBytes(out.toByteArray(), CBORConstants.BYTE_FALSE); 37 | 38 | out = new ByteArrayOutputStream(); 39 | gen = cborGenerator(out); 40 | gen.writeNull(); 41 | gen.close(); 42 | _verifyBytes(out.toByteArray(), CBORConstants.BYTE_NULL); 43 | } 44 | 45 | public void testMinimalIntValues() throws Exception 46 | { 47 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 48 | CBORGenerator gen = cborGenerator(out); 49 | assertTrue(gen.isEnabled(CBORGenerator.Feature.WRITE_MINIMAL_INTS)); 50 | gen.writeNumber(17); 51 | gen.close(); 52 | _verifyBytes(out.toByteArray(), 53 | (byte) (CBORConstants.PREFIX_TYPE_INT_POS + 17)); 54 | 55 | // then without minimal 56 | out = new ByteArrayOutputStream(); 57 | gen = cborGenerator(out); 58 | gen.disable(CBORGenerator.Feature.WRITE_MINIMAL_INTS); 59 | gen.writeNumber(17); 60 | gen.close(); 61 | _verifyBytes(out.toByteArray(), 62 | (byte) (CBORConstants.PREFIX_TYPE_INT_POS + 26), 63 | (byte) 0, (byte) 0, (byte) 0, (byte) 17); 64 | 65 | out = new ByteArrayOutputStream(); 66 | gen = cborGenerator(out); 67 | gen.writeNumber(Integer.MAX_VALUE); 68 | gen.close(); 69 | _verifyBytes(out.toByteArray(), 70 | (byte) (CBORConstants.PREFIX_TYPE_INT_POS + 26), 71 | (byte) 0x7F, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF); 72 | 73 | out = new ByteArrayOutputStream(); 74 | gen = cborGenerator(out); 75 | gen.writeNumber(Integer.MIN_VALUE); 76 | gen.close(); 77 | _verifyBytes(out.toByteArray(), 78 | (byte) (CBORConstants.PREFIX_TYPE_INT_NEG + 26), 79 | (byte) 0x7F, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF); 80 | } 81 | 82 | public void testIntValues() throws Exception 83 | { 84 | // first, single-byte 85 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 86 | CBORGenerator gen = cborGenerator(out); 87 | gen.writeNumber(13); 88 | gen.close(); 89 | _verifyBytes(out.toByteArray(), 90 | (byte) (CBORConstants.PREFIX_TYPE_INT_POS + 13)); 91 | 92 | out = new ByteArrayOutputStream(); 93 | gen = cborGenerator(out); 94 | gen.writeNumber(-13); 95 | gen.close(); 96 | _verifyBytes(out.toByteArray(), 97 | // note: since there is no "-0", number one less than it'd appear 98 | (byte) (CBORConstants.PREFIX_TYPE_INT_NEG + 12)); 99 | 100 | // then two byte 101 | out = new ByteArrayOutputStream(); 102 | gen = cborGenerator(out); 103 | gen.writeNumber(0xFF); 104 | gen.close(); 105 | _verifyBytes(out.toByteArray(), 106 | (byte) (CBORConstants.PREFIX_TYPE_INT_POS + 24), (byte) 0xFF); 107 | 108 | out = new ByteArrayOutputStream(); 109 | gen = cborGenerator(out); 110 | gen.writeNumber(-256); 111 | gen.close(); 112 | _verifyBytes(out.toByteArray(), 113 | // note: since there is no "-0", number one less than it'd appear 114 | (byte) (CBORConstants.PREFIX_TYPE_INT_NEG + 24), (byte) 0xFF); 115 | 116 | // and three byte 117 | out = new ByteArrayOutputStream(); 118 | gen = cborGenerator(out); 119 | gen.writeNumber(0xFEDC); 120 | gen.close(); 121 | _verifyBytes(out.toByteArray(), 122 | (byte) (CBORConstants.PREFIX_TYPE_INT_POS + 25), (byte) 0xFE, (byte) 0xDC); 123 | 124 | out = new ByteArrayOutputStream(); 125 | gen = cborGenerator(out); 126 | gen.writeNumber(-0xFFFE); 127 | gen.close(); 128 | _verifyBytes(out.toByteArray(), 129 | (byte) (CBORConstants.PREFIX_TYPE_INT_NEG + 25), (byte) 0xFF, (byte) 0xFD); 130 | 131 | out = new ByteArrayOutputStream(); 132 | gen = cborGenerator(out); 133 | gen.writeNumber(Integer.MAX_VALUE); 134 | gen.close(); 135 | _verifyBytes(out.toByteArray(), 136 | (byte) (CBORConstants.PREFIX_TYPE_INT_POS + 26), 137 | (byte) 0x7F, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF); 138 | 139 | out = new ByteArrayOutputStream(); 140 | gen = cborGenerator(out); 141 | gen.writeNumber(Integer.MIN_VALUE); 142 | gen.close(); 143 | _verifyBytes(out.toByteArray(), 144 | (byte) (CBORConstants.PREFIX_TYPE_INT_NEG + 26), 145 | (byte) 0x7F, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF); 146 | } 147 | 148 | public void testLongValues() throws Exception 149 | { 150 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 151 | CBORGenerator gen = cborGenerator(out); 152 | long l = -1L + Integer.MIN_VALUE; 153 | gen.writeNumber(l); 154 | gen.close(); 155 | byte[] b = out.toByteArray(); 156 | assertEquals((byte) (CBORConstants.PREFIX_TYPE_INT_NEG + 27), b[0]); 157 | assertEquals(9, b.length); 158 | // could test full contents, but for now this shall suffice 159 | assertEquals(0, b[1]); 160 | assertEquals(0, b[2]); 161 | assertEquals(0, b[3]); 162 | } 163 | 164 | public void testFloatValues() throws Exception 165 | { 166 | // first, 32-bit float 167 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 168 | CBORGenerator gen = cborGenerator(out); 169 | float f = 1.25f; 170 | gen.writeNumber(f); 171 | gen.close(); 172 | int raw = Float.floatToIntBits(f); 173 | _verifyBytes(out.toByteArray(), 174 | (byte) (CBORConstants.BYTE_FLOAT32), 175 | (byte) (raw >> 24), 176 | (byte) (raw >> 16), 177 | (byte) (raw >> 8), 178 | (byte) raw); 179 | 180 | // then 64-bit double 181 | out = new ByteArrayOutputStream(); 182 | gen = cborGenerator(out); 183 | double d = 0.75f; 184 | gen.writeNumber(d); 185 | gen.close(); 186 | long rawL = Double.doubleToLongBits(d); 187 | _verifyBytes(out.toByteArray(), 188 | (byte) (CBORConstants.BYTE_FLOAT64), 189 | (byte) (rawL >> 56), 190 | (byte) (rawL >> 48), 191 | (byte) (rawL >> 40), 192 | (byte) (rawL >> 32), 193 | (byte) (rawL >> 24), 194 | (byte) (rawL >> 16), 195 | (byte) (rawL >> 8), 196 | (byte) rawL); 197 | } 198 | 199 | public void testEmptyArray() throws Exception 200 | { 201 | // First: empty array (2 bytes) 202 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 203 | CBORGenerator gen = cborGenerator(out); 204 | gen.writeStartArray(); 205 | gen.writeEndArray(); 206 | gen.close(); 207 | _verifyBytes(out.toByteArray(), CBORConstants.BYTE_ARRAY_INDEFINITE, 208 | CBORConstants.BYTE_BREAK); 209 | } 210 | 211 | public void testEmptyObject() throws Exception 212 | { 213 | // First: empty array (2 bytes) 214 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 215 | CBORGenerator gen = cborGenerator(out); 216 | gen.writeStartObject(); 217 | gen.writeEndObject(); 218 | gen.close(); 219 | _verifyBytes(out.toByteArray(), CBORConstants.BYTE_OBJECT_INDEFINITE, 220 | CBORConstants.BYTE_BREAK); 221 | } 222 | 223 | public void testIntArray() throws Exception 224 | { 225 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 226 | CBORGenerator gen = cborGenerator(out); 227 | 228 | // currently will produce indefinite-length array 229 | gen.writeStartArray(); 230 | gen.writeNumber(1); 231 | gen.writeNumber(2); 232 | gen.writeNumber(3); 233 | gen.writeEndArray(); 234 | gen.close(); 235 | 236 | final byte[] EXP = new byte[] { 237 | CBORConstants.BYTE_ARRAY_INDEFINITE, 238 | (byte) (CBORConstants.PREFIX_TYPE_INT_POS + 1), 239 | (byte) (CBORConstants.PREFIX_TYPE_INT_POS + 2), 240 | (byte) (CBORConstants.PREFIX_TYPE_INT_POS + 3), 241 | CBORConstants.BYTE_BREAK 242 | }; 243 | 244 | _verifyBytes(out.toByteArray(), EXP); 245 | 246 | // Also, data-binding should produce identical 247 | byte[] b = MAPPER.writeValueAsBytes(new int[] { 1, 2, 3 }); 248 | _verifyBytes(b, EXP); 249 | } 250 | 251 | public void testTrivialObject() throws Exception 252 | { 253 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 254 | CBORGenerator gen = cborGenerator(out); 255 | 256 | // currently will produce indefinite-length Object 257 | gen.writeStartObject(); 258 | gen.writeNumberField("a", 1); 259 | gen.writeNumberField("b", 2); 260 | gen.writeEndObject(); 261 | gen.close(); 262 | 263 | final byte[] EXP = new byte[] { 264 | CBORConstants.BYTE_OBJECT_INDEFINITE, 265 | (byte) (CBORConstants.PREFIX_TYPE_TEXT + 1), 266 | (byte) 'a', 267 | (byte) (CBORConstants.PREFIX_TYPE_INT_POS + 1), 268 | (byte) (CBORConstants.PREFIX_TYPE_TEXT + 1), 269 | (byte) 'b', 270 | (byte) (CBORConstants.PREFIX_TYPE_INT_POS + 2), 271 | CBORConstants.BYTE_BREAK 272 | }; 273 | 274 | _verifyBytes(out.toByteArray(), EXP); 275 | Map map = new LinkedHashMap(); 276 | map.put("a", 1); 277 | map.put("b", 2); 278 | byte[] b = MAPPER.writeValueAsBytes(map); 279 | _verifyBytes(b, EXP); 280 | } 281 | 282 | public void testShortText() throws Exception 283 | { 284 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 285 | CBORGenerator gen = cborGenerator(out); 286 | gen.writeString(""); 287 | gen.close(); 288 | _verifyBytes(out.toByteArray(), CBORConstants.BYTE_EMPTY_STRING); 289 | 290 | out = new ByteArrayOutputStream(); 291 | gen = cborGenerator(out); 292 | gen.writeString("abc"); 293 | gen.close(); 294 | _verifyBytes(out.toByteArray(), (byte) (CBORConstants.PREFIX_TYPE_TEXT + 3), 295 | (byte) 'a', (byte) 'b', (byte) 'c'); 296 | } 297 | 298 | public void testLongerText() throws Exception 299 | { 300 | // First, something with 8-bit length 301 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 302 | CBORGenerator gen = cborGenerator(out); 303 | final String SHORT_ASCII = generateAsciiString(240); 304 | gen.writeString(SHORT_ASCII); 305 | gen.close(); 306 | byte[] b = SHORT_ASCII.getBytes("UTF-8"); 307 | int len = b.length; 308 | _verifyBytes(out.toByteArray(), 309 | (byte) (CBORConstants.PREFIX_TYPE_TEXT + 24), (byte) len, b); 310 | 311 | // and ditto with fuller Unicode 312 | out = new ByteArrayOutputStream(); 313 | gen = cborGenerator(out); 314 | final String SHORT_UNICODE = generateUnicodeString(160); 315 | gen.writeString(SHORT_UNICODE); 316 | gen.close(); 317 | b = SHORT_UNICODE.getBytes("UTF-8"); 318 | len = b.length; 319 | // just a sanity check; will break if generation changes 320 | assertEquals(196, len); 321 | _verifyBytes(out.toByteArray(), 322 | (byte) (CBORConstants.PREFIX_TYPE_TEXT + 24), (byte) len, b); 323 | 324 | // and then something bit more sizable 325 | out = new ByteArrayOutputStream(); 326 | gen = cborGenerator(out); 327 | final String MEDIUM_UNICODE = generateUnicodeString(800); 328 | gen.writeString(MEDIUM_UNICODE); 329 | gen.close(); 330 | b = MEDIUM_UNICODE.getBytes("UTF-8"); 331 | len = b.length; 332 | // just a sanity check; will break if generation changes 333 | assertEquals(926, len); 334 | _verifyBytes(out.toByteArray(), 335 | (byte) (CBORConstants.PREFIX_TYPE_TEXT + 25), 336 | (byte) (len>>8), (byte) len, 337 | b); 338 | } 339 | 340 | public void testInvalidWrites() throws Exception 341 | { 342 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 343 | CBORGenerator gen = cborGenerator(out); 344 | gen.writeStartObject(); 345 | // this should NOT succeed: 346 | try { 347 | gen.writeString("test"); 348 | fail("Should NOT allow write of anything but FIELD_NAME or END_OBJECT at this point"); 349 | } catch (JsonGenerationException e) { 350 | verifyException(e, "expecting field name"); 351 | } 352 | gen.close(); 353 | 354 | // and as per [dataformat-cbor#21] this also 355 | out = new ByteArrayOutputStream(); 356 | gen = cborGenerator(out); 357 | gen.writeStartArray(); 358 | gen.writeStartObject(); 359 | try { 360 | gen.writeString("BAR"); 361 | fail("Should NOT allow write of anything but FIELD_NAME or END_OBJECT at this point"); 362 | } catch (JsonGenerationException e) { 363 | verifyException(e, "expecting field name"); 364 | } 365 | gen.close(); 366 | } 367 | 368 | public void testCopyCurrentEventWithTag() throws Exception { 369 | final ByteArrayOutputStream sourceBytes = new ByteArrayOutputStream(); 370 | final CBORGenerator sourceGen = cborGenerator(sourceBytes); 371 | sourceGen.writeNumber(BigDecimal.ONE); 372 | sourceGen.close(); 373 | 374 | final ByteArrayOutputStream targetBytes = new ByteArrayOutputStream(); 375 | final CBORGenerator gen = cborGenerator(targetBytes); 376 | final CBORParser cborParser = cborParser(sourceBytes); 377 | while (cborParser.nextToken() != null) { 378 | gen.copyCurrentEvent(cborParser); 379 | } 380 | gen.close(); 381 | 382 | // copyCurrentEvent doesn't preserve fixed arrays, so we can't 383 | // compare with the source bytes. 384 | Assert.assertArrayEquals(new byte[] { 385 | CBORConstants.BYTE_TAG_BIGFLOAT, 386 | CBORConstants.BYTE_ARRAY_INDEFINITE, 387 | 0, 388 | 1, 389 | CBORConstants.BYTE_BREAK 390 | }, 391 | targetBytes.toByteArray()); 392 | } 393 | 394 | public void testCopyCurrentSturctureWithTag() throws Exception { 395 | final ByteArrayOutputStream sourceBytes = new ByteArrayOutputStream(); 396 | final CBORGenerator sourceGen = cborGenerator(sourceBytes); 397 | sourceGen.writeNumber(BigDecimal.ONE); 398 | sourceGen.close(); 399 | 400 | final ByteArrayOutputStream targetBytes = new ByteArrayOutputStream(); 401 | final CBORGenerator gen = cborGenerator(targetBytes); 402 | final CBORParser cborParser = cborParser(sourceBytes); 403 | cborParser.nextToken(); 404 | gen.copyCurrentStructure(cborParser); 405 | gen.close(); 406 | 407 | // copyCurrentEvent doesn't preserve fixed arrays, so we can't 408 | // compare with the source bytes. 409 | Assert.assertArrayEquals(new byte[] { 410 | CBORConstants.BYTE_TAG_BIGFLOAT, 411 | CBORConstants.BYTE_ARRAY_INDEFINITE, 412 | 0, 413 | 1, 414 | CBORConstants.BYTE_BREAK 415 | }, 416 | targetBytes.toByteArray()); 417 | } 418 | } 419 | -------------------------------------------------------------------------------- /src/test/java/com/fasterxml/jackson/dataformat/cbor/NumberBeanTest.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.dataformat.cbor; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.fasterxml.jackson.dataformat.cbor.CBORTestBase; 5 | 6 | public class NumberBeanTest extends CBORTestBase 7 | { 8 | static class IntsWrapper { 9 | public int[][] values; 10 | 11 | protected IntsWrapper() { } 12 | public IntsWrapper(int[][] v) { values = v; } 13 | } 14 | 15 | static class LongsWrapper { 16 | public long[][] values; 17 | 18 | protected LongsWrapper() { } 19 | public LongsWrapper(long[][] v) { values = v; } 20 | } 21 | 22 | static class DoublesWrapper { 23 | public double[][] values; 24 | 25 | protected DoublesWrapper() { } 26 | public DoublesWrapper(double[][] v) { values = v; } 27 | } 28 | 29 | /* 30 | /********************************************************** 31 | /* Test methods 32 | /********************************************************** 33 | */ 34 | 35 | private final ObjectMapper MAPPER = cborMapper(); 36 | 37 | public void testIntArrayRoundTrip() throws Exception 38 | { 39 | int[][] inputArray = new int[][]{ { -5, 3 } }; 40 | byte[] cbor = MAPPER.writeValueAsBytes(new IntsWrapper(inputArray)); 41 | IntsWrapper result = MAPPER.readValue(cbor, IntsWrapper.class); 42 | assertNotNull(result); 43 | assertNotNull(result.values); 44 | assertEquals(1, result.values.length); 45 | assertEquals(2, result.values[0].length); 46 | assertEquals(inputArray[0][0], result.values[0][0]); 47 | assertEquals(inputArray[0][1], result.values[0][1]); 48 | } 49 | 50 | public void testLongArrayRoundTrip() throws Exception 51 | { 52 | long[][] inputArray = new long[][]{ { 3L + Integer.MAX_VALUE, -3L + Integer.MIN_VALUE } }; 53 | byte[] cbor = MAPPER.writeValueAsBytes(new LongsWrapper(inputArray)); 54 | LongsWrapper result = MAPPER.readValue(cbor, LongsWrapper.class); 55 | assertNotNull(result); 56 | assertNotNull(result.values); 57 | assertEquals(1, result.values.length); 58 | assertEquals(2, result.values[0].length); 59 | assertEquals(inputArray[0][0], result.values[0][0]); 60 | assertEquals(inputArray[0][1], result.values[0][1]); 61 | } 62 | 63 | // for [dataformats-binary#31] 64 | public void testDoubleArrayRoundTrip() throws Exception 65 | { 66 | double[][] inputArray = new double[][]{ { 0.25, -1.5 } }; 67 | byte[] cbor = MAPPER.writeValueAsBytes(new DoublesWrapper(inputArray)); 68 | DoublesWrapper result = MAPPER.readValue(cbor, DoublesWrapper.class); 69 | assertNotNull(result); 70 | assertNotNull(result.values); 71 | assertEquals(1, result.values.length); 72 | assertEquals(2, result.values[0].length); 73 | assertEquals(inputArray[0][0], result.values[0][0]); 74 | assertEquals(inputArray[0][1], result.values[0][1]); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/test/java/com/fasterxml/jackson/dataformat/cbor/ParserBinaryTest.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.dataformat.cbor; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | 5 | import org.junit.Assert; 6 | 7 | import com.fasterxml.jackson.core.JsonParser; 8 | import com.fasterxml.jackson.core.JsonToken; 9 | import com.fasterxml.jackson.databind.ObjectMapper; 10 | 11 | public class ParserBinaryTest extends CBORTestBase 12 | { 13 | final static class Bytes { 14 | public byte[] bytes; 15 | 16 | public Bytes() { } 17 | public Bytes(byte[] b) { bytes = b; } 18 | } 19 | 20 | private final ObjectMapper MAPPER = cborMapper(); 21 | 22 | public void testSmallBinaryValues() throws Exception { 23 | _testBinary(0); 24 | _testBinary(1); 25 | _testBinary(20); 26 | _testBinary(100); 27 | } 28 | 29 | public void testMediumBinaryValues() throws Exception { 30 | _testBinary(500); 31 | _testBinary(1500); 32 | _testBinary(8900); 33 | } 34 | 35 | public void testLargeBinaryValues() throws Exception { 36 | _testBinary(99000); 37 | _testBinary(299000); 38 | _testBinary(740000); 39 | } 40 | 41 | public void _testBinary(int size) throws Exception 42 | { 43 | byte[] input = new byte[size]; 44 | for (int i = 0; i < input.length; ++i) { 45 | input[i] = (byte) i; 46 | } 47 | 48 | // First, read/write as individual value 49 | byte[] raw = MAPPER.writeValueAsBytes(input); 50 | byte[] b2 = MAPPER.readValue(raw, byte[].class); 51 | assertNotNull(b2); 52 | Assert.assertArrayEquals(input, b2); 53 | 54 | // then as POJO member 55 | raw = MAPPER.writeValueAsBytes(new Bytes(input)); 56 | Bytes bytes = MAPPER.readValue(raw, Bytes.class); 57 | assertNotNull(bytes); 58 | assertNotNull(bytes.bytes); 59 | Assert.assertArrayEquals(input, bytes.bytes); 60 | 61 | // and finally using incremental access method 62 | raw = MAPPER.writeValueAsBytes(input); 63 | JsonParser p = MAPPER.getFactory().createParser(raw); 64 | assertToken(JsonToken.VALUE_EMBEDDED_OBJECT, p.nextToken()); 65 | ByteArrayOutputStream bout = new ByteArrayOutputStream(input.length / 3); 66 | assertEquals(input.length, p.readBinaryValue(bout)); 67 | assertEquals(input.length, bout.size()); 68 | b2 = bout.toByteArray(); 69 | Assert.assertArrayEquals(input, b2); 70 | p.close(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/test/java/com/fasterxml/jackson/dataformat/cbor/ParserInputStreamTest.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.dataformat.cbor; 2 | 3 | import com.fasterxml.jackson.databind.JsonNode; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import org.junit.Test; 6 | 7 | import java.io.ByteArrayInputStream; 8 | import java.io.IOException; 9 | import java.io.SequenceInputStream; 10 | 11 | // for [dataformat-cbor#13] 12 | public class ParserInputStreamTest extends CBORTestBase 13 | { 14 | @Test 15 | public void testInpuStream() throws Exception { 16 | CBORFactory f = new CBORFactory(); 17 | ObjectMapper cborMapper = new ObjectMapper(new CBORFactory()); 18 | byte[] buffer = generateHugeCBOR(f); 19 | 20 | // split the buffer in two smaller buffer 21 | int len = 160; 22 | byte[] buf1 = new byte[len]; 23 | byte[] buf2 = new byte[buffer.length - len]; 24 | System.arraycopy(buffer, 0, buf1, 0, len); 25 | System.arraycopy(buffer, len, buf2, 0, buffer.length - len); 26 | 27 | // aggregate the two buffers via a SequenceInputStream 28 | ByteArrayInputStream in1 = new ByteArrayInputStream(buf1); 29 | ByteArrayInputStream in2 = new ByteArrayInputStream(buf2); 30 | SequenceInputStream inputStream = new SequenceInputStream(in1, in2); 31 | 32 | JsonNode jsonNode = cborMapper.readTree(inputStream); 33 | assertNotNull(jsonNode); 34 | } 35 | 36 | private byte[] generateHugeCBOR(CBORFactory f) throws IOException { 37 | String hugeJson = "{"; 38 | for (char c='a'; c <= 'z'; c++) { 39 | for (char cc='a'; cc <= 'z'; cc++) { 40 | hugeJson += "\"" + c + cc + "\":0,"; 41 | } 42 | for (int i = 0; i < 50; i++) { 43 | hugeJson += "\"" + c + i + "\":" + i + ","; 44 | } 45 | } 46 | hugeJson += "\"name\":123"; 47 | hugeJson += "}"; 48 | return cborDoc(f, hugeJson); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/test/java/com/fasterxml/jackson/dataformat/cbor/ParserInteropTest.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.dataformat.cbor; 2 | 3 | import com.fasterxml.jackson.core.*; 4 | import com.fasterxml.jackson.core.format.DataFormatDetector; 5 | import com.fasterxml.jackson.core.format.DataFormatMatcher; 6 | import com.fasterxml.jackson.core.format.MatchStrength; 7 | 8 | /** 9 | * Unit tests geared at testing issues that were raised due to 10 | * inter-operability with other CBOR codec implementations 11 | */ 12 | public class ParserInteropTest extends CBORTestBase 13 | { 14 | private final static byte[] SELF_DESC_PLUS_TRUE = new byte[] { 15 | (byte) 0xD9, 16 | (byte) 0xD9, 17 | (byte) 0xF7, 18 | CBORConstants.BYTE_TRUE 19 | }; 20 | 21 | // for [Issue#5]; Perl CBOR::XS module uses binary encoding for 22 | // Map/Object keys; presumably in UTF-8. 23 | public void testBinaryEncodedKeys() throws Exception 24 | { 25 | // from equivalent of '{"query":{} }' 26 | final byte[] INPUT = { (byte) 0xa1, 0x45, 0x71, 0x75, 0x65, 0x72, 0x79, (byte) 0xa0 }; 27 | JsonParser p = cborParser(INPUT); 28 | 29 | assertToken(JsonToken.START_OBJECT, p.nextToken()); 30 | assertToken(JsonToken.FIELD_NAME, p.nextToken()); 31 | assertEquals("query", p.getCurrentName()); 32 | assertToken(JsonToken.START_OBJECT, p.nextToken()); 33 | assertToken(JsonToken.END_OBJECT, p.nextToken()); 34 | assertToken(JsonToken.END_OBJECT, p.nextToken()); 35 | 36 | assertNull(p.nextToken()); 37 | p.close(); 38 | } 39 | 40 | // for [Issue#6]: should be fine to have self-desc tag in general 41 | public void testSelfDescribeTagRead() throws Exception 42 | { 43 | CBORParser p = cborParser(SELF_DESC_PLUS_TRUE); 44 | 45 | assertEquals(-1, p.getCurrentTag()); 46 | 47 | assertToken(JsonToken.VALUE_TRUE, p.nextToken()); 48 | assertEquals(CBORConstants.TAG_ID_SELF_DESCRIBE, p.getCurrentTag()); 49 | 50 | assertNull(p.nextToken()); 51 | assertEquals(-1, p.getCurrentTag()); 52 | 53 | p.close(); 54 | } 55 | 56 | // as per [Issue#6], self-describe great for format auto-detection 57 | public void testFormatDetection() throws Exception 58 | { 59 | CBORFactory f = cborFactory(); 60 | // let's try to confuse auto-detector with JSON one too... 61 | DataFormatDetector det = new DataFormatDetector(new JsonFactory(), f); 62 | det = det.withMinimalMatch(MatchStrength.WEAK_MATCH).withOptimalMatch(MatchStrength.SOLID_MATCH); 63 | 64 | DataFormatMatcher match = det.findFormat(SELF_DESC_PLUS_TRUE); 65 | JsonFactory result = match.getMatch(); 66 | assertNotNull(result); 67 | assertEquals("CBOR", match.getMatchedFormatName()); 68 | assertEquals(MatchStrength.FULL_MATCH, match.getMatchStrength()); 69 | 70 | 71 | // but there are other ok matches too 72 | match = det.findFormat(cborDoc(f, "{\"field\" :\"value\"}")); 73 | result = match.getMatch(); 74 | assertNotNull(result); 75 | assertEquals("CBOR", match.getMatchedFormatName()); 76 | assertEquals(MatchStrength.SOLID_MATCH, match.getMatchStrength()); 77 | 78 | match = det.findFormat(cborDoc(f, "true")); 79 | result = match.getMatch(); 80 | assertNotNull(result); 81 | assertEquals("CBOR", match.getMatchedFormatName()); 82 | assertEquals(MatchStrength.SOLID_MATCH, match.getMatchStrength()); 83 | 84 | // and others so-so 85 | match = det.findFormat(cborDoc(f, "[ 1, 2, 3 ]")); 86 | result = match.getMatch(); 87 | assertNotNull(result); 88 | assertEquals("CBOR", match.getMatchedFormatName()); 89 | assertEquals(MatchStrength.WEAK_MATCH, match.getMatchStrength()); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/test/java/com/fasterxml/jackson/dataformat/cbor/ParserNextXxxTest.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.dataformat.cbor; 2 | 3 | import java.io.*; 4 | import java.util.Random; 5 | 6 | import com.fasterxml.jackson.core.*; 7 | import com.fasterxml.jackson.core.io.SerializedString; 8 | 9 | // note: copied from test of same name from jackson-dataformat-smile 10 | public class ParserNextXxxTest extends CBORTestBase 11 | { 12 | public void testIsNextTokenName() throws Exception 13 | { 14 | _testIsNextTokenName1(); 15 | _testIsNextTokenName2(); 16 | } 17 | 18 | public void testIssue34() throws Exception 19 | { 20 | final int TESTROUNDS = 223; 21 | 22 | final CBORFactory f = new CBORFactory(); 23 | 24 | // build the big document to trigger issue 25 | ByteArrayOutputStream bytes = new ByteArrayOutputStream(2000); 26 | JsonGenerator g = f.createGenerator(bytes); 27 | for (int i = 0; i < TESTROUNDS; ++i) { 28 | g.writeStartObject(); 29 | g.writeNumberField("fieldName", 1); 30 | g.writeEndObject(); 31 | } 32 | g.close(); 33 | final byte[] DOC = bytes.toByteArray(); 34 | 35 | SerializableString fieldName = new SerializedString("fieldName"); 36 | JsonParser parser = f.createParser(DOC); 37 | 38 | for (int i = 0; i < TESTROUNDS - 1; i++) { 39 | assertEquals(JsonToken.START_OBJECT, parser.nextToken()); 40 | 41 | // These will succeed 42 | assertTrue(parser.nextFieldName(fieldName)); 43 | 44 | parser.nextLongValue(-1); 45 | assertEquals(JsonToken.END_OBJECT, parser.nextToken()); 46 | } 47 | 48 | assertEquals(JsonToken.START_OBJECT, parser.nextToken()); 49 | 50 | // This will fail 51 | assertTrue(parser.nextFieldName(fieldName)); 52 | parser.close(); 53 | } 54 | 55 | public void testIssue38() throws Exception 56 | { 57 | final CBORFactory f = new CBORFactory(); 58 | byte[] DOC = cborDoc(f, "{\"field\" :\"value\"}"); 59 | 60 | SerializableString fieldName = new SerializedString("field"); 61 | JsonParser parser = f.createParser(DOC); 62 | assertEquals(JsonToken.START_OBJECT, parser.nextToken()); 63 | assertTrue(parser.nextFieldName(fieldName)); 64 | assertEquals(JsonToken.VALUE_STRING, parser.nextToken()); 65 | assertEquals("value", parser.getText()); 66 | assertEquals(JsonToken.END_OBJECT, parser.nextToken()); 67 | assertNull(parser.nextToken()); 68 | parser.close(); 69 | } 70 | 71 | public void testNextNameWithLongContent() throws Exception 72 | { 73 | final CBORFactory f = new CBORFactory(); 74 | 75 | // do 3 meg thingy 76 | final int SIZE = 3 * 1024 * 1024; 77 | ByteArrayOutputStream bytes = new ByteArrayOutputStream(SIZE + 20); 78 | 79 | JsonGenerator g = f.createGenerator(bytes); 80 | 81 | g.writeStartObject(); 82 | Random rnd = new Random(1); 83 | int count = 0; 84 | 85 | while (bytes.size() < SIZE) { 86 | ++count; 87 | int val = rnd.nextInt(); 88 | g.writeFieldName("f"+val); 89 | g.writeNumber(val % 1000); 90 | } 91 | g.writeEndObject(); 92 | g.close(); 93 | final byte[] DOC = bytes.toByteArray(); 94 | 95 | JsonParser parser = f.createParser(DOC); 96 | assertToken(JsonToken.START_OBJECT, parser.nextToken()); 97 | rnd = new Random(1); 98 | for (int i = 0; i < count; ++i) { 99 | int exp = rnd.nextInt(); 100 | SerializableString expName = new SerializedString("f"+exp); 101 | assertTrue(parser.nextFieldName(expName)); 102 | assertToken(JsonToken.VALUE_NUMBER_INT, parser.nextToken()); 103 | assertEquals(exp % 1000, parser.getIntValue()); 104 | } 105 | assertToken(JsonToken.END_OBJECT, parser.nextToken()); 106 | parser.close(); 107 | } 108 | 109 | /* 110 | /******************************************************** 111 | /* Actual test code 112 | /******************************************************** 113 | */ 114 | 115 | private void _testIsNextTokenName1() throws Exception 116 | { 117 | CBORFactory f = new CBORFactory(); 118 | final byte[] DOC = cborDoc(f, "{\"name\":123,\"name2\":14,\"x\":\"name\"}"); 119 | JsonParser jp = f.createParser(DOC); 120 | final SerializedString NAME = new SerializedString("name"); 121 | assertFalse(jp.nextFieldName(NAME)); 122 | assertToken(JsonToken.START_OBJECT, jp.getCurrentToken()); 123 | assertTrue(jp.nextFieldName(NAME)); 124 | assertToken(JsonToken.FIELD_NAME, jp.getCurrentToken()); 125 | assertEquals(NAME.getValue(), jp.getCurrentName()); 126 | assertEquals(NAME.getValue(), jp.getText()); 127 | assertFalse(jp.nextFieldName(NAME)); 128 | assertToken(JsonToken.VALUE_NUMBER_INT, jp.getCurrentToken()); 129 | assertEquals(123, jp.getIntValue()); 130 | 131 | assertFalse(jp.nextFieldName(NAME)); 132 | assertToken(JsonToken.FIELD_NAME, jp.getCurrentToken()); 133 | assertEquals("name2", jp.getCurrentName()); 134 | assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); 135 | 136 | assertFalse(jp.nextFieldName(NAME)); 137 | assertToken(JsonToken.FIELD_NAME, jp.getCurrentToken()); 138 | assertEquals("x", jp.getCurrentName()); 139 | 140 | assertFalse(jp.nextFieldName(NAME)); 141 | assertToken(JsonToken.VALUE_STRING, jp.getCurrentToken()); 142 | 143 | assertFalse(jp.nextFieldName(NAME)); 144 | assertToken(JsonToken.END_OBJECT, jp.getCurrentToken()); 145 | 146 | assertFalse(jp.nextFieldName(NAME)); 147 | assertNull(jp.getCurrentToken()); 148 | 149 | jp.close(); 150 | 151 | // Actually, try again with slightly different sequence... 152 | jp = f.createParser(DOC); 153 | assertToken(JsonToken.START_OBJECT, jp.nextToken()); 154 | assertFalse(jp.nextFieldName(new SerializedString("Nam"))); 155 | assertToken(JsonToken.FIELD_NAME, jp.getCurrentToken()); 156 | assertEquals(NAME.getValue(), jp.getCurrentName()); 157 | assertEquals(NAME.getValue(), jp.getText()); 158 | assertFalse(jp.nextFieldName(NAME)); 159 | assertToken(JsonToken.VALUE_NUMBER_INT, jp.getCurrentToken()); 160 | assertEquals(123, jp.getIntValue()); 161 | 162 | assertFalse(jp.nextFieldName(NAME)); 163 | assertToken(JsonToken.FIELD_NAME, jp.getCurrentToken()); 164 | assertEquals("name2", jp.getCurrentName()); 165 | assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); 166 | 167 | assertFalse(jp.nextFieldName(NAME)); 168 | assertToken(JsonToken.FIELD_NAME, jp.getCurrentToken()); 169 | assertEquals("x", jp.getCurrentName()); 170 | 171 | assertFalse(jp.nextFieldName(NAME)); 172 | assertToken(JsonToken.VALUE_STRING, jp.getCurrentToken()); 173 | 174 | assertFalse(jp.nextFieldName(NAME)); 175 | assertToken(JsonToken.END_OBJECT, jp.getCurrentToken()); 176 | 177 | assertFalse(jp.nextFieldName(NAME)); 178 | assertNull(jp.getCurrentToken()); 179 | 180 | jp.close(); 181 | } 182 | 183 | private void _testIsNextTokenName2() throws Exception 184 | { 185 | CBORFactory f = new CBORFactory(); 186 | final byte[] DOC = cborDoc(f, "{\"name\":123,\"name2\":14,\"x\":\"name\"}"); 187 | JsonParser jp = f.createParser(DOC); 188 | SerializableString NAME = new SerializedString("name"); 189 | assertFalse(jp.nextFieldName(NAME)); 190 | assertToken(JsonToken.START_OBJECT, jp.getCurrentToken()); 191 | assertTrue(jp.nextFieldName(NAME)); 192 | assertToken(JsonToken.FIELD_NAME, jp.getCurrentToken()); 193 | assertEquals(NAME.getValue(), jp.getCurrentName()); 194 | assertEquals(NAME.getValue(), jp.getText()); 195 | assertFalse(jp.nextFieldName(NAME)); 196 | assertToken(JsonToken.VALUE_NUMBER_INT, jp.getCurrentToken()); 197 | assertEquals(123, jp.getIntValue()); 198 | 199 | assertFalse(jp.nextFieldName(NAME)); 200 | assertToken(JsonToken.FIELD_NAME, jp.getCurrentToken()); 201 | assertEquals("name2", jp.getCurrentName()); 202 | assertToken(JsonToken.VALUE_NUMBER_INT, jp.nextToken()); 203 | 204 | assertFalse(jp.nextFieldName(NAME)); 205 | assertToken(JsonToken.FIELD_NAME, jp.getCurrentToken()); 206 | assertEquals("x", jp.getCurrentName()); 207 | 208 | assertFalse(jp.nextFieldName(NAME)); 209 | assertToken(JsonToken.VALUE_STRING, jp.getCurrentToken()); 210 | 211 | assertFalse(jp.nextFieldName(NAME)); 212 | assertToken(JsonToken.END_OBJECT, jp.getCurrentToken()); 213 | 214 | assertFalse(jp.nextFieldName(NAME)); 215 | assertNull(jp.getCurrentToken()); 216 | 217 | jp.close(); 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /src/test/java/com/fasterxml/jackson/dataformat/cbor/ParserSimpleTest.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.dataformat.cbor; 2 | 3 | import java.io.*; 4 | import java.util.LinkedHashMap; 5 | import java.util.Map; 6 | 7 | import com.fasterxml.jackson.core.*; 8 | import com.fasterxml.jackson.core.JsonParser.NumberType; 9 | import com.fasterxml.jackson.databind.ObjectMapper; 10 | 11 | /** 12 | * Unit tests for simple value types. 13 | */ 14 | public class ParserSimpleTest extends CBORTestBase 15 | { 16 | private final ObjectMapper MAPPER = cborMapper(); 17 | 18 | /** 19 | * Test for verifying handling of 'true', 'false' and 'null' literals 20 | */ 21 | public void testSimpleLiterals() throws Exception 22 | { 23 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 24 | JsonGenerator gen = cborGenerator(out); 25 | gen.writeBoolean(true); 26 | gen.close(); 27 | JsonParser p = cborParser(out); 28 | assertEquals(JsonToken.VALUE_TRUE, p.nextToken()); 29 | assertNull(p.nextToken()); 30 | p.close(); 31 | 32 | out = new ByteArrayOutputStream(); 33 | gen = cborGenerator(out); 34 | gen.writeBoolean(false); 35 | gen.close(); 36 | p = cborParser(out); 37 | assertEquals(JsonToken.VALUE_FALSE, p.nextToken()); 38 | assertNull(p.nextToken()); 39 | p.close(); 40 | 41 | out = new ByteArrayOutputStream(); 42 | gen = cborGenerator(out); 43 | gen.writeNull(); 44 | gen.close(); 45 | p = cborParser(out); 46 | assertEquals(JsonToken.VALUE_NULL, p.nextToken()); 47 | assertNull(p.nextToken()); 48 | p.close(); 49 | } 50 | 51 | public void testIntValues() throws Exception 52 | { 53 | // first, single-byte 54 | CBORFactory f = cborFactory(); 55 | // single byte 56 | _verifyInt(f, 13); 57 | _verifyInt(f, -19); 58 | // two bytes 59 | _verifyInt(f, 255); 60 | _verifyInt(f, -127); 61 | // three 62 | _verifyInt(f, 256); 63 | _verifyInt(f, 0xFFFF); 64 | _verifyInt(f, -300); 65 | _verifyInt(f, -0xFFFF); 66 | // and all 4 bytes 67 | _verifyInt(f, 0x7FFFFFFF); 68 | _verifyInt(f, 0x70000000 << 1); 69 | } 70 | 71 | private void _verifyInt(CBORFactory f, int value) throws Exception 72 | { 73 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 74 | JsonGenerator gen = cborGenerator(f, out); 75 | gen.writeNumber(value); 76 | gen.close(); 77 | JsonParser p = cborParser(f, out.toByteArray()); 78 | assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); 79 | assertEquals(NumberType.INT, p.getNumberType()); 80 | assertEquals(value, p.getIntValue()); 81 | assertEquals((double) value, p.getDoubleValue()); 82 | assertNull(p.nextToken()); 83 | p.close(); 84 | } 85 | 86 | public void testLongValues() throws Exception 87 | { 88 | CBORFactory f = cborFactory(); 89 | _verifyLong(f, 1L + Integer.MAX_VALUE); 90 | _verifyLong(f, -1L + Integer.MIN_VALUE); 91 | } 92 | 93 | private void _verifyLong(CBORFactory f, long value) throws Exception 94 | { 95 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 96 | JsonGenerator gen = cborGenerator(f, out); 97 | gen.writeNumber(value); 98 | gen.close(); 99 | JsonParser p = cborParser(f, out.toByteArray()); 100 | assertEquals(JsonToken.VALUE_NUMBER_INT, p.nextToken()); 101 | assertEquals(value, p.getLongValue()); 102 | assertEquals(NumberType.LONG, p.getNumberType()); 103 | assertEquals((double) value, p.getDoubleValue()); 104 | assertNull(p.nextToken()); 105 | p.close(); 106 | } 107 | 108 | public void testFloatValues() throws Exception 109 | { 110 | // first, single-byte 111 | CBORFactory f = cborFactory(); 112 | // single byte 113 | _verifyFloat(f, 0.25); 114 | _verifyFloat(f, 20.5); 115 | 116 | // But then, oddity: 16-bit mini-float 117 | // Examples from [https://en.wikipedia.org/wiki/Half_precision_floating-point_format] 118 | _verifyHalfFloat(f, 0, 0.0); 119 | _verifyHalfFloat(f, 0x3C00, 1.0); 120 | _verifyHalfFloat(f, 0xC000, -2.0); 121 | _verifyHalfFloat(f, 0x7BFF, 65504.0); 122 | _verifyHalfFloat(f, 0x7C00, Double.POSITIVE_INFINITY); 123 | _verifyHalfFloat(f, 0xFC00, Double.NEGATIVE_INFINITY); 124 | 125 | // ... can add more, but need bit looser comparison if so 126 | } 127 | 128 | private void _verifyFloat(CBORFactory f, double value) throws Exception 129 | { 130 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 131 | JsonGenerator gen = cborGenerator(f, out); 132 | gen.writeNumber((float) value); 133 | gen.close(); 134 | JsonParser p = cborParser(f, out.toByteArray()); 135 | assertEquals(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); 136 | if (NumberType.FLOAT != p.getNumberType()) { 137 | fail("Expected `NumberType.FLOAT`, got "+p.getNumberType()+": "+p.getText()); 138 | } 139 | assertEquals(value, p.getDoubleValue()); 140 | assertNull(p.nextToken()); 141 | p.close(); 142 | } 143 | 144 | private void _verifyHalfFloat(JsonFactory f, int i16, double value) throws IOException 145 | { 146 | JsonParser p = f.createParser(new byte[] { 147 | (byte) (CBORConstants.PREFIX_TYPE_MISC + 25), 148 | (byte) (i16 >> 8), (byte) i16 149 | }); 150 | assertEquals(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); 151 | assertEquals(NumberType.FLOAT, p.getNumberType()); 152 | assertEquals(value, p.getDoubleValue()); 153 | assertNull(p.nextToken()); 154 | p.close(); 155 | } 156 | 157 | public void testSimpleArray() throws Exception 158 | { 159 | byte[] b = MAPPER.writeValueAsBytes(new int[] { 1, 2, 3, 4}); 160 | int[] output = MAPPER.readValue(b, int[].class); 161 | assertEquals(4, output.length); 162 | for (int i = 1; i <= output.length; ++i) { 163 | assertEquals(i, output[i-1]); 164 | } 165 | } 166 | 167 | public void testSimpleObject() throws Exception 168 | { 169 | Map input = new LinkedHashMap(); 170 | input.put("a", 1); 171 | input.put("bar", "foo"); 172 | final String NON_ASCII_NAME = "Y\\u00F6"; 173 | input.put(NON_ASCII_NAME, -3.25); 174 | input.put("", ""); 175 | byte[] b = MAPPER.writeValueAsBytes(input); 176 | 177 | // First, using streaming API 178 | JsonParser p = cborParser(b); 179 | assertToken(JsonToken.START_OBJECT, p.nextToken()); 180 | 181 | assertToken(JsonToken.FIELD_NAME, p.nextToken()); 182 | assertEquals("a", p.getCurrentName()); 183 | assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken()); 184 | assertEquals(1, p.getIntValue()); 185 | 186 | assertToken(JsonToken.FIELD_NAME, p.nextToken()); 187 | assertEquals("bar", p.getCurrentName()); 188 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); 189 | assertEquals("foo", p.getText()); 190 | 191 | assertToken(JsonToken.FIELD_NAME, p.nextToken()); 192 | assertEquals(NON_ASCII_NAME, p.getCurrentName()); 193 | assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); 194 | assertEquals(-3.25, p.getDoubleValue()); 195 | 196 | assertToken(JsonToken.FIELD_NAME, p.nextToken()); 197 | assertEquals("", p.getCurrentName()); 198 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); 199 | assertEquals("", p.getText()); 200 | 201 | assertToken(JsonToken.END_OBJECT, p.nextToken()); 202 | 203 | p.close(); 204 | 205 | Map output = MAPPER.readValue(b, Map.class); 206 | assertEquals(4, output.size()); 207 | assertEquals(Integer.valueOf(1), output.get("a")); 208 | assertEquals("foo", output.get("bar")); 209 | assertEquals(Double.valueOf(-3.25), output.get(NON_ASCII_NAME)); 210 | assertEquals("", output.get("")); 211 | } 212 | 213 | public void testMediumText() throws Exception 214 | { 215 | _testMedium(1100); 216 | _testMedium(1300); 217 | _testMedium(1900); 218 | _testMedium(2300); 219 | _testMedium(3900); 220 | } 221 | 222 | private void _testMedium(int len) throws Exception 223 | { 224 | // First, use size that should fit in output buffer, but 225 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 226 | CBORGenerator gen = cborGenerator(out); 227 | final String MEDIUM = generateUnicodeString(len); 228 | gen.writeString(MEDIUM); 229 | gen.close(); 230 | 231 | final byte[] b = out.toByteArray(); 232 | 233 | // verify that it is indeed non-chunked still... 234 | assertEquals((byte) (CBORConstants.PREFIX_TYPE_TEXT + 25), b[0]); 235 | 236 | JsonParser p = cborParser(b); 237 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); 238 | assertEquals(MEDIUM, p.getText()); 239 | assertNull(p.nextToken()); 240 | p.close(); 241 | } 242 | 243 | public void testCurrentLocationByteOffset() throws Exception { 244 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 245 | CBORGenerator gen = cborGenerator(out); 246 | gen.writeString("1234567890"); 247 | gen.writeString("1234567890"); 248 | gen.close(); 249 | 250 | final byte[] b = out.toByteArray(); 251 | 252 | JsonParser p = cborParser(b); 253 | 254 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); 255 | assertEquals(1, p.getCurrentLocation().getByteOffset()); 256 | p.getText(); // fully read token. 257 | assertEquals(11, p.getCurrentLocation().getByteOffset()); 258 | 259 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); 260 | assertEquals(12, p.getCurrentLocation().getByteOffset()); 261 | p.getText(); 262 | assertEquals(22, p.getCurrentLocation().getByteOffset()); 263 | 264 | assertNull(p.nextToken()); 265 | assertEquals(22, p.getCurrentLocation().getByteOffset()); 266 | 267 | p.close(); 268 | assertEquals(22, p.getCurrentLocation().getByteOffset()); 269 | } 270 | 271 | public void testLongNonChunkedText() throws Exception 272 | { 273 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 274 | 275 | final String LONG = generateUnicodeString(37000); 276 | final byte[] LONG_B = LONG.getBytes("UTF-8"); 277 | final int BYTE_LEN = LONG_B.length; 278 | out.write(CBORConstants.BYTE_ARRAY_INDEFINITE); 279 | out.write((byte) (CBORConstants.PREFIX_TYPE_TEXT + 25)); 280 | out.write((byte) (BYTE_LEN >> 8)); 281 | out.write((byte) BYTE_LEN); 282 | out.write(LONG.getBytes("UTF-8")); 283 | out.write(CBORConstants.BYTE_BREAK); 284 | 285 | final byte[] b = out.toByteArray(); 286 | assertEquals(BYTE_LEN + 5, b.length); 287 | 288 | // Important! Need to construct a stream, to force boundary conditions 289 | JsonParser p = cborParser(new ByteArrayInputStream(b)); 290 | assertToken(JsonToken.START_ARRAY, p.nextToken()); 291 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); 292 | String actual = p.getText(); 293 | 294 | final int end = Math.min(LONG.length(), actual.length()); 295 | for (int i = 0; i < end; ++i) { 296 | if (LONG.charAt(i) != actual.charAt(i)) { 297 | fail("Character #"+i+" (of "+end+") differs; expected 0x"+Integer.toHexString(LONG.charAt(i)) 298 | +" found 0x"+Integer.toHexString(actual.charAt(i))); 299 | } 300 | } 301 | 302 | assertEquals(LONG.length(), actual.length()); 303 | 304 | assertEquals(LONG, p.getText()); 305 | assertToken(JsonToken.END_ARRAY, p.nextToken()); 306 | assertNull(p.nextToken()); 307 | p.close(); 308 | } 309 | 310 | public void testLongChunkedText() throws Exception 311 | { 312 | // First, try with ASCII content 313 | StringBuilder sb = new StringBuilder(21000); 314 | for (int i = 0; i < 21000; ++i) { 315 | sb.append('Z'); 316 | } 317 | _testLongChunkedText(sb.toString()); 318 | // Second, with actual variable byte-length Unicode 319 | _testLongChunkedText(generateUnicodeString(21000)); 320 | } 321 | 322 | public void _testLongChunkedText(String input) throws Exception 323 | { 324 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 325 | CBORGenerator gen = cborGenerator(out); 326 | gen.writeString(input); 327 | gen.close(); 328 | 329 | final int textByteCount = input.getBytes("UTF-8").length; 330 | final byte[] b = out.toByteArray(); 331 | assertEquals((byte) (CBORConstants.PREFIX_TYPE_TEXT + 0x1F), b[0]); 332 | assertEquals(CBORConstants.BYTE_BREAK, b[b.length-1]); 333 | 334 | // First, verify validity by scanning 335 | int i = 1; 336 | int total = 0; 337 | 338 | for (int end = b.length-1; i < end; ) { 339 | int type = b[i++] & 0xFF; 340 | int len = type - CBORConstants.PREFIX_TYPE_TEXT; 341 | 342 | if (len < 24) { // tiny, fine 343 | ; 344 | } else if (len == 24) { // 1-byte 345 | len = (b[i++] & 0xFF); 346 | } else if (len == 25) { // 2-byte 347 | len = ((b[i++] & 0xFF) << 8) + (b[i++] & 0xFF); 348 | } 349 | i += len; 350 | total += len; 351 | } 352 | assertEquals(b.length-1, i); 353 | assertEquals(textByteCount, total); 354 | 355 | JsonParser p; 356 | 357 | // then skipping 358 | p = cborParser(new ByteArrayInputStream(b)); 359 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); 360 | assertNull(p.nextToken()); 361 | p.close(); 362 | 363 | // and then with actual full parsing/access 364 | p = cborParser(new ByteArrayInputStream(b)); 365 | assertToken(JsonToken.VALUE_STRING, p.nextToken()); 366 | String actual = p.getText(); 367 | assertNull(p.nextToken()); 368 | assertEquals(input.length(), actual.length()); 369 | if (!input.equals(actual)) { 370 | i = 0; 371 | while (i < input.length() && input.charAt(i) == actual.charAt(i)) { ++i; } 372 | fail("Strings differ at #"+i+" (length "+input.length()+"); expected 0x" 373 | +Integer.toHexString(input.charAt(i))+", got 0x" 374 | +Integer.toHexString(actual.charAt(i))); 375 | } 376 | assertEquals(input, actual); 377 | p.close(); 378 | } 379 | 380 | public void testFloatNumberType() throws IOException { 381 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 382 | CBORGenerator generator = cborGenerator(out); 383 | generator.writeStartObject(); 384 | generator.writeFieldName("foo"); 385 | generator.writeNumber(3f); 386 | generator.writeEndObject(); 387 | generator.close(); 388 | 389 | CBORParser parser = cborParser(out.toByteArray()); 390 | assertEquals(JsonToken.START_OBJECT, parser.nextToken()); 391 | assertEquals(JsonToken.FIELD_NAME, parser.nextToken()); 392 | assertEquals(JsonToken.VALUE_NUMBER_FLOAT, parser.nextToken()); 393 | assertEquals(NumberType.FLOAT, parser.getNumberType()); // fails with expected but was 394 | assertEquals(JsonToken.END_OBJECT, parser.nextToken()); 395 | parser.close(); 396 | } 397 | } 398 | -------------------------------------------------------------------------------- /src/test/java/com/fasterxml/jackson/dataformat/cbor/ParserWithJsonOrgSampleTest.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.dataformat.cbor; 2 | 3 | import java.io.IOException; 4 | 5 | import com.fasterxml.jackson.core.JsonParser; 6 | import com.fasterxml.jackson.core.JsonToken; 7 | 8 | /** 9 | * Tests that use the json.org sample document. 10 | */ 11 | public class ParserWithJsonOrgSampleTest extends CBORTestBase 12 | { 13 | // From JSON specification, sample doc... 14 | protected final static int SAMPLE_SPEC_VALUE_WIDTH = 800; 15 | protected final static int SAMPLE_SPEC_VALUE_HEIGHT = 600; 16 | protected final static String SAMPLE_SPEC_VALUE_TITLE = "View from 15th Floor"; 17 | protected final static String SAMPLE_SPEC_VALUE_TN_URL = "http://www.example.com/image/481989943"; 18 | protected final static int SAMPLE_SPEC_VALUE_TN_HEIGHT = 125; 19 | protected final static String SAMPLE_SPEC_VALUE_TN_WIDTH = "100"; 20 | protected final static int SAMPLE_SPEC_VALUE_TN_ID1 = 116; 21 | protected final static int SAMPLE_SPEC_VALUE_TN_ID2 = 943; 22 | protected final static int SAMPLE_SPEC_VALUE_TN_ID3 = 234; 23 | protected final static int SAMPLE_SPEC_VALUE_TN_ID4 = 38793; 24 | 25 | protected final static String SAMPLE_DOC_JSON_SPEC = 26 | "{\n" 27 | +" \"Image\" : {\n" 28 | +" \"Width\" : "+SAMPLE_SPEC_VALUE_WIDTH+",\n" 29 | +" \"Height\" : "+SAMPLE_SPEC_VALUE_HEIGHT+"," 30 | +"\"Title\" : \""+SAMPLE_SPEC_VALUE_TITLE+"\",\n" 31 | +" \"Thumbnail\" : {\n" 32 | +" \"Url\" : \""+SAMPLE_SPEC_VALUE_TN_URL+"\",\n" 33 | +"\"Height\" : "+SAMPLE_SPEC_VALUE_TN_HEIGHT+",\n" 34 | +" \"Width\" : \""+SAMPLE_SPEC_VALUE_TN_WIDTH+"\"\n" 35 | +" },\n" 36 | +" \"IDs\" : ["+SAMPLE_SPEC_VALUE_TN_ID1+","+SAMPLE_SPEC_VALUE_TN_ID2+","+SAMPLE_SPEC_VALUE_TN_ID3+","+SAMPLE_SPEC_VALUE_TN_ID4+"]\n" 37 | +" }" 38 | +"}" 39 | ; 40 | 41 | public void testJsonSampleDoc() throws IOException 42 | { 43 | byte[] data = cborDoc(SAMPLE_DOC_JSON_SPEC); 44 | verifyJsonSpecSampleDoc(cborParser(data), true, true); 45 | verifyJsonSpecSampleDoc(cborParser(data), true, false); 46 | verifyJsonSpecSampleDoc(cborParser(data), false, false); 47 | verifyJsonSpecSampleDoc(cborParser(data), false, true); 48 | } 49 | 50 | protected void verifyJsonSpecSampleDoc(JsonParser jp, boolean verifyContents, 51 | boolean requireNumbers) throws IOException 52 | { 53 | if (!jp.hasCurrentToken()) { 54 | jp.nextToken(); 55 | } 56 | assertToken(JsonToken.START_OBJECT, jp.getCurrentToken()); // main object 57 | 58 | assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Image' 59 | if (verifyContents) { 60 | verifyFieldName(jp, "Image"); 61 | } 62 | 63 | assertToken(JsonToken.START_OBJECT, jp.nextToken()); // 'image' object 64 | 65 | assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Width' 66 | if (verifyContents) { 67 | verifyFieldName(jp, "Width"); 68 | } 69 | 70 | verifyIntToken(jp.nextToken(), requireNumbers); 71 | if (verifyContents) { 72 | verifyIntValue(jp, SAMPLE_SPEC_VALUE_WIDTH); 73 | } 74 | 75 | assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Height' 76 | if (verifyContents) { 77 | verifyFieldName(jp, "Height"); 78 | } 79 | 80 | verifyIntToken(jp.nextToken(), requireNumbers); 81 | if (verifyContents) { 82 | verifyIntValue(jp, SAMPLE_SPEC_VALUE_HEIGHT); 83 | } 84 | assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Title' 85 | if (verifyContents) { 86 | verifyFieldName(jp, "Title"); 87 | } 88 | assertToken(JsonToken.VALUE_STRING, jp.nextToken()); 89 | assertEquals(SAMPLE_SPEC_VALUE_TITLE, getAndVerifyText(jp)); 90 | assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Thumbnail' 91 | if (verifyContents) { 92 | verifyFieldName(jp, "Thumbnail"); 93 | } 94 | 95 | assertToken(JsonToken.START_OBJECT, jp.nextToken()); // 'thumbnail' object 96 | assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Url' 97 | if (verifyContents) { 98 | verifyFieldName(jp, "Url"); 99 | } 100 | assertToken(JsonToken.VALUE_STRING, jp.nextToken()); 101 | if (verifyContents) { 102 | assertEquals(SAMPLE_SPEC_VALUE_TN_URL, getAndVerifyText(jp)); 103 | } 104 | assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Height' 105 | if (verifyContents) { 106 | verifyFieldName(jp, "Height"); 107 | } 108 | verifyIntToken(jp.nextToken(), requireNumbers); 109 | if (verifyContents) { 110 | verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_HEIGHT); 111 | } 112 | assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'Width' 113 | if (verifyContents) { 114 | verifyFieldName(jp, "Width"); 115 | } 116 | // Width value is actually a String in the example 117 | assertToken(JsonToken.VALUE_STRING, jp.nextToken()); 118 | if (verifyContents) { 119 | assertEquals(SAMPLE_SPEC_VALUE_TN_WIDTH, getAndVerifyText(jp)); 120 | } 121 | 122 | assertToken(JsonToken.END_OBJECT, jp.nextToken()); // 'thumbnail' object 123 | assertToken(JsonToken.FIELD_NAME, jp.nextToken()); // 'IDs' 124 | assertToken(JsonToken.START_ARRAY, jp.nextToken()); // 'ids' array 125 | verifyIntToken(jp.nextToken(), requireNumbers); // ids[0] 126 | if (verifyContents) { 127 | verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID1); 128 | } 129 | verifyIntToken(jp.nextToken(), requireNumbers); // ids[1] 130 | if (verifyContents) { 131 | verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID2); 132 | } 133 | verifyIntToken(jp.nextToken(), requireNumbers); // ids[2] 134 | if (verifyContents) { 135 | verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID3); 136 | } 137 | verifyIntToken(jp.nextToken(), requireNumbers); // ids[3] 138 | if (verifyContents) { 139 | verifyIntValue(jp, SAMPLE_SPEC_VALUE_TN_ID4); 140 | } 141 | assertToken(JsonToken.END_ARRAY, jp.nextToken()); // 'ids' array 142 | 143 | assertToken(JsonToken.END_OBJECT, jp.nextToken()); // 'image' object 144 | 145 | assertToken(JsonToken.END_OBJECT, jp.nextToken()); // main object 146 | } 147 | 148 | private void verifyIntToken(JsonToken t, boolean requireNumbers) 149 | { 150 | if (t == JsonToken.VALUE_NUMBER_INT) { 151 | return; 152 | } 153 | if (requireNumbers) { // to get error 154 | assertToken(JsonToken.VALUE_NUMBER_INT, t); 155 | } 156 | // if not number, must be String 157 | if (t != JsonToken.VALUE_STRING) { 158 | fail("Expected INT or STRING value, got "+t); 159 | } 160 | } 161 | 162 | protected void verifyFieldName(JsonParser jp, String expName) 163 | throws IOException 164 | { 165 | assertEquals(expName, jp.getText()); 166 | assertEquals(expName, jp.getCurrentName()); 167 | } 168 | 169 | protected void verifyIntValue(JsonParser jp, long expValue) 170 | throws IOException 171 | { 172 | // First, via textual 173 | assertEquals(String.valueOf(expValue), jp.getText()); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/test/java/com/fasterxml/jackson/dataformat/cbor/TestBiggerData.java: -------------------------------------------------------------------------------- 1 | package com.fasterxml.jackson.dataformat.cbor; 2 | 3 | import java.util.*; 4 | 5 | import com.fasterxml.jackson.databind.*; 6 | 7 | /** 8 | * Bigger test to try to do smoke-testing of overall functionality, 9 | * using more sizable (500k of JSON, 200k of encoded data) dataset. 10 | * Should tease out at least some of boundary conditions. 11 | */ 12 | public class TestBiggerData extends CBORTestBase 13 | { 14 | static class Citm 15 | { 16 | public Map areaNames; 17 | public Map audienceSubCategoryNames; 18 | public Map blockNames; 19 | public Map seatCategoryNames; 20 | public Map subTopicNames; 21 | public Map subjectNames; 22 | public Map topicNames; 23 | public Map topicSubTopics; 24 | public Map venueNames; 25 | 26 | public Map events; 27 | public List performances; 28 | } 29 | 30 | static class Event 31 | { 32 | public int id; 33 | public String name; 34 | public String description; 35 | public String subtitle; 36 | public String logo; 37 | public int subjectCode; 38 | public int[] topicIds; 39 | public LinkedHashSet subTopicIds; 40 | } 41 | 42 | static class Performance 43 | { 44 | public int id; 45 | public int eventId; 46 | public String name; 47 | public String description; 48 | public String logo; 49 | 50 | public List prices; 51 | public List seatCategories; 52 | 53 | public long start; 54 | public String seatMapImage; 55 | public String venueCode; 56 | } 57 | 58 | static class Price { 59 | public int amount; 60 | public int audienceSubCategoryId; 61 | public int seatCategoryId; 62 | } 63 | 64 | static class SeatCategory { 65 | public int seatCategoryId; 66 | public List areas; 67 | } 68 | 69 | static class Area { 70 | public int areaId; 71 | public int[] blockIds; 72 | } 73 | 74 | /* 75 | /********************************************************** 76 | /* Test methods 77 | /********************************************************** 78 | */ 79 | 80 | final ObjectMapper MAPPER = new ObjectMapper(); 81 | 82 | public void testReading() throws Exception 83 | { 84 | Citm citm0 = MAPPER.readValue(getClass().getResourceAsStream("/data/citm_catalog.json"), 85 | Citm.class); 86 | 87 | ObjectMapper mapper = cborMapper(); 88 | byte[] cbor = mapper.writeValueAsBytes(citm0); 89 | 90 | Citm citm = mapper.readValue(cbor, Citm.class); 91 | 92 | assertNotNull(citm); 93 | assertNotNull(citm.areaNames); 94 | assertEquals(17, citm.areaNames.size()); 95 | assertNotNull(citm.events); 96 | assertEquals(184, citm.events.size()); 97 | 98 | assertNotNull(citm.seatCategoryNames); 99 | assertEquals(64, citm.seatCategoryNames.size()); 100 | assertNotNull(citm.subTopicNames); 101 | assertEquals(19, citm.subTopicNames.size()); 102 | assertNotNull(citm.subjectNames); 103 | assertEquals(0, citm.subjectNames.size()); 104 | assertNotNull(citm.topicNames); 105 | assertEquals(4, citm.topicNames.size()); 106 | assertNotNull(citm.topicSubTopics); 107 | assertEquals(4, citm.topicSubTopics.size()); 108 | assertNotNull(citm.venueNames); 109 | assertEquals(1, citm.venueNames.size()); 110 | } 111 | 112 | public void testRoundTrip() throws Exception 113 | { 114 | Citm citm0 = MAPPER.readValue(getClass().getResourceAsStream("/data/citm_catalog.json"), 115 | Citm.class); 116 | ObjectMapper mapper = cborMapper(); 117 | byte[] cbor = mapper.writeValueAsBytes(citm0); 118 | 119 | Citm citm = mapper.readValue(cbor, Citm.class); 120 | 121 | byte[] smile1 = mapper.writeValueAsBytes(citm); 122 | Citm citm2 = mapper.readValue(smile1, Citm.class); 123 | byte[] smile2 = mapper.writeValueAsBytes(citm2); 124 | 125 | assertEquals(smile1.length, smile2.length); 126 | 127 | assertNotNull(citm.areaNames); 128 | assertEquals(17, citm.areaNames.size()); 129 | assertNotNull(citm.events); 130 | assertEquals(184, citm.events.size()); 131 | 132 | assertEquals(citm.seatCategoryNames.size(), citm2.seatCategoryNames.size()); 133 | assertEquals(citm.subTopicNames.size(), citm2.subTopicNames.size()); 134 | assertEquals(citm.subjectNames.size(), citm2.subjectNames.size()); 135 | assertEquals(citm.topicNames.size(), citm2.topicNames.size()); 136 | assertEquals(citm.topicSubTopics.size(), citm2.topicSubTopics.size()); 137 | assertEquals(citm.venueNames.size(), citm2.venueNames.size()); 138 | } 139 | } 140 | --------------------------------------------------------------------------------