├── .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 |
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
307 | * Since CBOR format always uses UTF-8 internally,
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
57 | * Default value is
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 | ArrayListenc
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 | *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 | *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 | *