com.fasterxml.jackson.databind.node.NodeSerialization Maven / Gradle / Ivy
package com.fasterxml.jackson.databind.node;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import com.fasterxml.jackson.core.util.ByteArrayBuilder;
/**
* Helper value class only used during JDK serialization: contains JSON as `byte[]`
*
* @since 2.10
*/
class NodeSerialization implements java.io.Serializable,
java.io.Externalizable
{
// To avoid malicious input only allocate up to 100k
protected final static int LONGEST_EAGER_ALLOC = 100_000;
private static final long serialVersionUID = 1L;
public byte[] json;
public NodeSerialization() { }
public NodeSerialization(byte[] b) { json = b; }
protected Object readResolve() {
try {
return InternalNodeMapper.bytesToNode(json);
} catch (IOException e) {
throw new IllegalArgumentException("Failed to JDK deserialize `JsonNode` value: "+e.getMessage(), e);
}
}
public static NodeSerialization from(Object o) {
try {
return new NodeSerialization(InternalNodeMapper.valueToBytes(o));
} catch (IOException e) {
throw new IllegalArgumentException("Failed to JDK serialize `"+o.getClass().getSimpleName()+"` value: "+e.getMessage(), e);
}
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(json.length);
out.write(json);
}
@Override
public void readExternal(ObjectInput in) throws IOException {
final int len = in.readInt();
json = _read(in, len);
}
private byte[] _read(ObjectInput in, int expLen) throws IOException {
// Common case, just read directly
if (expLen <= LONGEST_EAGER_ALLOC) {
byte[] result = new byte[expLen];
in.readFully(result, 0, expLen);
return result;
}
// but longer content needs more care to avoid DoS by maliciously crafted data
// (this wrt [databind#3328]
try (final ByteArrayBuilder bb = new ByteArrayBuilder(LONGEST_EAGER_ALLOC)) {
byte[] buffer = bb.resetAndGetFirstSegment();
int outOffset = 0;
while (true) {
int toRead = Math.min(buffer.length - outOffset, expLen);
in.readFully(buffer, 0, toRead);
expLen -= toRead;
outOffset += toRead;
// Did we get everything we needed? If so, we are done
if (expLen == 0) {
return bb.completeAndCoalesce(outOffset);
}
// Or perhaps we filled the current segment? If so, finish, get next
if (outOffset == buffer.length) {
buffer = bb.finishCurrentSegment();
outOffset = 0;
}
}
}
}
}