
com.hazelcast.internal.serialization.impl.compact.Schema Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2008-2024, Hazelcast, Inc. All Rights Reserved.
*
* 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 com.hazelcast.internal.serialization.impl.compact;
import com.hazelcast.internal.serialization.impl.compact.schema.SchemaDataSerializerHook;
import com.hazelcast.nio.ObjectDataInput;
import com.hazelcast.nio.ObjectDataOutput;
import com.hazelcast.nio.serialization.FieldKind;
import com.hazelcast.nio.serialization.IdentifiedDataSerializable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import static com.hazelcast.internal.serialization.impl.FieldKindBasedOperations.VARIABLE_SIZE;
import static com.hazelcast.internal.serialization.impl.FieldOperations.fieldOperations;
/**
* Represents the schema of a class.
* Consists of field definitions and the class name.
*/
public class Schema implements IdentifiedDataSerializable {
private String typeName;
private Map fieldsMap;
private List fields;
private int numberVarSizeFields;
private int fixedSizeFieldsLength;
private transient long schemaId;
public Schema() {
}
public Schema(String typeName, List fields) {
this.typeName = typeName;
this.fields = fields;
init();
}
private void init() {
// Construct the fields map for field lookups
Map fieldsMap = new HashMap<>(fields.size());
for (FieldDescriptor field : fields) {
fieldsMap.put(field.getFieldName(), field);
}
this.fieldsMap = fieldsMap;
// Sort the fields by the field name so that the field offsets/indexes
// can be set correctly.
fields.sort(Comparator.comparing(FieldDescriptor::getFieldName));
List fixedSizeFields = new ArrayList<>();
List booleanFields = new ArrayList<>();
List variableSizeFields = new ArrayList<>();
for (FieldDescriptor descriptor : fields) {
FieldKind fieldKind = descriptor.getKind();
if (fieldOperations(fieldKind).kindSizeInBytes() == VARIABLE_SIZE) {
variableSizeFields.add(descriptor);
} else {
if (FieldKind.BOOLEAN == fieldKind) {
booleanFields.add(descriptor);
} else {
fixedSizeFields.add(descriptor);
}
}
}
// Fixed size fields should be in descending order of size in bytes.
// For ties, the alphabetical order(ascending) of the field name will
// be used. Since, `fields` is sorted at this point, and the `sort`
// method is stable, only sorting by the size in bytes is enough for
// this invariant to hold.
fixedSizeFields.sort(
Comparator.comparingInt(
d -> fieldOperations(((FieldDescriptor) d).getKind()).kindSizeInBytes()
).reversed()
);
int offset = 0;
for (FieldDescriptor descriptor : fixedSizeFields) {
descriptor.setOffset(offset);
offset += fieldOperations(descriptor.getKind()).kindSizeInBytes();
}
int bitOffset = 0;
for (FieldDescriptor descriptor : booleanFields) {
descriptor.setOffset(offset);
descriptor.setBitOffset((byte) (bitOffset % Byte.SIZE));
bitOffset++;
if (bitOffset % Byte.SIZE == 0) {
offset += 1;
}
}
if (bitOffset % Byte.SIZE != 0) {
offset++;
}
fixedSizeFieldsLength = offset;
// Variable size fields should be in ascending alphabetical ordering
// of the field names
int index = 0;
for (FieldDescriptor descriptor : variableSizeFields) {
descriptor.setIndex(index++);
}
numberVarSizeFields = index;
schemaId = RabinFingerprint.fingerprint64(this);
}
/**
* The class name provided when building a schema
* In Java, when it is not configured explicitly, this falls back to
* fully qualified class name.
*
* @return name of the class
*/
public String getTypeName() {
return typeName;
}
public Collection getFields() {
return fields;
}
public Set getFieldNames() {
return fieldsMap.keySet();
}
public int getNumberOfVariableSizeFields() {
return numberVarSizeFields;
}
public int getFixedSizeFieldsLength() {
return fixedSizeFieldsLength;
}
public int getFieldCount() {
return fieldsMap.size();
}
public FieldDescriptor getField(String fieldName) {
return fieldsMap.get(fieldName);
}
public boolean hasField(String fieldName) {
return fieldsMap.containsKey(fieldName);
}
public long getSchemaId() {
return schemaId;
}
@Override
public String toString() {
return "Schema {"
+ " className = " + typeName
+ ", numberOfComplexFields = " + numberVarSizeFields
+ ", primitivesLength = " + fixedSizeFieldsLength
+ ", map = " + fieldsMap
+ '}';
}
@Override
public void writeData(ObjectDataOutput out) throws IOException {
out.writeString(typeName);
out.writeInt(fields.size());
for (FieldDescriptor descriptor : fields) {
out.writeString(descriptor.getFieldName());
out.writeInt(descriptor.getKind().getId());
}
}
@Override
public void readData(ObjectDataInput in) throws IOException {
typeName = in.readString();
int fieldCount = in.readInt();
fields = new ArrayList<>(fieldCount);
for (int i = 0; i < fieldCount; i++) {
String name = in.readString();
FieldKind kind = FieldKind.get(in.readInt());
FieldDescriptor descriptor = new FieldDescriptor(name, kind);
fields.add(descriptor);
}
init();
}
public static List readSchemas(DataInput in) throws IOException {
int schemaCount = in.readInt();
List schemas = new ArrayList<>(schemaCount);
for (int i = 0; i < schemaCount; i++) {
String typeName = in.readUTF();
int fieldCount = in.readInt();
List fields = new ArrayList<>(fieldCount);
for (int j = 0; j < fieldCount; j++) {
String name = in.readUTF();
FieldKind kind = FieldKind.get(in.readInt());
FieldDescriptor descriptor = new FieldDescriptor(name, kind);
fields.add(descriptor);
}
var schema = new Schema(typeName, fields);
schemas.add(schema);
}
return schemas;
}
public static void writeSchemas(DataOutput out, Collection schemas) throws IOException {
out.writeInt(schemas.size());
for (Schema schema : schemas) {
out.writeUTF(schema.getTypeName());
out.writeInt(schema.getFieldCount());
for (FieldDescriptor descriptor : schema.getFields()) {
out.writeUTF(descriptor.getFieldName());
out.writeInt(descriptor.getKind().getId());
}
}
}
@Override
public int getFactoryId() {
return SchemaDataSerializerHook.F_ID;
}
@Override
public int getClassId() {
return SchemaDataSerializerHook.SCHEMA;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Schema schema = (Schema) o;
return numberVarSizeFields == schema.numberVarSizeFields
&& fixedSizeFieldsLength == schema.fixedSizeFieldsLength
&& schemaId == schema.schemaId
&& Objects.equals(typeName, schema.typeName)
&& Objects.equals(fields, schema.fields)
&& Objects.equals(fieldsMap, schema.fieldsMap);
}
@Override
public int hashCode() {
return (int) schemaId;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy