Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 93 additions & 53 deletions cbor/src/main/java/tools/jackson/dataformat/cbor/CBORGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,21 @@ public class CBORGenerator extends GeneratorBase
*/
private final static int INDEFINITE_LENGTH = -2; // just to allow -1 as marker for "one too many"

// True when VarHandles are available on this runtime. Checked once at class
// load so that if CBORVarHandleUtil itself fails to load (e.g. Android
// without java.lang.invoke), we fall back to manual byte shifting instead
// of propagating NoClassDefFoundError.
private static final boolean _VARHANDLE_AVAILABLE;
static {
boolean available = false;
try {
available = CBORVarHandleUtil.INT_BE != null;
} catch (Throwable t) {
// CBORVarHandleUtil class not loadable — fall back to manual byte shifting
}
_VARHANDLE_AVAILABLE = available;
}

/*
/**********************************************************************
/* Configuration
Expand Down Expand Up @@ -603,10 +618,15 @@ private final void _writeIntFull(int markerBase, int i) throws JacksonException
_ensureRoomForOutput(5);

_outputBuffer[_outputTail++] = (byte) (markerBase + SUFFIX_UINT32_ELEMENTS);
_outputBuffer[_outputTail++] = (byte) (i >> 24);
_outputBuffer[_outputTail++] = (byte) (i >> 16);
_outputBuffer[_outputTail++] = (byte) (i >> 8);
_outputBuffer[_outputTail++] = (byte) i;
if (_VARHANDLE_AVAILABLE) {
CBORVarHandleUtil.setInt(_outputBuffer, _outputTail, i);
_outputTail += 4;
} else {
_outputBuffer[_outputTail++] = (byte) (i >> 24);
_outputBuffer[_outputTail++] = (byte) (i >> 16);
_outputBuffer[_outputTail++] = (byte) (i >> 8);
_outputBuffer[_outputTail++] = (byte) i;
}
}

// Helper method that works like `writeNumber(long)` but DOES NOT
Expand Down Expand Up @@ -634,16 +654,21 @@ private final void _writeLongNoCheck(long l) throws JacksonException
} else {
_outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_POS + SUFFIX_UINT64_ELEMENTS);
}
int i = (int) (l >> 32);
_outputBuffer[_outputTail++] = (byte) (i >> 24);
_outputBuffer[_outputTail++] = (byte) (i >> 16);
_outputBuffer[_outputTail++] = (byte) (i >> 8);
_outputBuffer[_outputTail++] = (byte) i;
i = (int) l;
_outputBuffer[_outputTail++] = (byte) (i >> 24);
_outputBuffer[_outputTail++] = (byte) (i >> 16);
_outputBuffer[_outputTail++] = (byte) (i >> 8);
_outputBuffer[_outputTail++] = (byte) i;
if (_VARHANDLE_AVAILABLE) {
CBORVarHandleUtil.setLong(_outputBuffer, _outputTail, l);
_outputTail += 8;
} else {
int i = (int) (l >> 32);
_outputBuffer[_outputTail++] = (byte) (i >> 24);
_outputBuffer[_outputTail++] = (byte) (i >> 16);
_outputBuffer[_outputTail++] = (byte) (i >> 8);
_outputBuffer[_outputTail++] = (byte) i;
i = (int) l;
_outputBuffer[_outputTail++] = (byte) (i >> 24);
_outputBuffer[_outputTail++] = (byte) (i >> 16);
_outputBuffer[_outputTail++] = (byte) (i >> 8);
_outputBuffer[_outputTail++] = (byte) i;
}
}

private final void _writeFloatNoCheck(float f) throws JacksonException {
Expand All @@ -654,33 +679,38 @@ private final void _writeFloatNoCheck(float f) throws JacksonException {
* if there are cases where collapsing of NaN was needed (for non-Java
* clients), this can be changed
*/
int i = Float.floatToRawIntBits(f);
_outputBuffer[_outputTail++] = BYTE_FLOAT32;
_outputBuffer[_outputTail++] = (byte) (i >> 24);
_outputBuffer[_outputTail++] = (byte) (i >> 16);
_outputBuffer[_outputTail++] = (byte) (i >> 8);
_outputBuffer[_outputTail++] = (byte) i;
if (_VARHANDLE_AVAILABLE) {
CBORVarHandleUtil.setFloat(_outputBuffer, _outputTail, f);
_outputTail += 4;
} else {
int i = Float.floatToRawIntBits(f);
_outputBuffer[_outputTail++] = (byte) (i >> 24);
_outputBuffer[_outputTail++] = (byte) (i >> 16);
_outputBuffer[_outputTail++] = (byte) (i >> 8);
_outputBuffer[_outputTail++] = (byte) i;
}
}

