/*
* Copyright © 2014 Cask Data, Inc.
*
* Licensed 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 co.cask.cdap.common.io;
import com.google.common.base.Charsets;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
/**
* An {@link Encoder} for binary-format data.
*/
public final class BinaryEncoder implements Encoder {
private final OutputStream output;
public BinaryEncoder(OutputStream output) {
this.output = output;
}
@Override
public Encoder writeNull() throws IOException {
// No-op
return this;
}
@Override
public Encoder writeBool(boolean b) throws IOException {
output.write(b ? 1 : 0);
return this;
}
@Override
public Encoder writeInt(int i) throws IOException {
// Compute the zig-zag value. First double the value and flip the bit if the input is negative.
int val = (i << 1) ^ (i >> 31);
if ((val & ~0x7f) != 0) {
output.write(0x80 | val & 0x7f);
val >>>= 7;
while (val > 0x7f) {
output.write(0x80 | val & 0x7f);
val >>>= 7;
}
}
output.write(val);
return this;
}
@Override
public Encoder writeLong(long l) throws IOException {
// Compute the zig-zag value. First double the value and flip the bit if the input is negative.
long val = (l << 1) ^ (l >> 63);
if ((val & ~0x7f) != 0) {
output.write((int) (0x80 | val & 0x7f));
val >>>= 7;
while (val > 0x7f) {
output.write((int) (0x80 | val & 0x7f));
val >>>= 7;
}
}
output.write((int) val);
return this;
}
@Override
public Encoder writeFloat(float f) throws IOException {
int bits = Float.floatToIntBits(f);
output.write(bits & 0xff);
output.write((bits >> 8) & 0xff);
output.write((bits >> 16) & 0xff);
output.write((bits >> 24) & 0xff);
return this;
}
@Override
public Encoder writeDouble(double d) throws IOException {
long bits = Double.doubleToLongBits(d);
int low = (int) bits;
int high = (int) (bits >> 32);
output.write(low & 0xff);
output.write((low >> 8) & 0xff);
output.write((low >> 16) & 0xff);
output.write((low >> 24) & 0xff);
output.write(high & 0xff);
output.write((high >> 8) & 0xff);
output.write((high >> 16) & 0xff);
output.write((high >> 24) & 0xff);
return this;
}
@Override
public Encoder writeString(String s) throws IOException {
return writeBytes(Charsets.UTF_8.encode(s));
}
@Override
public Encoder writeBytes(byte[] bytes) throws IOException {
return writeBytes(bytes, 0, bytes.length);
}
@Override
public Encoder writeBytes(byte[] bytes, int off, int len) throws IOException {
writeLong(len);
output.write(bytes, off, len);
return this;
}
@Override
public Encoder writeBytes(ByteBuffer buffer) throws IOException {
writeInt(buffer.remaining());
if (buffer.hasArray()) {
output.write(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
} else {
byte[] bytes = new byte[buffer.remaining()];
int pos = buffer.position();
buffer.get(bytes);
output.write(bytes);
buffer.position(pos);
}
return this;
}
}