com.amazon.ion.impl.OutputStreamFastAppendable 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.makeUnicodeScalar;
import com.amazon.ion.util._Private_FastAppendable;
import java.io.Closeable;
import java.io.Flushable;
import java.io.IOException;
import java.io.OutputStream;
/**
* Adapts an {@link OutputStream} to implement {@link _Private_FastAppendable}.
* This always outputs UTF-8!
*/
final class OutputStreamFastAppendable
implements _Private_FastAppendable, Closeable, Flushable
{
private static final int MAX_BYTES_LEN = 4096;
private final OutputStream _out;
/** Aggregates bytes so we can write to {@link #_out} in large batches. */
private final byte[] _byteBuffer;
/** Position in {@link #_byteBuffer} where we'll write the next byte. */
private int _pos;
OutputStreamFastAppendable(OutputStream out)
{
out.getClass(); // Efficient null check
_out = out;
_pos = 0;
_byteBuffer = new byte[MAX_BYTES_LEN];
}
// ------------------- FastAppendable Appendable Methods -------------------
public Appendable append(char c)
throws IOException
{
// Choose what method to use depending on type of character.
if (c < 0x80) {
appendAscii(c);
} else {
appendUtf16(c);
}
return this;
}
public Appendable append(CharSequence csq)
throws IOException
{
append(csq, 0, csq.length());
return this;
}
public Appendable append(CharSequence csq, int start, int end)
throws IOException
{
for (int ii = start; ii < end; ++ii) {
append(csq.charAt(ii));
}
return this;
}
public final void appendAscii(char c)
throws IOException
{
if (_pos == _byteBuffer.length) {
_out.write(_byteBuffer, 0, _pos);
_pos = 0;
}
assert c < 0x80;
_byteBuffer[_pos++] = (byte)c;
}
public final void appendAscii(CharSequence csq)
throws IOException
{
appendAscii(csq, 0, csq.length());
}
@SuppressWarnings("deprecation")
public final void appendAscii(CharSequence csq, int start, int end)
throws IOException
{
if (csq instanceof String) {
// Using deprecated String.getBytes intentionally, since it is
// correct behavior in this case, and much faster.
String str = (String)csq;
int len = end - start;
if (_pos + len < _byteBuffer.length) {
// put String bytes directly into buffer
str.getBytes(start, end, _byteBuffer, _pos);
_pos += len;
} else {
do {
// flush the buffer on every loop
_out.write(_byteBuffer, 0, _pos);
// check if we still need to split into chunks
_pos = (end - start > _byteBuffer.length
? _byteBuffer.length
: end - start);
str.getBytes(start, start + _pos, _byteBuffer, 0);
start += _pos;
} while (start < end);
}
} else {
for (int ii=start; ii < end; ii++) {
if (_pos == _byteBuffer.length) {
_out.write(_byteBuffer, 0, _pos);
_pos = 0;
}
char c = csq.charAt(ii);
assert c < 0x80;
_byteBuffer[_pos++] = (byte)c;
}
}
}
public final void appendUtf16(char c)
throws IOException
{
assert c >= 0x80;
if (_pos > _byteBuffer.length - 3) {
_out.write(_byteBuffer, 0, _pos);
_pos = 0;
}
if (c < 0x800) {
_byteBuffer[_pos++] = (byte)( 0xff & (0xC0 | ( c >> 6 )) );
_byteBuffer[_pos++] = (byte)( 0xff & (0x80 | ( c & 0x3F)) );
} else if (c < 0x10000) {
_byteBuffer[_pos++] = (byte)( 0xff & (0xE0 | ( c >> 12 )) );
_byteBuffer[_pos++] = (byte)( 0xff & (0x80 | ((c >> 6) & 0x3F)) );
_byteBuffer[_pos++] = (byte)( 0xff & (0x80 | ( c & 0x3F)) );
}
}
public final void appendUtf16Surrogate(char leadSurrogate,
char trailSurrogate)
throws IOException
{
// Here we must convert a UTF-16 surrogate pair to UTF-8 bytes.
int c = makeUnicodeScalar(leadSurrogate, trailSurrogate);
assert c >= 0x10000;
if (_pos > _byteBuffer.length - 4) {
_out.write(_byteBuffer, 0, _pos);
_pos = 0;
}
_byteBuffer[_pos++] = (byte)( 0xff & (0xF0 | ( c >> 18 )) );
_byteBuffer[_pos++] = (byte)( 0xff & (0x80 | ((c >> 12) & 0x3F)) );
_byteBuffer[_pos++] = (byte)( 0xff & (0x80 | ((c >> 6) & 0x3F)) );
_byteBuffer[_pos++] = (byte)( 0xff & (0x80 | ( c & 0x3F)) );
}
public final void flush()
throws IOException
{
if (_pos > 0) {
_out.write(_byteBuffer, 0, _pos);
_pos = 0;
}
_out.flush();
}
public final void close()
throws IOException
{
try
{
flush();
}
finally
{
_out.close();
}
}
}