com.amazon.ion.impl.IonWriterSystemBinary Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ion-java Show documentation
Show all versions of ion-java Show documentation
A Java implementation of the Amazon Ion data notation.
/*
* Copyright 2007-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.amazon.ion.impl;
import static com.amazon.ion.impl._Private_IonConstants.tidDATAGRAM;
import static com.amazon.ion.impl._Private_IonConstants.tidList;
import static com.amazon.ion.impl._Private_IonConstants.tidSexp;
import static com.amazon.ion.impl._Private_IonConstants.tidStruct;
import static com.amazon.ion.impl.lite._Private_LiteDomTrampoline.reverseEncode;
import com.amazon.ion.IonException;
import com.amazon.ion.IonType;
import com.amazon.ion.SymbolTable;
import com.amazon.ion.Timestamp;
import com.amazon.ion.impl.BlockedBuffer.BlockedByteInputStream;
import com.amazon.ion.impl.IonBinary.BufferManager;
import com.amazon.ion.system.IonWriterBuilder.InitialIvmHandling;
import com.amazon.ion.system.IonWriterBuilder.IvmMinimizing;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.LinkedList;
import java.util.Queue;
final class IonWriterSystemBinary
extends IonWriterSystem
implements _Private_ListWriter
{
/**
* Implements patching of the internal OutputStream with actual value
* lengths and types. Every instance of this class represents a container
* (including the datagram). Information about the children of the container
* is stored in _types/_positions/_lengths arrays.
*
* The patching mechanism works as follows. For every primitive value, we store
* its type, position in internal OutputStream and the serialized lengths
* in this class. With every value insertion, its length is then summed to the
* length of the @_parent. Upon seeing a container, a new PatchedValue is created
* and inserted into @_children queue, then all subsequent values are written
* as described above. As the container is finished, its @_parent becomes active
* and values are continued to be written. In the end, a general tree is created
* with information about all the values in the datagram. The values themselves
* are serialized into internal OutputStream
*
* To finalize the internal OutputStream into a user stream, the arrays in
* PatchedValues are traversed from the beginning. For every value in
* in @_types[i], it's type descriptor is written into user stream. If the type
* is primitive, content of the internal OutputStream is copied starting
* from @_positions[i] with length @_lengths[i]. If @_types[i] is a container,
* a child is popped from the @_children queue and recursively written as
* described above. Whenever TID_SYMBOL_TABLE_PATCH is seen in @_types, the
* current symbol table gets reset with the one on the top of @_symtabs queue.
*/
static class PatchedValues {
private final static int DEFAULT_PATCH_COUNT = 10;
/** first free position in the _types/_positions/_lengths arrays */
int _freePos;
/** types of the container */
int[] _types;
/** positions where values start in buffer */
int[] _positions;
/**
* lengths of the values. The high 32bits of this value store the length
* of the field name, the low 32bits store the actual value length. We need the
* field name length to properly adjust the total @_parent length in @endPatch
*/
long[] _lengths;
// the parent
PatchedValues _parent;
Queue _children;
Queue _symtabs;
PatchedValues() {
_freePos = -1;
_types = new int[DEFAULT_PATCH_COUNT];
_positions = new int[DEFAULT_PATCH_COUNT];
_lengths = new long[DEFAULT_PATCH_COUNT];
}
void reset() {
_freePos = -1;
_children = null;
_symtabs = null;
}
/**
* Add a new PatchedValues instance to _children and return it
*/
PatchedValues addChild() {
PatchedValues pv = new PatchedValues();
pv._parent = this;
if (_children == null) {
_children = new LinkedList();
}
_children.add(pv);
return pv;
}
/**
* Inject a symbol table at the specified position in internal OutputStream. Most of the
* times, the injection will happen upon seeing the first non-system symbol. At that point
* the information about the symbol is already stored here by @startPatch call, so we need
* to be able to inject a symtab before this value (injectBeforeCurrent)
*
* @param st
* @param injectBeforeCurrent Flags if symbol table must be injected before the current value
* which essentially shifts the array by 1
* @throws IllegalStateException if the PatchedValues is not a top-level one
*/
void injectSymbolTable(SymbolTable st, boolean injectBeforeCurrent) {
if (_parent != null) {
// we're not on top-level
throw new IllegalStateException("Cannot inject a symbol table when not on top-level");
}
if (_symtabs == null) {
_symtabs = new LinkedList();
}
++_freePos;
if (_freePos == _positions.length) {
grow();
}
if (injectBeforeCurrent) {
// move current value to the right
_types[_freePos] = _types[_freePos - 1];
_lengths[_freePos] = _lengths[_freePos - 1];
// set previous value to symbol table type
_types[_freePos - 1] = TID_SYMBOL_TABLE_PATCH;
_lengths[_freePos - 1] = 0;
} else {
// add symbol table to the next free position as a usual value
_types[_freePos] = TID_SYMBOL_TABLE_PATCH;
_lengths[_freePos] = 0;
}
// inject the symbol table to the current position on the top
_symtabs.add(st);
}
int getType() {
return _types[_freePos];
}
PatchedValues getParent() {
return _parent;
}
/** Start the patch for given @type at given @pos */
void startPatch(int type, int pos) {
++_freePos;
if (_freePos == _positions.length) {
grow();
}
_types[_freePos] = type;
_lengths[_freePos] = 0;
_positions[_freePos] = pos;
}
/** Set the field name length as high 32bits of @_lengths item */
void patchFieldName(int fieldNameLength) {
_lengths[_freePos] = ((long)fieldNameLength) << 32;
}
/**
* Set the value length as low 32bits of @_lengths item. The low 32bits will be
* summed with the given value (say, it's a list that is in struct)
* @param len
*/
void patchValue(int len) {
long memLen = (_lengths[_freePos] & 0xFFFFFFFF00000000L);
long curLen = (_lengths[_freePos] & 0x00000000FFFFFFFFL);
_lengths[_freePos] = memLen | (curLen + len);
}
/**
* End this patch. If there's a @_parent available, it's @_length will be summed
* with the length of current patched value
*/
void endPatch() {
if (_parent != null) {
int memberLen = (int)(_lengths[_freePos] >> 32);
int valueLen = (int)(_lengths[_freePos] & 0xFFFFFFFF);
int totalLen = memberLen + valueLen;
switch (_types[_freePos]) {
case TID_SYMBOL_TABLE_PATCH:
case TID_RAW:
break;
case TID_ANNOTATION_PATCH:
totalLen += IonBinary.lenVarUInt(valueLen);
break;
default:
// add the type if it's specified
++totalLen;
// add actual length of @totalLen :)
if (valueLen >= _Private_IonConstants.lnIsVarLen) {
totalLen += IonBinary.lenVarUInt(valueLen);
}
}
_parent.patchValue(totalLen);
}
}
// grow all related arrays
private void grow() {
int newSize = _positions.length * 2;
_types = growOne(_types, newSize);
_positions = growOne(_positions, newSize);
_lengths = growOne(_lengths, newSize);
}
// grow single array and return the result
static int[] growOne(int[] source, int newSize) {
int[] dest = new int[newSize];
System.arraycopy(source, 0, dest, 0, source.length);
return dest;
}
// grow single array and return the result
static long[] growOne(long[] source, int newSize) {
long[] dest = new long[newSize];
System.arraycopy(source, 0, dest, 0, source.length);
return dest;
}
}
// top-level patch
PatchedValues _patch = new PatchedValues();
private final static int TID_ANNOTATION_PATCH = _Private_IonConstants.tidDATAGRAM + 1;
private final static int TID_SYMBOL_TABLE_PATCH = _Private_IonConstants.tidDATAGRAM + 2;
private final static int TID_RAW = _Private_IonConstants.tidDATAGRAM + 3;
// private static final boolean _verbose_debug = false;
static final int UNKNOWN_LENGTH = -1;
BufferManager _manager;
IonBinary.Writer _writer;
/** Not null */
private final OutputStream _user_output_stream;
/**
* Do we {@link #flush()} after each top-level value?
* @see #closeValue()
*/
private final boolean _auto_flush;
boolean _in_struct;
/** Ensure we don't use a closed {@link #output} stream. */
private boolean _closed;
private final static int TID_FOR_SYMBOL_TABLE_PATCH = _Private_IonConstants.tidDATAGRAM + 1;
private final static int DEFAULT_PATCH_COUNT = 10;
private final static int DEFAULT_PATCH_DEPTH = 10;
private final static int NOT_A_SYMBOL_TABLE_IDX = -1;
// Patch:
// offset in data stream
// accumulated length -- combine w offset in long? offset:len (allows len+=more)
// type of data (should this be in the data stream?
// patched value's parent is struct flag (low nibble in data stream?)
//
// the patches are the accumulated list of patch points and are
// in position order (which is conveniently the order they are
// encountered and created).
int _patch_count = 0;
int [] _patch_lengths = new int[DEFAULT_PATCH_COUNT]; // TODO: should these be merged? (since array access is expensive)
int [] _patch_offsets = new int[DEFAULT_PATCH_COUNT]; // should patch lengths and patch offsets be longs?
int [] _patch_table_idx = new int[DEFAULT_PATCH_COUNT];
int [] _patch_types = new int[DEFAULT_PATCH_COUNT];
boolean [] _patch_in_struct = new boolean[DEFAULT_PATCH_COUNT];
// this is only loaded by the User writer, but it is read
// by the "get byte" operations and must be coordinated
// with the patch list that the system writer maintains here.
int _patch_symbol_table_count = 0;
SymbolTable[] _patch_symbol_tables = new SymbolTable[DEFAULT_PATCH_COUNT];
// the patch stack is the list of patch points that currently
// need updating. The value is the index into the patch arrays.
// As a value requiring patches is closed its patch idx is removed
// from the stack.
int _top;
int [] _patch_stack = new int[DEFAULT_PATCH_DEPTH];
/**
* This is the depth as seen by the user. Since there are cases where we
* don't push onto the patch stack and cases where we push non-user
* containers onto the patch stack we compute this separately during
* stepIn and stepOut.
*/
private int _user_depth;
/**
* @param out OutputStream the users output byte stream, if specified
* @param autoFlush when true the writer flushes to the output stream
* between top level values
* @param ensureInitialIvm when true, an initial IVM will be emitted even
* when the user doesn't explicitly write one. When false, an initial IVM
* won't be emitted unless the user does it. That can result in an invalid
* Ion stream if not used carefully.
* @throws NullPointerException if any parameter is null.
*/
IonWriterSystemBinary(SymbolTable defaultSystemSymtab,
OutputStream out,
boolean autoFlush,
boolean ensureInitialIvm)
{
super(defaultSystemSymtab,
(ensureInitialIvm ? InitialIvmHandling.ENSURE : null),
IvmMinimizing.ADJACENT);
out.getClass(); // Efficient null check
_user_output_stream = out;
// the buffer manager and writer
// are used to hold the buffered
// binary values pending flush().
_manager = new BufferManager();
_writer = _manager.openWriter();
_auto_flush = autoFlush;
}
/**
* Empty our buffers, assuming it is safe to do so.
* This is called by {@link #flush()} and {@link #finish()}.
*/
private void writeAllBufferedData()
throws IOException
{
writeBytes(_user_output_stream);
clearFieldName();
clearAnnotations();
_in_struct = false;
_patch_count = 0;
_patch_symbol_table_count = 0;
_top = 0;
try {
_writer.setPosition(0);
_writer.truncate();
}
catch (IOException e) {
throw new IonException(e);
}
}
@Override
public void finish() throws IOException
{
if (getDepth() != 0) {
throw new IllegalStateException(ERROR_FINISH_NOT_AT_TOP_LEVEL);
}
writeAllBufferedData();
super.finish();
}
final OutputStream getOutputStream()
{
return _user_output_stream;
}
public final boolean isInStruct()
{
return _in_struct;
}
private final boolean topInStruct() {
if (_top == 0) return false;
boolean in_struct = _patch_in_struct[_patch_stack[_top - 1]];
return in_struct;
}
protected final boolean atDatagramLevel()
{
return (topType() == _Private_IonConstants.tidDATAGRAM);
// return is_datagram;
}
@Override
public final int getDepth()
{
return _user_depth;
}
protected final IonType getContainer()
{
IonType type;
int tid = parentType();
switch (tid) {
case tidList:
type = IonType.LIST;
break;
case tidSexp:
type = IonType.SEXP;
break;
case tidStruct:
type = IonType.STRUCT;
break;
case tidDATAGRAM:
type = IonType.DATAGRAM;
break;
default:
throw new IonException("unexpected parent type "+tid+" does not represent a container");
}
return type;
}
@Override
final SymbolTable inject_local_symbol_table() throws IOException
{
SymbolTable symbols = super.inject_local_symbol_table();
PatchedValues top;
// find the parent
for (top = _patch; top.getParent() != null; top = top.getParent()) {}
// inject the symbol table; if @_patch is not the top element, then inject
// the symtab before this top-level value begins
super.startValue();
top.injectSymbolTable(symbols, _patch.getParent() != null);
super.endValue();
return symbols;
}
private final int topLength() {
return _patch_lengths[_patch_stack[_top - 1]];
}
private final int topType() {
if (_top == 0) return _Private_IonConstants.tidDATAGRAM;
return _patch_types[_patch_stack[_top - 1]];
}
private final int parentType() {
int ii = _top - 2;
while (ii >= 0) {
int type = _patch_types[_patch_stack[ii]];
if (type != _Private_IonConstants.tidTypedecl) return type;
ii--;
}
return _Private_IonConstants.tidDATAGRAM;
}
private final void startValue(int value_type)
throws IOException
{
super.startValue();
int[] sids = null;
int sid_count = annotationCount();
if (sid_count > 0) {
// prepare the SIDs of annotations before doing the patch as this may
// fail and leave the Writer in undefined state
sids = super.internAnnotationsAndGetSids();
_patch.startPatch(_Private_IonConstants.tidTypedecl, _writer.position());
} else {
_patch.startPatch(value_type, _writer.position());
}
// write field name
if (_in_struct) {
if (!isFieldNameSet()) {
throw new IllegalStateException(ERROR_MISSING_FIELD_NAME);
}
int sid = super.getFieldId();
if (sid < 0) {
throw new UnsupportedOperationException("symbol resolution must be handled by the user writer");
}
int fieldNameLength = _writer.writeVarUIntValue(sid, true);
_patch.patchFieldName(fieldNameLength);
clearFieldName();
}
// write annotations
if (sid_count > 0) {
_patch = _patch.addChild();
// add all annotations as if it's a value with type = -1
_patch.startPatch(TID_ANNOTATION_PATCH, _writer.position());
int len = 0;
for (int ii=0; ii
* The {@link OutputStream} spec is mum regarding the behavior of flush on
* a closed stream, so we shouldn't assume that our stream can handle that.
*/
public final void flush() throws IOException
{
if (! _closed)
{
if (atDatagramLevel() && ! hasAnnotations())
{
SymbolTable symtab = getSymbolTable();
if (symtab != null &&
symtab.isReadOnly() &&
symtab.isLocalTable())
{
// It's no longer possible to add more symbols to the local
// symtab, so we can safely write everything out.
writeAllBufferedData();
}
}
_user_output_stream.flush();
}
}
public final void close() throws IOException
{
if (! _closed) {
try
{
if (getDepth() == 0) {
finish();
}
}
finally
{
// Do this first so we are closed even if the call below throws.
_closed = true;
_user_output_stream.close();
}
}
}
@Override
void writeIonVersionMarkerAsIs(SymbolTable systemSymtab)
throws IOException
{
if (_user_depth != 0) {
throw new IllegalStateException("IVM not on top-level");
}
super.startValue();
_patch.startPatch(TID_RAW, _writer.position());
_patch.patchValue(4);
_writer.write(_Private_IonConstants.BINARY_VERSION_MARKER_1_0);
_patch.endPatch();
super.endValue();
}
@Override
void writeLocalSymtab(SymbolTable symbols)
throws IOException
{
// this method *should* be called when @_patch is a top-level value, but
// we cannot be sure, so try to find the top-level anyway
PatchedValues top;
// find the parent
for (top = _patch; top.getParent() != null; top = top.getParent()) {}
super.startValue();
top.injectSymbolTable(symbols, _patch.getParent() != null);
super.endValue();
super.writeLocalSymtab(symbols);
}
public final void stepIn(IonType containerType) throws IOException
{
int tid;
switch (containerType)
{
case LIST: tid = tidList; break;
case SEXP: tid = tidSexp; break;
case STRUCT: tid = tidStruct; break;
default:
throw new IllegalArgumentException();
}
startValue(tid);
_patch = _patch.addChild();
_in_struct = (tid == tidStruct);
++_user_depth;
}
public final void stepOut() throws IOException
{
if (_patch.getParent() == null) {
throw new IllegalStateException(IonMessages.CANNOT_STEP_OUT);
}
// container is over, getting out
_patch = _patch.getParent();
// now close current value
closeValue();
if (_patch.getParent() == null) {
_in_struct = false;
// we're on top-level
if (_auto_flush) {
flush();
}
} else {
_in_struct = (_patch.getParent().getType() == _Private_IonConstants.tidStruct);
}
--_user_depth;
}
public void writeNull(IonType type) throws IOException
{
int tid;
switch (type) {
case NULL: tid = _Private_IonConstants.tidNull; break;
case BOOL: tid = _Private_IonConstants.tidBoolean; break;
case INT: tid = _Private_IonConstants.tidPosInt; break;
case FLOAT: tid = _Private_IonConstants.tidFloat; break;
case DECIMAL: tid = _Private_IonConstants.tidDecimal; break;
case TIMESTAMP: tid = _Private_IonConstants.tidTimestamp; break;
case SYMBOL: tid = _Private_IonConstants.tidSymbol; break;
case STRING: tid = _Private_IonConstants.tidString; break;
case BLOB: tid = _Private_IonConstants.tidBlob; break;
case CLOB: tid = _Private_IonConstants.tidClob; break;
case SEXP: tid = _Private_IonConstants.tidSexp; break;
case LIST: tid = _Private_IonConstants.tidList; break;
case STRUCT: tid = _Private_IonConstants.tidStruct; break;
default:
throw new IllegalArgumentException("Invalid type: " + type);
}
startValue(TID_RAW);
_writer.write((tid << 4) | _Private_IonConstants.lnIsNull);
_patch.patchValue(1);
closeValue();
}
public void writeBool(boolean value) throws IOException
{
int ln = value ? _Private_IonConstants.lnBooleanTrue : _Private_IonConstants.lnBooleanFalse;
startValue(TID_RAW);
_writer.write((_Private_IonConstants.tidBoolean << 4) | ln);
_patch.patchValue(1);
closeValue();
}
public void writeInt(long value) throws IOException
{
int len;
if (value < 0) {
startValue(_Private_IonConstants.tidNegInt);
len = _writer.writeUIntValue(-value);
} else {
startValue(_Private_IonConstants.tidPosInt);
len = _writer.writeUIntValue(value);
}
_patch.patchValue(len);
closeValue();
}
public void writeInt(BigInteger value) throws IOException
{
if (value == null) {
writeNull(IonType.INT);
return;
}
boolean is_negative = (value.signum() < 0);
BigInteger positive = value;
if (is_negative) {
positive = value.negate();
}
int len = IonBinary.lenIonInt(positive);
startValue(is_negative ? _Private_IonConstants.tidNegInt : _Private_IonConstants.tidPosInt);
_writer.writeUIntValue(positive, len);
_patch.patchValue(len);
closeValue();
}
public void writeFloat(double value) throws IOException
{
int len = IonBinary.lenIonFloat(value);
startValue(_Private_IonConstants.tidFloat); // int's are always less than varlen long
len = _writer.writeFloatValue(value);
_patch.patchValue(len);
closeValue();
}
@Override
public void writeDecimal(BigDecimal value) throws IOException
{
if (value == null) {
writeNull(IonType.DECIMAL);
return;
}
startValue(_Private_IonConstants.tidDecimal);
int len = _writer.writeDecimalContent(value);
_patch.patchValue(len);
closeValue();
}
public void writeTimestamp(Timestamp value) throws IOException
{
if (value == null) {
writeNull(IonType.TIMESTAMP);
return;
}
startValue(_Private_IonConstants.tidTimestamp);
int len = _writer.writeTimestamp(value);
_patch.patchValue(len);
closeValue();
}
public void writeString(String value) throws IOException
{
if (value == null) {
writeNull(IonType.STRING);
return;
}
startValue(_Private_IonConstants.tidString);
int len = _writer.writeStringData(value);
_patch.patchValue(len);
closeValue();
}
@Override
void writeSymbolAsIs(int symbolId) throws IOException
{
startValue(_Private_IonConstants.tidSymbol);
int len = _writer.writeUIntValue(symbolId);
_patch.patchValue(len);
closeValue();
}
@Override
public void writeSymbolAsIs(String value) throws IOException
{
if (value == null) {
writeNull(IonType.SYMBOL);
return;
}
int sid = add_symbol(value);
writeSymbolAsIs(sid);
}
public void writeClob(byte[] value, int start, int len) throws IOException
{
if (value == null) {
writeNull(IonType.CLOB);
return;
}
if (start < 0 || len < 0 || start+len > value.length) {
throw new IllegalArgumentException("the start and len must be contained in the byte array");
}
startValue(_Private_IonConstants.tidClob);
_writer.write(value, start, len);
_patch.patchValue(len);
closeValue();
}
public void writeBlob(byte[] value, int start, int len) throws IOException
{
if (value == null) {
writeNull(IonType.BLOB);
return;
}
if (start < 0 || len < 0 || start+len > value.length) {
throw new IllegalArgumentException("the start and len must be contained in the byte array");
}
startValue(_Private_IonConstants.tidBlob);
_writer.write(value, start, len);
_patch.patchValue(len);
closeValue();
}
// just transfer the bytes into the current patch as 'proper' ion binary serialization
public void writeRaw(byte[] value, int start, int len) throws IOException
{
startValue(TID_RAW);
_writer.write(value, start, len);
_patch.patchValue(len);
closeValue();
}
public void writeBoolList(boolean[] values) throws IOException
{
stepIn(IonType.LIST);
for (boolean b : values) {
writeBool(b);
}
stepOut();
}
public void writeIntList(byte[] values) throws IOException
{
stepIn(IonType.LIST);
for (byte b : values) {
writeInt(b);
}
stepOut();
}
public void writeIntList(short[] values) throws IOException
{
stepIn(IonType.LIST);
for (short s : values) {
writeInt(s);
}
stepOut();
}
public void writeIntList(int[] values) throws IOException
{
stepIn(IonType.LIST);
for (int i : values) {
writeInt(i);
}
stepOut();
}
public void writeIntList(long[] values) throws IOException
{
stepIn(IonType.LIST);
for (long l : values) {
writeInt(l);
}
stepOut();
}
public void writeFloatList(float[] values) throws IOException
{
stepIn(IonType.LIST);
for (float f : values) {
writeFloat(f);
}
stepOut();
}
public void writeFloatList(double[] values) throws IOException
{
stepIn(IonType.LIST);
for (double d : values) {
writeFloat(d);
}
stepOut();
}
public void writeStringList(String[] values) throws IOException
{
stepIn(IonType.LIST);
for (String s : values) {
writeString(s);
}
stepOut();
}
// TODO make private after IonBinaryWriter is removed
/**
* Writes everything we've got into the output stream, performing all
* necessary patches along the way.
*
* This implements {@link com.amazon.ion.IonBinaryWriter#writeBytes(OutputStream)}
* via our subclass {@link IonWriterBinaryCompatibility.System}.
*/
int writeBytes(OutputStream userstream) throws IOException {
if (_patch.getParent() != null) {
throw new IllegalStateException("Tried to flush while not on top-level");
}
try {
BlockedByteInputStream datastream =
new BlockedByteInputStream(_manager.buffer());
int size = writeRecursive(datastream, userstream, _patch);
return size;
} finally {
_patch.reset();
}
}
int writeRecursive(BlockedByteInputStream datastream, OutputStream userstream, PatchedValues p) throws IOException {
int totalSize = 0;
for (int i = 0; i <= p._freePos; ++i) {
int type = p._types[i];
int pos = p._positions[i];
int fnlen = (int)(p._lengths[i] >> 32);
int vallen = (int)(p._lengths[i] & 0xFFFFFFFF);
if (p.getParent() == null) {
if (pos > totalSize) {
// write whatever data that we have in the datastream (eg external data)
datastream.writeTo(userstream, pos - totalSize);
totalSize = pos;
}
totalSize += fnlen + vallen;
}
// write member name
if (fnlen > 0) {
datastream.writeTo(userstream, fnlen);
}
switch (type) {
case TID_ANNOTATION_PATCH:
IonBinary.writeVarUInt(userstream, vallen);
datastream.writeTo(userstream, vallen);
break;
case TID_SYMBOL_TABLE_PATCH:
SymbolTable symtab = p._symtabs.remove();
if (!symtab.isSystemTable()) {
byte[] symtabBytes = reverseEncode(1024, symtab);
userstream.write(symtabBytes);
totalSize += symtabBytes.length;
}
break;
case TID_RAW:
datastream.writeTo(userstream, vallen);
break;
default:
// write type
if (vallen >= _Private_IonConstants.lnIsVarLen) {
int typeByte = (type << 4) | _Private_IonConstants.lnIsVarLen;
userstream.write(typeByte);
IonBinary.writeVarUInt(userstream, vallen);
} else {
int typeByte = (type << 4) | vallen;
userstream.write(typeByte);
}
switch (type) {
case _Private_IonConstants.tidList:
case _Private_IonConstants.tidSexp:
case _Private_IonConstants.tidStruct:
case _Private_IonConstants.tidTypedecl:
// write the container
assert p._children != null;
writeRecursive(datastream, userstream, p._children.remove());
break;
default:
datastream.writeTo(userstream, vallen);
}
}
}
return totalSize;
}
static class CountingStream extends OutputStream
{
private final OutputStream _wrapped;
private int _written;
CountingStream(OutputStream userstream) {
_wrapped = userstream;
}
public int getBytesWritten() {
return _written;
}
@Override
public void write(int b) throws IOException
{
_wrapped.write(b);
_written++;
}
@Override
public void write(byte[] bytes) throws IOException
{
_wrapped.write(bytes);
_written += bytes.length;
}
@Override
public void write(byte[] bytes, int off, int len) throws IOException
{
_wrapped.write(bytes, off, len);
_written += len;
}
}
protected int write_symbol_table(OutputStream userstream,
SymbolTable symtab) throws IOException
{
CountingStream cs = new CountingStream(userstream);
// TODO this is assuming the symtab needed here, broken for open content.
IonWriterSystemBinary writer =
new IonWriterSystemBinary(_default_system_symbol_table,
cs,
false /* autoflush */ ,
false /* ensureInitialIvm */);
symtab.writeTo(writer);
writer.finish();
int symtab_len = cs.getBytesWritten();
return symtab_len;
}
protected int XXX_get_pending_length_with_no_symbol_tables()
{
int buffer_length = _manager.buffer().size();
int patch_amount = 0;
for (int patch_idx = 0; patch_idx < _patch_count; patch_idx ++) {
// int vlen = _patch_list[patch_idx + IonBinaryWriter.POSITION_OFFSET];
int vlen = _patch_lengths[patch_idx];
if (vlen >= _Private_IonConstants.lnIsVarLen) {
int ln = IonBinary.lenVarUInt(vlen);
patch_amount += ln;
}
}
int symbol_table_length = 0;
int total_length = 0;
total_length += buffer_length
+ patch_amount
+ symbol_table_length;
return total_length;
}
}