Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
131 changes: 78 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 @@ -603,10 +603,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 (CBORVarHandleUtil.INT_BE != null) {
CBORVarHandleUtil.INT_BE.set(_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 +639,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 (CBORVarHandleUtil.LONG_BE != null) {
CBORVarHandleUtil.LONG_BE.set(_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 +664,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 (CBORVarHandleUtil.FLOAT_BE != null) {
CBORVarHandleUtil.FLOAT_BE.set(_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 (CBORVarHandleUtil.DOUBLE_BE != null) {
CBORVarHandleUtil.DOUBLE_BE.set(_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 +1039,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 (CBORVarHandleUtil.LONG_BE != null) {
CBORVarHandleUtil.LONG_BE.set(_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 +1078,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 (CBORVarHandleUtil.LONG_BE != null) {
CBORVarHandleUtil.LONG_BE.set(_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,58 @@
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).
*/

@cowtowncoder cowtowncoder Aug 14, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@since 3.3

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 (Exception e) {
// 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() { }
}