private final void _writeDoubleNoCheck(double d) throws JacksonException {
_ensureRoomForOutput(9);
// 17-Apr-2010, tatu: could also use 'doubleToIntBits', but it seems
// more accurate to use exact representation; and possibly faster.
// However, if there are cases where collapsing of NaN was needed (for
// non-Java clients), this can be changed
long l = Double.doubleToRawLongBits(d);
_outputBuffer[_outputTail++] = BYTE_FLOAT64;

int i = (int) (l >> 32);
_outputBuffer[_outputTail++] = (byte) (i >> 24);
_outputBuffer[_outputTail++] = (byte) (i >> 16);
_outputBuffer[_outputTail++] = (byte) (i >> 8);
_outputBuffer[_outputTail++] = (byte) i;
i = (int) l;
_outputBuffer[_outputTail++] = (byte) (i >> 24);
_outputBuffer[_outputTail++] = (byte) (i >> 16);
_outputBuffer[_outputTail++] = (byte) (i >> 8);
_outputBuffer[_outputTail++] = (byte) i;
if (_VARHANDLE_AVAILABLE) {
CBORVarHandleUtil.setDouble(_outputBuffer, _outputTail, d);
_outputTail += 8;
} else {
long l = Double.doubleToRawLongBits(d);
int i = (int) (l >> 32);
_outputBuffer[_outputTail++] = (byte) (i >> 24);
_outputBuffer[_outputTail++] = (byte) (i >> 16);
_outputBuffer[_outputTail++] = (byte) (i >> 8);
_outputBuffer[_outputTail++] = (byte) i;
i = (int) l;
_outputBuffer[_outputTail++] = (byte) (i >> 24);
_outputBuffer[_outputTail++] = (byte) (i >> 16);
_outputBuffer[_outputTail++] = (byte) (i >> 8);
_outputBuffer[_outputTail++] = (byte) i;
}
}

