blob: 10aefa6c098f5f362ed2e5128bf16a8c5714fb60 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License 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 org.apache.cassandra.io.util;
import java.io.DataOutput;
import java.io.IOException;
import java.io.UTFDataFormatException;
import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;
import org.apache.cassandra.utils.memory.MemoryUtil;
import com.google.common.base.Function;
/**
* Base class for DataOutput implementations that does not have an optimized implementations of Plus methods
* and does no buffering.
* <p>
* Unlike BufferedDataOutputStreamPlus this is capable of operating as an unbuffered output stream.
* Currently necessary because SequentialWriter implements its own buffering along with mark/reset/truncate.
* </p>
*/
public abstract class UnbufferedDataOutputStreamPlus extends DataOutputStreamPlus
{
private static final byte[] zeroBytes = new byte[2];
protected UnbufferedDataOutputStreamPlus()
{
super();
}
protected UnbufferedDataOutputStreamPlus(WritableByteChannel channel)
{
super(channel);
}
/*
!! DataOutput methods below are copied from the implementation in Apache Harmony RandomAccessFile.
*/
/**
* Writes the entire contents of the byte array <code>buffer</code> to
* this RandomAccessFile starting at the current file pointer.
*
* @param buffer the buffer to be written.
* @throws IOException If an error occurs trying to write to this RandomAccessFile.
*/
public void write(byte[] buffer) throws IOException
{
write(buffer, 0, buffer.length);
}
/**
* Writes <code>count</code> bytes from the byte array <code>buffer</code>
* starting at <code>offset</code> to this RandomAccessFile starting at
* the current file pointer..
*
* @param buffer the bytes to be written
* @param offset offset in buffer to get bytes
* @param count number of bytes in buffer to write
* @throws IOException If an error occurs attempting to write to this
* RandomAccessFile.
* @throws IndexOutOfBoundsException If offset or count are outside of bounds.
*/
public abstract void write(byte[] buffer, int offset, int count) throws IOException;
/**
* Writes the specified byte <code>oneByte</code> to this RandomAccessFile
* starting at the current file pointer. Only the low order byte of
* <code>oneByte</code> is written.
*
* @param oneByte the byte to be written
* @throws IOException If an error occurs attempting to write to this
* RandomAccessFile.
*/
public abstract void write(int oneByte) throws IOException;
/**
* Writes a boolean to this output stream.
*
* @param val the boolean value to write to the OutputStream
* @throws IOException If an error occurs attempting to write to this
* DataOutputStream.
*/
public final void writeBoolean(boolean val) throws IOException
{
write(val ? 1 : 0);
}
/**
* Writes a 8-bit byte to this output stream.
*
* @param val the byte value to write to the OutputStream
* @throws java.io.IOException If an error occurs attempting to write to this
* DataOutputStream.
*/
public final void writeByte(int val) throws IOException
{
write(val & 0xFF);
}
/**
* Writes the low order 8-bit bytes from a String to this output stream.
*
* @param str the String containing the bytes to write to the OutputStream
* @throws IOException If an error occurs attempting to write to this
* DataOutputStream.
*/
public final void writeBytes(String str) throws IOException
{
byte bytes[] = new byte[str.length()];
for (int index = 0; index < str.length(); index++)
{
bytes[index] = (byte) (str.charAt(index) & 0xFF);
}
write(bytes);
}
/**
* Writes the specified 16-bit character to the OutputStream. Only the lower
* 2 bytes are written with the higher of the 2 bytes written first. This
* represents the Unicode value of val.
*
* @param val the character to be written
* @throws IOException If an error occurs attempting to write to this
* DataOutputStream.
*/
public final void writeChar(int val) throws IOException
{
write((val >>> 8) & 0xFF);
write((val >>> 0) & 0xFF);
}
/**
* Writes the specified 16-bit characters contained in str to the
* OutputStream. Only the lower 2 bytes of each character are written with
* the higher of the 2 bytes written first. This represents the Unicode
* value of each character in str.
*
* @param str the String whose characters are to be written.
* @throws IOException If an error occurs attempting to write to this
* DataOutputStream.
*/
public final void writeChars(String str) throws IOException
{
byte newBytes[] = new byte[str.length() * 2];
for (int index = 0; index < str.length(); index++)
{
int newIndex = index == 0 ? index : index * 2;
newBytes[newIndex] = (byte) ((str.charAt(index) >> 8) & 0xFF);
newBytes[newIndex + 1] = (byte) (str.charAt(index) & 0xFF);
}
write(newBytes);
}
/**
* Writes a 64-bit double to this output stream. The resulting output is the
* 8 bytes resulting from calling Double.doubleToLongBits().
*
* @param val the double to be written.
* @throws IOException If an error occurs attempting to write to this
* DataOutputStream.
*/
public final void writeDouble(double val) throws IOException
{
writeLong(Double.doubleToLongBits(val));
}
/**
* Writes a 32-bit float to this output stream. The resulting output is the
* 4 bytes resulting from calling Float.floatToIntBits().
*
* @param val the float to be written.
* @throws IOException If an error occurs attempting to write to this
* DataOutputStream.
*/
public final void writeFloat(float val) throws IOException
{
writeInt(Float.floatToIntBits(val));
}
/**
* Writes a 32-bit int to this output stream. The resulting output is the 4
* bytes, highest order first, of val.
*
* @param val the int to be written.
* @throws IOException If an error occurs attempting to write to this
* DataOutputStream.
*/
public void writeInt(int val) throws IOException
{
write((val >>> 24) & 0xFF);
write((val >>> 16) & 0xFF);
write((val >>> 8) & 0xFF);
write((val >>> 0) & 0xFF);
}
/**
* Writes a 64-bit long to this output stream. The resulting output is the 8
* bytes, highest order first, of val.
*
* @param val the long to be written.
* @throws IOException If an error occurs attempting to write to this
* DataOutputStream.
*/
public void writeLong(long val) throws IOException
{
write((int) (val >>> 56) & 0xFF);
write((int) (val >>> 48) & 0xFF);
write((int) (val >>> 40) & 0xFF);
write((int) (val >>> 32) & 0xFF);
write((int) (val >>> 24) & 0xFF);
write((int) (val >>> 16) & 0xFF);
write((int) (val >>> 8) & 0xFF);
write((int) (val >>> 0) & 0xFF);
}
/**
* Writes the specified 16-bit short to the OutputStream. Only the lower 2
* bytes are written with the higher of the 2 bytes written first.
*
* @param val the short to be written
* @throws IOException If an error occurs attempting to write to this
* DataOutputStream.
*/
public void writeShort(int val) throws IOException
{
writeChar(val);
}
/**
* Writes the specified String out in UTF format to the provided DataOutput
*
* @param str the String to be written in UTF format.
* @param out the DataOutput to write the UTF encoded string to
* @throws IOException If an error occurs attempting to write to this
* DataOutputStream.
*/
public static void writeUTF(String str, DataOutput out) throws IOException
{
int length = str.length();
if (length == 0)
{
out.write(zeroBytes);
return;
}
int utfCount = 0;
int maxSize = 2;
for (int i = 0 ; i < length ; i++)
{
int ch = str.charAt(i);
if ((ch > 0) & (ch <= 127))
utfCount += 1;
else if (ch <= 2047)
utfCount += 2;
else
utfCount += maxSize = 3;
}
if (utfCount > 65535)
throw new UTFDataFormatException(); //$NON-NLS-1$
byte[] utfBytes = retrieveTemporaryBuffer(utfCount + 2);
int bufferLength = utfBytes.length;
if (utfCount == length)
{
utfBytes[0] = (byte) (utfCount >> 8);
utfBytes[1] = (byte) utfCount;
int firstIndex = 2;
for (int offset = 0 ; offset < length ; offset += bufferLength)
{
int runLength = Math.min(bufferLength - firstIndex, length - offset) + firstIndex;
offset -= firstIndex;
for (int i = firstIndex ; i < runLength; i++)
utfBytes[i] = (byte) str.charAt(offset + i);
out.write(utfBytes, 0, runLength);
firstIndex = 0;
}
}
else
{
int utfIndex = 2;
int offset = 0;
utfBytes[0] = (byte) (utfCount >> 8);
utfBytes[1] = (byte) utfCount;
while (length > 0)
{
int charRunLength = (utfBytes.length - utfIndex) / maxSize;
if (charRunLength < 128 && charRunLength < length)
{
out.write(utfBytes, 0, utfIndex);
utfIndex = 0;
}
if (charRunLength > length)
charRunLength = length;
for (int i = 0 ; i < charRunLength ; i++)
{
char ch = str.charAt(offset + i);
if ((ch > 0) & (ch <= 127))
{
utfBytes[utfIndex++] = (byte) ch;
}
else if (ch <= 2047)
{
utfBytes[utfIndex++] = (byte) (0xc0 | (0x1f & (ch >> 6)));
utfBytes[utfIndex++] = (byte) (0x80 | (0x3f & ch));
}
else
{
utfBytes[utfIndex++] = (byte) (0xe0 | (0x0f & (ch >> 12)));
utfBytes[utfIndex++] = (byte) (0x80 | (0x3f & (ch >> 6)));
utfBytes[utfIndex++] = (byte) (0x80 | (0x3f & ch));
}
}
offset += charRunLength;
length -= charRunLength;
}
out.write(utfBytes, 0, utfIndex);
}
}
/**
* Writes the specified String out in UTF format.
*
* @param str the String to be written in UTF format.
* @throws IOException If an error occurs attempting to write to this
* DataOutputStream.
*/
public final void writeUTF(String str) throws IOException
{
writeUTF(str, this);
}
// ByteBuffer to use for defensive copies
private final ByteBuffer hollowBufferD = MemoryUtil.getHollowDirectByteBuffer();
@Override
public void write(ByteBuffer buf) throws IOException
{
if (buf.hasArray())
{
write(buf.array(), buf.arrayOffset() + buf.position(), buf.remaining());
}
else
{
assert buf.isDirect();
MemoryUtil.duplicateDirectByteBuffer(buf, hollowBufferD);
while (hollowBufferD.hasRemaining())
channel.write(hollowBufferD);
}
}
public void write(Memory memory, long offset, long length) throws IOException
{
for (ByteBuffer buffer : memory.asByteBuffers(offset, length))
write(buffer);
}
@Override
public <R> R applyToChannel(Function<WritableByteChannel, R> f) throws IOException
{
return f.apply(channel);
}
}