Skip to content

Commit

Permalink
Merge pull request #177 from NordicSemiconductor/bugfix/canonical-cbor
Browse files Browse the repository at this point in the history
Fixed encoding CBOR in canonical format
  • Loading branch information
philips77 authored Aug 26, 2024
2 parents a3c3a6e + 7dc41e3 commit c491d05
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 1 deletion.
3 changes: 3 additions & 0 deletions mcumgr-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,11 @@ dependencies {

// Import CBOR parser - version 2.14+ requires Android 8
// See: https://github.com/NordicSemiconductor/Android-nRF-Connect-Device-Manager/issues/135
//noinspection GradleDependency
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.13.5' // don't update
//noinspection GradleDependency
implementation 'com.fasterxml.jackson.core:jackson-core:2.13.5' // don't update
//noinspection GradleDependency
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.5' // don't update

// Test
Expand Down
2 changes: 1 addition & 1 deletion mcumgr-core/src/main/java/io/runtime/mcumgr/util/CBOR.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

@SuppressWarnings("unused")
public class CBOR {
private final static CBORFactory sFactory = new CBORFactory();
private final static CBORFactory sFactory = new CanonicalCBORFactory();

public static byte[] toBytes(Object obj) throws IOException {
ObjectMapper mapper = new ObjectMapper(sFactory);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package io.runtime.mcumgr.util;

import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.dataformat.cbor.CBORConstants;
import com.fasterxml.jackson.dataformat.cbor.CBORFactory;
import com.fasterxml.jackson.dataformat.cbor.CBORGenerator;

import java.io.IOException;
import java.io.OutputStream;

/**
* This class is a copy of {@link CBORFactory} with the only difference
* that it returns {@link CanonicalCBORGenerator} instead of {@link CBORGenerator}.
*/
public class CanonicalCBORFactory extends CBORFactory {

@Override
public CBORGenerator createGenerator(OutputStream out, JsonEncoding enc) throws IOException {
final IOContext ctxt = _createContext(_createContentReference(out), false);
return _createCBORGenerator(ctxt,
_generatorFeatures, _formatGeneratorFeatures, _objectCodec,
_decorate(out, ctxt));
}

@Override
public CBORGenerator createGenerator(OutputStream out) throws IOException {
final IOContext ctxt = _createContext(_createContentReference(out), false);
return _createCBORGenerator(ctxt,
_generatorFeatures, _formatGeneratorFeatures, _objectCodec,
_decorate(out, ctxt));
}

@Override
protected CBORGenerator _createUTF8Generator(OutputStream out, IOContext ctxt) throws IOException {
return _createCBORGenerator(ctxt,
_generatorFeatures, _formatGeneratorFeatures, _objectCodec, out);
}

// These methods are required to make the overriding class to work:

private CBORGenerator _createCBORGenerator(IOContext ctxt,
int stdFeat, int formatFeat, ObjectCodec codec, OutputStream out) throws IOException
{
// false -> we won't manage the stream unless explicitly directed to
CanonicalCBORGenerator gen = new CanonicalCBORGenerator(ctxt, stdFeat, formatFeat, _objectCodec, out);
if (CBORGenerator.Feature.WRITE_TYPE_HEADER.enabledIn(formatFeat)) {
gen.writeTag(CBORConstants.TAG_ID_SELF_DESCRIBE);
}
return gen;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package io.runtime.mcumgr.util;

import static com.fasterxml.jackson.dataformat.cbor.CBORConstants.PREFIX_TYPE_OBJECT;
import static com.fasterxml.jackson.dataformat.cbor.CBORConstants.SUFFIX_UINT16_ELEMENTS;
import static com.fasterxml.jackson.dataformat.cbor.CBORConstants.SUFFIX_UINT32_ELEMENTS;
import static com.fasterxml.jackson.dataformat.cbor.CBORConstants.SUFFIX_UINT8_ELEMENTS;

import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.dataformat.cbor.CBORGenerator;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;

/**
* This class is a copy of {@link CBORGenerator} with the only difference
* that it writes maps in canonical form.
* <p>
* The implementation is copied from {@link CBORGenerator} at version 2.17.1, which cannot be used
* as 2.14+ support only Android 8+.
*/
public class CanonicalCBORGenerator extends CBORGenerator {

public CanonicalCBORGenerator(IOContext ctxt, int stdFeatures, int formatFeatures, ObjectCodec codec, OutputStream out) {
super(ctxt, stdFeatures, formatFeatures, codec, out);
}

public CanonicalCBORGenerator(IOContext ctxt, int stdFeatures, int formatFeatures, ObjectCodec codec, OutputStream out, byte[] outputBuffer, int offset, boolean bufferRecyclable) {
super(ctxt, stdFeatures, formatFeatures, codec, out, outputBuffer, offset, bufferRecyclable);
}

@Override
public void writeStartObject(Object forValue, int elementsToWrite) throws IOException {
_verifyValueWrite("start an object");
_streamWriteContext = _streamWriteContext.createChildObjectContext(forValue);
_pushRemainingElements();
_currentRemainingElements = elementsToWrite;
_writeLengthMarker(PREFIX_TYPE_OBJECT, elementsToWrite);
}

// These methods are required to make the overriding class to work:

private void _pushRemainingElements() {
if (_elementCounts.length == _elementCountsPtr) { // initially, as well as if full
_elementCounts = Arrays.copyOf(_elementCounts, _elementCounts.length+10);
}
_elementCounts[_elementCountsPtr++] = _currentRemainingElements;
}

private void _writeLengthMarker(int majorType, int i)
throws IOException {
_ensureRoomForOutput(5);
if (i < 24) {
_outputBuffer[_outputTail++] = (byte) (majorType + i);
return;
}
if (i <= 0xFF) {
_outputBuffer[_outputTail++] = (byte) (majorType + SUFFIX_UINT8_ELEMENTS);
_outputBuffer[_outputTail++] = (byte) i;
return;
}
final byte b0 = (byte) i;
i >>= 8;
if (i <= 0xFF) {
_outputBuffer[_outputTail++] = (byte) (majorType + SUFFIX_UINT16_ELEMENTS);
_outputBuffer[_outputTail++] = (byte) i;
_outputBuffer[_outputTail++] = b0;
return;
}
_outputBuffer[_outputTail++] = (byte) (majorType + SUFFIX_UINT32_ELEMENTS);
_outputBuffer[_outputTail++] = (byte) (i >> 16);
_outputBuffer[_outputTail++] = (byte) (i >> 8);
_outputBuffer[_outputTail++] = (byte) i;
_outputBuffer[_outputTail++] = b0;
}

private void _ensureRoomForOutput(int needed) throws IOException {
if ((_outputTail + needed) >= _outputEnd) {
_flushBuffer();
}
}
}

0 comments on commit c491d05

Please sign in to comment.