package com.fasterxml.jackson.dataformat.protobuf;
import java.io.*;
Helper object used for buffering content for cases where we need (byte-)length prefixes
for content like packed arrays, embedded messages, Strings and (perhaps) binary content.
/**
* Helper object used for buffering content for cases where we need (byte-)length prefixes
* for content like packed arrays, embedded messages, Strings and (perhaps) binary content.
*/
public class ByteAccumulator
{
protected final ByteAccumulator _parent;
Caller-provided buffer in which optional type prefix,
and mandatory length indicator may be added.
Caller ensures there is enough room for both, i.e. up
to 10 bytes (if both) or 5 bytes (if just length)
/**
* Caller-provided buffer in which optional type prefix,
* and mandatory length indicator may be added.
* Caller ensures there is enough room for both, i.e. up
* to 10 bytes (if both) or 5 bytes (if just length)
*/
protected final byte[] _prefixBuffer;
Offset within _prefixBuffer
where there is room for encoding prefix (type, tag, length). /**
* Offset within {@link #_prefixBuffer} where there is room for encoding
* prefix (type, tag, length).
*/
protected final int _prefixOffset;
protected final int _typedTag;
protected Segment _firstSegment, _lastSegment;
Total number of bytes contained within buffers, to be used for length prefix.
/**
* Total number of bytes contained within buffers, to be used for length prefix.
*/
protected int _segmentBytes;
Pointer to start of contents provided by parent, preceding room
for prefix (that is, same as or less than `_prefixOffset`)
Since: 2.8.8
/**
* Pointer to start of contents provided by parent, preceding room
* for prefix (that is, same as or less than `_prefixOffset`)
*
* @since 2.8.8
*/
protected int _parentStart;
public ByteAccumulator(ByteAccumulator p, int typedTag,
byte[] prefixBuffer, int prefixOffset, int parentStart)
{
_parent = p;
_typedTag = typedTag;
_prefixBuffer = prefixBuffer;
_prefixOffset = prefixOffset;
_parentStart = parentStart;
}
public void append(byte[] buf, int offset, int len) {
Segment s = new Segment(buf, offset, len);
if (_lastSegment == null) {
_firstSegment = _lastSegment = s;
} else {
_lastSegment = _lastSegment.linkNext(s);
}
_segmentBytes += len;
}
public ByteAccumulator finish(OutputStream out,
byte[] input, int offset, int len) throws IOException
{
final byte[] prefixBuf = _prefixBuffer;
int ptr;
// First: encode full tag to use, now that we length to calculate prefix from
if (_typedTag == -1) {
ptr = _prefixOffset;
} else {
ptr = ProtobufUtil.appendLengthLength(_typedTag, prefixBuf, _prefixOffset);
}
int plen = _segmentBytes + len;
ptr = ProtobufUtil.appendLengthLength(plen, prefixBuf, ptr);
// root? Just output it all
final int writeStart = _parentStart; // same as `_prefixOffset` or less, if buffered content
if (_parent == null) {
// 04-Apr-2017, tatu: We know that parent will have flushed anything it might have,
// so `_parentStart` is irrelevant here (but not in the other branch)
out.write(prefixBuf, writeStart, ptr-writeStart);
for (Segment s = _firstSegment; s != null; s = s.next()) {
s.writeTo(out);
}
if (len > 0) {
out.write(input, offset, len);
}
} else {
// 04-Apr-2017, tatu: for [dataformats-binary#67], need to flush possible
// content parent had...
_parent.append(prefixBuf, writeStart, ptr-writeStart);
if (_firstSegment != null) {
_parent.appendAll(_firstSegment, _lastSegment, _segmentBytes);
}
if (len > 0) {
_parent.append(input, offset, len);
}
}
return _parent;
}
public ByteAccumulator finish(OutputStream out, byte[] input) throws IOException
{
int ptr;
final byte[] prefixBuf = _prefixBuffer;
if (_typedTag == -1) {
ptr = _prefixOffset;
} else {
ptr = ProtobufUtil.appendLengthLength(_typedTag, prefixBuf, _prefixOffset);
}
int plen = _segmentBytes;
ptr = ProtobufUtil.appendLengthLength(plen, prefixBuf, ptr);
final int writeStart = _parentStart; // same as `_prefixOffset` or less, if buffered content
// root? Just output it all
if (_parent == null) {
// 04-Apr-2017, tatu: We know that parent will have flushed anything it might have,
// so `_parentStart` is irrelevant here (but not in the other branch)
out.write(prefixBuf, writeStart, ptr-writeStart);
for (Segment s = _firstSegment; s != null; s = s.next()) {
s.writeTo(out);
}
} else {
_parent.append(prefixBuf, writeStart, ptr-writeStart);
if (_firstSegment != null) {
_parent.appendAll(_firstSegment, _lastSegment, _segmentBytes);
}
}
return _parent;
}
private void appendAll(Segment first, Segment last, int segmentBytes)
{
_segmentBytes += segmentBytes;
if (_firstSegment == null) {
_firstSegment = first;
_lastSegment = last;
} else {
_lastSegment.linkNext(first);
_lastSegment = last;
}
}
/*
/**********************************************************
/* Helper classes
/**********************************************************
*/
private final static class Segment
{
private final byte[] _buffer;
private final int _start, _length;
private Segment _next;
public Segment(byte[] buffer, int start, int length) {
_buffer = buffer;
_start = start;
_length = length;
}
public Segment linkNext(Segment next) {
_next = next;
return next;
}
public Segment next() {
return _next;
}
public void writeTo(OutputStream out) throws IOException {
out.write(_buffer, _start, _length);
}
}
}