/** * 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.hadoop.hive.serde2.columnar; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import org.apache.hadoop.io.Writable; import org.apache.hadoop.io.WritableComparator; import org.apache.hadoop.io.WritableFactories; import org.apache.hadoop.io.WritableFactory; /** * <tt>BytesRefWritable</tt> referenced a section of byte array. It can be used * to avoid unnecessary byte copy. */ public class BytesRefWritable implements Writable, Comparable<BytesRefWritable> { private static final byte[] EMPTY_BYTES = new byte[0]; public static BytesRefWritable ZeroBytesRefWritable = new BytesRefWritable(); int start = 0; int length = 0; byte[] bytes = null; LazyDecompressionCallback lazyDecompressObj; /** * Create a zero-size bytes. */ public BytesRefWritable() { this(EMPTY_BYTES); } /** * Create a BytesRefWritable with <tt>length</tt> bytes. */ public BytesRefWritable(int length) { assert length > 0; this.length = length; bytes = new byte[this.length]; start = 0; } /** * Create a BytesRefWritable referenced to the given bytes. */ public BytesRefWritable(byte[] bytes) { this.bytes = bytes; length = bytes.length; start = 0; } /** * Create a BytesRefWritable referenced to one section of the given bytes. The * section is determined by argument <tt>offset</tt> and <tt>len</tt>. */ public BytesRefWritable(byte[] data, int offset, int len) { bytes = data; start = offset; length = len; } /** * Create a BytesRefWritable referenced to one section of the given bytes. The * argument <tt>lazyDecompressData</tt> refers to a LazyDecompressionCallback * object. The arguments <tt>offset</tt> and <tt>len</tt> are referred to * uncompressed bytes of <tt>lazyDecompressData</tt>. Use <tt>offset</tt> and * <tt>len</tt> after uncompressing the data. */ public BytesRefWritable(LazyDecompressionCallback lazyDecompressData, int offset, int len) { lazyDecompressObj = lazyDecompressData; start = offset; length = len; } private void lazyDecompress() throws IOException { if (bytes == null && lazyDecompressObj != null) { bytes = lazyDecompressObj.decompress(); } } /** * Returns a copy of the underlying bytes referenced by this instance. * * @return a new copied byte array * @throws IOException */ public byte[] getBytesCopy() throws IOException { lazyDecompress(); byte[] bb = new byte[length]; System.arraycopy(bytes, start, bb, 0, length); return bb; } /** * Returns the underlying bytes. * * @throws IOException */ public byte[] getData() throws IOException { lazyDecompress(); return bytes; } /** * readFields() will corrupt the array. So use the set method whenever * possible. * * @see #readFields(DataInput) */ public void set(byte[] newData, int offset, int len) { bytes = newData; start = offset; length = len; lazyDecompressObj = null; } /** * readFields() will corrupt the array. So use the set method whenever * possible. * * @see #readFields(DataInput) */ public void set(LazyDecompressionCallback newData, int offset, int len) { bytes = null; start = offset; length = len; lazyDecompressObj = newData; } public void writeDataTo(DataOutput out) throws IOException { lazyDecompress(); out.write(bytes, start, length); } /** * Always reuse the bytes array if length of bytes array is equal or greater * to the current record, otherwise create a new one. readFields will corrupt * the array. Please use set() whenever possible. * * @see #set(byte[], int, int) */ public void readFields(DataInput in) throws IOException { int len = in.readInt(); if (len > bytes.length) { bytes = new byte[len]; } start = 0; length = len; in.readFully(bytes, start, length); } /** {@inheritDoc} */ public void write(DataOutput out) throws IOException { lazyDecompress(); out.writeInt(length); out.write(bytes, start, length); } /** {@inheritDoc} */ @Override public int hashCode() { return super.hashCode(); } /** {@inheritDoc} */ @Override public String toString() { StringBuilder sb = new StringBuilder(3 * length); for (int idx = start; idx < length; idx++) { // if not the first, put a blank separator in if (idx != 0) { sb.append(' '); } String num = Integer.toHexString(0xff & bytes[idx]); // if it is only one digit, add a leading 0. if (num.length() < 2) { sb.append('0'); } sb.append(num); } return sb.toString(); } /** {@inheritDoc} */ @Override public int compareTo(BytesRefWritable other) { if (other == null) { throw new IllegalArgumentException("Argument can not be null."); } if (this == other) { return 0; } try { return WritableComparator.compareBytes(getData(), start, getLength(), other.getData(), other.start, other.getLength()); } catch (IOException e) { throw new RuntimeException(e); } } /** {@inheritDoc} */ @Override public boolean equals(Object right_obj) { if (right_obj == null || !(right_obj instanceof BytesRefWritable)) { return false; } return compareTo((BytesRefWritable) right_obj) == 0; } static { WritableFactories.setFactory(BytesRefWritable.class, new WritableFactory() { @Override public Writable newInstance() { return new BytesRefWritable(); } }); } public int getLength() { return length; } public int getStart() { return start; } }