/**
* 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.hbase.types;
import org.apache.hadoop.hbase.classification.InterfaceAudience;
import org.apache.hadoop.hbase.util.Order;
import org.apache.hadoop.hbase.util.PositionedByteRange;
import org.apache.hadoop.hbase.util.SimplePositionedMutableByteRange;
/**
* Wraps an existing {@link DataType} implementation as a fixed-length
* version of itself. This has the useful side-effect of turning an existing
* {@link DataType} which is not {@code skippable} into a {@code skippable}
* variant.
*/
@InterfaceAudience.Public
public class FixedLengthWrapper<T> implements DataType<T> {
protected final DataType<T> base;
protected final int length;
/**
* Create a fixed-length version of the {@code wrapped}.
* @param base the {@link DataType} to restrict to a fixed length.
* @param length the maximum length (in bytes) for encoded values.
*/
public FixedLengthWrapper(DataType<T> base, int length) {
this.base = base;
this.length = length;
}
/**
* Retrieve the maximum length (in bytes) of encoded values.
*/
public int getLength() { return length; }
@Override
public boolean isOrderPreserving() { return base.isOrderPreserving(); }
@Override
public Order getOrder() { return base.getOrder(); }
@Override
public boolean isNullable() { return base.isNullable(); }
@Override
public boolean isSkippable() { return true; }
@Override
public int encodedLength(T val) { return length; }
@Override
public Class<T> encodedClass() { return base.encodedClass(); }
@Override
public int skip(PositionedByteRange src) {
src.setPosition(src.getPosition() + this.length);
return this.length;
}
@Override
public T decode(PositionedByteRange src) {
if (src.getRemaining() < length) {
throw new IllegalArgumentException("Not enough buffer remaining. src.offset: "
+ src.getOffset() + " src.length: " + src.getLength() + " src.position: "
+ src.getPosition() + " max length: " + length);
}
// create a copy range limited to length bytes. boo.
PositionedByteRange b = new SimplePositionedMutableByteRange(length);
src.get(b.getBytes());
return base.decode(b);
}
@Override
public int encode(PositionedByteRange dst, T val) {
if (dst.getRemaining() < length) {
throw new IllegalArgumentException("Not enough buffer remaining. dst.offset: "
+ dst.getOffset() + " dst.length: " + dst.getLength() + " dst.position: "
+ dst.getPosition() + " max length: " + length);
}
int written = base.encode(dst, val);
if (written > length) {
throw new IllegalArgumentException("Length of encoded value (" + written
+ ") exceeds max length (" + length + ").");
}
// TODO: is the zero-padding appropriate?
for (; written < length; written++) { dst.put((byte) 0x00); }
return written;
}
}