private final void _writeDoubleMinimal(double d) throws JacksonException {
Expand Down Expand Up @@ -1024,14 +1054,19 @@ public void writeNumberUnsigned(long l) throws IOException {
_verifyValueWrite("write number unsigned");
_ensureRoomForOutput(9);
_outputBuffer[_outputTail++] = (byte) (PREFIX_TYPE_INT_POS + SUFFIX_UINT64_ELEMENTS);
_outputBuffer[_outputTail++] = (byte) (l >> 56);
_outputBuffer[_outputTail++] = (byte) (l >> 48);
_outputBuffer[_outputTail++] = (byte) (l >> 40);
_outputBuffer[_outputTail++] = (byte) (l >> 32);
_outputBuffer[_outputTail++] = (byte) (l >> 24);
_outputBuffer[_outputTail++] = (byte) (l >> 16);
_outputBuffer[_outputTail++] = (byte) (l >> 8);
_outputBuffer[_outputTail++] = (byte) l;
if (_VARHANDLE_AVAILABLE) {
CBORVarHandleUtil.setLong(_outputBuffer, _outputTail, l);
_outputTail += 8;
} else {
_outputBuffer[_outputTail++] = (byte) (l >> 56);
_outputBuffer[_outputTail++] = (byte) (l >> 48);
_outputBuffer[_outputTail++] = (byte) (l >> 40);
_outputBuffer[_outputTail++] = (byte) (l >> 32);
_outputBuffer[_outputTail++] = (byte) (l >> 24);
_outputBuffer[_outputTail++] = (byte) (l >> 16);
_outputBuffer[_outputTail++] = (byte) (l >> 8);
_outputBuffer[_outputTail++] = (byte) l;
}
}

@Override
Expand All @@ -1058,16 +1093,21 @@ public JsonGenerator writeNumber(long l) throws JacksonException {
} else {
_outputBuffer[_outputTail++] = (PREFIX_TYPE_INT_POS + SUFFIX_UINT64_ELEMENTS);
}
int i = (int) (l >> 32);
_outputBuffer[_outputTail++] = (byte) (i >> 24);
_outputBuffer[_outputTail++] = (byte) (i >> 16);
_outputBuffer[_outputTail++] = (byte) (i >> 8);
_outputBuffer[_outputTail++] = (byte) i;
i = (int) l;
_outputBuffer[_outputTail++] = (byte) (i >> 24);
_outputBuffer[_outputTail++] = (byte) (i >> 16);
_outputBuffer[_outputTail++] = (byte) (i >> 8);
_outputBuffer[_outputTail++] = (byte) i;
if (_VARHANDLE_AVAILABLE) {
CBORVarHandleUtil.setLong(_outputBuffer, _outputTail, l);
_outputTail += 8;
} else {
int i = (int) (l >> 32);
_outputBuffer[_outputTail++] = (byte) (i >> 24);
_outputBuffer[_outputTail++] = (byte) (i >> 16);
_outputBuffer[_outputTail++] = (byte) (i >> 8);
_outputBuffer[_outputTail++] = (byte) i;
i = (int) l;
_outputBuffer[_outputTail++] = (byte) (i >> 24);
_outputBuffer[_outputTail++] = (byte) (i >> 16);
_outputBuffer[_outputTail++] = (byte) (i >> 8);
_outputBuffer[_outputTail++] = (byte) i;
}
return this;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package tools.jackson.dataformat.cbor;

import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;

/**
* Utility class that provides {@link VarHandle} instances for efficient
* multi-byte primitive writes to byte arrays. Falls back gracefully
* if VarHandles are not available (e.g., on some Android runtimes).
* @since 3.3
*/
Comment thread
pjfanning marked this conversation as resolved.
final class CBORVarHandleUtil
{
/**
* VarHandle for writing a {@code float} as 4 big-endian bytes.
* {@code null} if VarHandles are unavailable.
*/
static final VarHandle FLOAT_BE;

/**
* VarHandle for writing a {@code double} as 8 big-endian bytes.
* {@code null} if VarHandles are unavailable.
*/
static final VarHandle DOUBLE_BE;

/**
* VarHandle for writing an {@code int} as 4 big-endian bytes.
* {@code null} if VarHandles are unavailable.
*/
static final VarHandle INT_BE;

/**
* VarHandle for writing a {@code long} as 8 big-endian bytes.
* {@code null} if VarHandles are unavailable.
*/
static final VarHandle LONG_BE;

static {
VarHandle floatBe = null;
VarHandle doubleBe = null;
VarHandle intBe = null;
VarHandle longBe = null;
try {
floatBe = MethodHandles.byteArrayViewVarHandle(float[].class, ByteOrder.BIG_ENDIAN);
doubleBe = MethodHandles.byteArrayViewVarHandle(double[].class, ByteOrder.BIG_ENDIAN);
intBe = MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.BIG_ENDIAN);
longBe = MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.BIG_ENDIAN);
} catch (Throwable t) {
// VarHandles not available (e.g., Android) — fall back to manual byte shifting
}
FLOAT_BE = floatBe;
DOUBLE_BE = doubleBe;
INT_BE = intBe;
LONG_BE = longBe;
}

private CBORVarHandleUtil() { }

// Helper methods that write primitives via the class's own VarHandle fields.
// Only called when the corresponding field is non-null, which implies
// VarHandle is available on this runtime.

static void setInt(byte[] array, int offset, int value) {
INT_BE.set(array, offset, value);
}

static void setLong(byte[] array, int offset, long value) {
LONG_BE.set(array, offset, value);
}

static void setFloat(byte[] array, int offset, float value) {
FLOAT_BE.set(array, offset, value);
}

static void setDouble(byte[] array, int offset, double value) {
DOUBLE_BE.set(array, offset, value);
}
}