Java tutorial
/* * 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.phoenix.schema; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.hadoop.io.Writable; import org.apache.hadoop.io.WritableUtils; import org.apache.phoenix.schema.types.PDataType; import org.apache.phoenix.util.SizedUtil; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; /** * * Simple flat schema over a byte array where fields may be any of {@link org.apache.phoenix.schema.types.PDataType}. * Optimized for positional access by index. * * * @since 0.1 */ public abstract class ValueSchema implements Writable { public static final int ESTIMATED_VARIABLE_LENGTH_SIZE = 10; private int[] fieldIndexByPosition; private List<Field> fields; private int estimatedLength; private boolean isFixedLength; private boolean isMaxLength; private int minNullable; // Only applicable for RowKeySchema (and only due to PHOENIX-2067), but // added here as this is where serialization is done (and we need to // maintain the same serialization shape for b/w compat). protected boolean rowKeyOrderOptimizable; public ValueSchema() { } protected ValueSchema(int minNullable, List<Field> fields) { this(minNullable, fields, true); } protected ValueSchema(int minNullable, List<Field> fields, boolean rowKeyOrderOptimizable) { init(minNullable, fields, rowKeyOrderOptimizable); } @Override public String toString() { return fields.toString(); } public int getEstimatedSize() { // Memory size of ValueSchema int count = fieldIndexByPosition.length; return SizedUtil.OBJECT_SIZE + SizedUtil.POINTER_SIZE + SizedUtil.INT_SIZE * (4 + count) + SizedUtil.ARRAY_SIZE + count * Field.ESTIMATED_SIZE + SizedUtil.sizeOfArrayList(count); } private void init(int minNullable, List<Field> fields, boolean rowKeyOrderOptimizable) { this.rowKeyOrderOptimizable = rowKeyOrderOptimizable; this.minNullable = minNullable; this.fields = ImmutableList.copyOf(fields); int estimatedLength = 0; boolean isMaxLength = true, isFixedLength = true; int positions = 0; for (Field field : fields) { int fieldEstLength = 0; PDataType type = field.getDataType(); if (type != null) { Integer byteSize = type.getByteSize(); if (type.isFixedWidth()) { fieldEstLength += field.getByteSize(); } else { isFixedLength = false; // Account for vint for length if not fixed if (byteSize == null) { isMaxLength = false; fieldEstLength += ESTIMATED_VARIABLE_LENGTH_SIZE; } else { fieldEstLength += WritableUtils.getVIntSize(byteSize); fieldEstLength = byteSize; } } } positions += field.getCount(); estimatedLength += fieldEstLength * field.getCount(); } fieldIndexByPosition = new int[positions]; for (int i = 0, j = 0; i < fields.size(); i++) { Field field = fields.get(i); Arrays.fill(fieldIndexByPosition, j, j + field.getCount(), i); j += field.getCount(); } this.isFixedLength = isFixedLength; this.isMaxLength = isMaxLength; this.estimatedLength = estimatedLength; } public int getFieldCount() { return fieldIndexByPosition.length; } public List<Field> getFields() { return fields; } /** * @return true if all types are fixed width */ public boolean isFixedLength() { return isFixedLength; } /** * @return true if {@link #getEstimatedValueLength()} returns the maximum length * of a serialized value for this schema */ public boolean isMaxLength() { return isMaxLength; } /** * @return estimated size in bytes of a serialized value for this schema */ public int getEstimatedValueLength() { return estimatedLength; } /** * Non-nullable fields packed to the left so that we do not need to store trailing nulls. * Knowing the minimum position of a nullable field enables this. * @return the minimum position of a nullable field */ public int getMinNullable() { return minNullable; } public static final class Field implements Writable, PDatum { @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + byteSize; result = prime * result + (type == null ? 0 : type.hashCode()); result = prime * result + sortOrder.hashCode(); result = prime * result + (isNullable ? 1231 : 1237); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Field other = (Field) obj; if (byteSize != other.byteSize) return false; if (sortOrder != other.sortOrder) return false; if (isNullable != other.isNullable) return false; if (type != other.type) return false; return true; } public static final int ESTIMATED_SIZE = SizedUtil.OBJECT_SIZE + SizedUtil.POINTER_SIZE * 2 + SizedUtil.INT_SIZE * 3; private int count; private PDataType type; private int byteSize = 0; private boolean isNullable; private SortOrder sortOrder; public Field() { } private Field(PDatum datum, boolean isNullable, int count, SortOrder sortOrder) { Preconditions.checkNotNull(sortOrder); this.type = datum.getDataType(); this.sortOrder = sortOrder; this.count = count; this.isNullable = isNullable; if (this.type != null && this.type.isFixedWidth() && this.type.getByteSize() == null) { if (datum.getMaxLength() != null) { this.byteSize = datum.getMaxLength(); } } } @Override public String toString() { return (count == 1 ? "" : count + " * ") + type + (byteSize == 0 ? "" : "(" + byteSize + ")") + (isNullable ? "" : " NOT NULL") + (sortOrder == SortOrder.ASC ? "" : " " + sortOrder); } private Field(Field field, int count) { this.type = field.getDataType(); this.byteSize = field.byteSize; this.count = count; this.sortOrder = SortOrder.getDefault(); } @Override public final SortOrder getSortOrder() { return sortOrder; } @Override public final PDataType getDataType() { return type; } @Override public final boolean isNullable() { return isNullable; } public final int getByteSize() { return type.getByteSize() == null ? byteSize : type.getByteSize(); } public final int getCount() { return count; } @Override public Integer getMaxLength() { return type.isFixedWidth() ? byteSize : null; } @Override public Integer getScale() { return null; } @Override public void readFields(DataInput input) throws IOException { // Encode isNullable in sign bit of type ordinal (offset by 1, since ordinal could be 0) int typeOrdinal = WritableUtils.readVInt(input); if (typeOrdinal < 0) { typeOrdinal *= -1; this.isNullable = true; } this.type = PDataType.values()[typeOrdinal - 1]; this.count = WritableUtils.readVInt(input); if (this.count < 0) { this.count *= -1; this.sortOrder = SortOrder.DESC; } else { this.sortOrder = SortOrder.ASC; } if (this.type.isFixedWidth() && this.type.getByteSize() == null) { this.byteSize = WritableUtils.readVInt(input); } } @Override public void write(DataOutput output) throws IOException { WritableUtils.writeVInt(output, (type.ordinal() + 1) * (this.isNullable ? -1 : 1)); WritableUtils.writeVInt(output, count * (sortOrder == SortOrder.ASC ? 1 : -1)); if (type.isFixedWidth() && type.getByteSize() == null) { WritableUtils.writeVInt(output, byteSize); } } } public abstract static class ValueSchemaBuilder { protected List<Field> fields = new ArrayList<Field>(); protected int nFields = Integer.MAX_VALUE; protected final int minNullable; public ValueSchemaBuilder(int minNullable) { this.minNullable = minNullable; } protected List<Field> buildFields() { List<Field> condensedFields = new ArrayList<Field>(fields.size()); for (int i = 0; i < Math.min(nFields, fields.size());) { Field field = fields.get(i); int count = 1; while (++i < fields.size() && field.equals(fields.get(i))) { count++; } condensedFields.add(count == 1 ? field : new Field(field, count)); } return condensedFields; } abstract public ValueSchema build(); public ValueSchemaBuilder setMaxFields(int nFields) { this.nFields = nFields; return this; } protected ValueSchemaBuilder addField(PDatum datum, boolean isNullable, SortOrder sortOrder) { if (fields.size() >= nFields) { throw new IllegalArgumentException("Adding too many fields to Schema (max " + nFields + ")"); } fields.add(new Field(datum, isNullable, 1, sortOrder)); return this; } } public int getEstimatedByteSize() { int size = 0; size += WritableUtils.getVIntSize(minNullable); size += WritableUtils.getVIntSize(fields.size()); size += fields.size() * 3; return size; } public Field getField(int position) { return fields.get(fieldIndexByPosition[position]); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + minNullable; for (Field field : fields) { result = prime * result + field.hashCode(); } return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; ValueSchema other = (ValueSchema) obj; if (minNullable != other.minNullable) return false; if (fields.size() != other.fields.size()) return false; for (int i = 0; i < fields.size(); i++) { if (!fields.get(i).equals(other.fields.get(i))) return false; } return true; } @Override public void readFields(DataInput in) throws IOException { int minNullable = WritableUtils.readVInt(in); int nFields = WritableUtils.readVInt(in); boolean rowKeyOrderOptimizable = false; if (nFields < 0) { rowKeyOrderOptimizable = true; nFields *= -1; } List<Field> fields = Lists.newArrayListWithExpectedSize(nFields); for (int i = 0; i < nFields; i++) { Field field = new Field(); field.readFields(in); fields.add(field); } init(minNullable, fields, rowKeyOrderOptimizable); } @Override public void write(DataOutput out) throws IOException { WritableUtils.writeVInt(out, minNullable); WritableUtils.writeVInt(out, fields.size() * (rowKeyOrderOptimizable ? -1 : 1)); for (int i = 0; i < fields.size(); i++) { fields.get(i).write(out); } } }