From 98b0b3f17ab14c8914d2bdc2064cd2849b33639f Mon Sep 17 00:00:00 2001 From: Eduardo Ramirez Date: Tue, 3 Sep 2024 23:22:29 -0700 Subject: [PATCH] chore: make FlatRecordTraversalNode concurrent safe --- .../flatrecords/FlatRecordOrdinalReader.java | 342 ++++++++++++++++++ .../FlatRecordTraversalListNode.java | 68 ++-- .../traversal/FlatRecordTraversalMapNode.java | 104 ++---- .../traversal/FlatRecordTraversalNode.java | 30 +- .../FlatRecordTraversalObjectNode.java | 183 +++------- ...FlatRecordTraversalObjectNodeEquality.java | 4 +- .../traversal/FlatRecordTraversalSetNode.java | 64 ++-- 7 files changed, 480 insertions(+), 315 deletions(-) create mode 100644 hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/flatrecords/FlatRecordOrdinalReader.java diff --git a/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/flatrecords/FlatRecordOrdinalReader.java b/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/flatrecords/FlatRecordOrdinalReader.java new file mode 100644 index 000000000..f512f31e0 --- /dev/null +++ b/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/flatrecords/FlatRecordOrdinalReader.java @@ -0,0 +1,342 @@ +package com.netflix.hollow.core.write.objectmapper.flatrecords; + +import com.netflix.hollow.core.memory.encoding.VarInt; +import com.netflix.hollow.core.memory.encoding.ZigZag; +import com.netflix.hollow.core.schema.HollowObjectSchema; +import com.netflix.hollow.core.schema.HollowSchema; +import com.netflix.hollow.core.util.IntList; +import com.netflix.hollow.core.write.HollowObjectWriteRecord; + +public class FlatRecordOrdinalReader { + private final FlatRecord record; + private final IntList ordinalOffsets = new IntList(); + + public FlatRecordOrdinalReader(FlatRecord record) { + this.record = record; + populateOrdinalOffset(); + } + + private void populateOrdinalOffset() { + int offset = record.dataStartByte; + while (offset < record.dataEndByte) { + ordinalOffsets.add(offset); + offset += sizeOfOrdinal(ordinalOffsets.size() - 1); + } + } + + private int getOrdinalOffset(int ordinal) { + return ordinalOffsets.get(ordinal); + } + + public int getOrdinalCount() { + return ordinalOffsets.size(); + } + + public HollowSchema readSchema(int ordinal) { + int schemaId = VarInt.readVInt(record.data, getOrdinalOffset(ordinal)); + return record.schemaIdMapper.getSchema(schemaId); + } + + public int readSize(int ordinal) { + int offset = getOrdinalOffset(ordinal); + + int schemaId = VarInt.readVInt(record.data, offset); + offset += VarInt.sizeOfVInt(schemaId); + + HollowSchema schema = record.schemaIdMapper.getSchema(schemaId); + if (schema.getSchemaType() != HollowSchema.SchemaType.LIST && + schema.getSchemaType() != HollowSchema.SchemaType.SET && + schema.getSchemaType() != HollowSchema.SchemaType.MAP) { + throw new IllegalArgumentException(String.format("Ordinal %d is not a LIST, SET, or MAP type (found %s)", ordinal, schema.getSchemaType())); + } + + return VarInt.readVInt(record.data, offset); + } + + public void readListElementsInto(int ordinal, int[] elements) { + int offset = getOrdinalOffset(ordinal); + + int schemaId = VarInt.readVInt(record.data, offset); + offset += VarInt.sizeOfVInt(schemaId); + + HollowSchema schema = record.schemaIdMapper.getSchema(schemaId); + if (schema.getSchemaType() != HollowSchema.SchemaType.LIST) { + throw new IllegalArgumentException(String.format("Ordinal %d is not a LIST type (found %s)", ordinal, schema.getSchemaType())); + } + + int size = VarInt.readVInt(record.data, offset); + offset += VarInt.sizeOfVInt(size); + + for (int i = 0; i < size; i++) { + elements[i] = VarInt.readVInt(record.data, offset); + offset += VarInt.sizeOfVInt(elements[i]); + } + } + + public void readSetElementsInto(int ordinal, int[] elements) { + int offset = getOrdinalOffset(ordinal); + + int schemaId = VarInt.readVInt(record.data, offset); + offset += VarInt.sizeOfVInt(schemaId); + + HollowSchema schema = record.schemaIdMapper.getSchema(schemaId); + if (schema.getSchemaType() != HollowSchema.SchemaType.SET) { + throw new IllegalArgumentException(String.format("Ordinal %d is not a SET type (found %s)", ordinal, schema.getSchemaType())); + } + + int size = VarInt.readVInt(record.data, offset); + offset += VarInt.sizeOfVInt(size); + + int elementOrdinal = 0; + for (int i = 0; i < size; i++) { + int elementOrdinalDelta = VarInt.readVInt(record.data, offset); + offset += VarInt.sizeOfVInt(elementOrdinalDelta); + elementOrdinal += elementOrdinalDelta; + elements[i] = elementOrdinal; + } + } + + public void readMapElementsInto(int ordinal, int[] keys, int[] values) { + int offset = getOrdinalOffset(ordinal); + + int schemaId = VarInt.readVInt(record.data, offset); + offset += VarInt.sizeOfVInt(schemaId); + + HollowSchema schema = record.schemaIdMapper.getSchema(schemaId); + if (schema.getSchemaType() != HollowSchema.SchemaType.MAP) { + throw new IllegalArgumentException(String.format("Ordinal %d is not a MAP type (found %s)", ordinal, schema.getSchemaType())); + } + + int size = VarInt.readVInt(record.data, offset); + offset += VarInt.sizeOfVInt(size); + + int keyOrdinal = 0; + for (int i = 0; i < size; i++) { + int keyOrdinalDelta = VarInt.readVInt(record.data, offset); + offset += VarInt.sizeOfVInt(keyOrdinalDelta); + keyOrdinal += keyOrdinalDelta; + keys[i] = keyOrdinal; + values[i] = VarInt.readVInt(record.data, offset); + offset += VarInt.sizeOfVInt(values[i]); + } + } + + public int readFieldReference(int ordinal, String field) { + int offset = skipToField(ordinal, HollowObjectSchema.FieldType.REFERENCE, field); + if (offset == -1) { + return -1; + } + + if (VarInt.readVNull(record.data, offset)) { + return -1; + } + + return VarInt.readVInt(record.data, offset); + } + + public Boolean readFieldBoolean(int ordinal, String field) { + int offset = skipToField(ordinal, HollowObjectSchema.FieldType.BOOLEAN, field); + if (offset == -1) { + return null; + } + + if (VarInt.readVNull(record.data, offset)) { + return null; + } + + int value = record.data.get(offset); + return value == 1 ? Boolean.TRUE : Boolean.FALSE; + } + + public int readFieldInt(int ordinal, String field) { + int offset = skipToField(ordinal, HollowObjectSchema.FieldType.INT, field); + if (offset == -1) { + return Integer.MIN_VALUE; + } + + if (VarInt.readVNull(record.data, offset)) { + return Integer.MIN_VALUE; + } + + int value = VarInt.readVInt(record.data, offset); + return ZigZag.decodeInt(value); + } + + public long readFieldLong(int ordinal, String field) { + int offset = skipToField(ordinal, HollowObjectSchema.FieldType.LONG, field); + if (offset == -1) { + return Long.MIN_VALUE; + } + + if (VarInt.readVNull(record.data, offset)) { + return Long.MIN_VALUE; + } + + long value = VarInt.readVLong(record.data, offset); + return ZigZag.decodeLong(value); + } + + public float readFieldFloat(int ordinal, String field) { + int offset = skipToField(ordinal, HollowObjectSchema.FieldType.FLOAT, field); + if (offset == -1) { + return Float.NaN; + } + + int value = record.data.readIntBits(offset); + if (value == HollowObjectWriteRecord.NULL_FLOAT_BITS) { + return Float.NaN; + } + + return Float.intBitsToFloat(value); + } + + public double readFieldDouble(int ordinal, String field) { + int offset = skipToField(ordinal, HollowObjectSchema.FieldType.DOUBLE, field); + if (offset == -1) { + return Double.NaN; + } + + long value = record.data.readLongBits(offset); + if (value == HollowObjectWriteRecord.NULL_DOUBLE_BITS) { + return Double.NaN; + } + + return Double.longBitsToDouble(value); + } + + public String readFieldString(int ordinal, String field) { + int offset = skipToField(ordinal, HollowObjectSchema.FieldType.STRING, field); + if (offset == -1) { + return null; + } + + if (VarInt.readVNull(record.data, offset)) { + return null; + } + + int length = VarInt.readVInt(record.data, offset); + offset += VarInt.sizeOfVInt(length); + + int cLength = VarInt.countVarIntsInRange(record.data, offset, length); + char[] s = new char[cLength]; + for (int i = 0; i < cLength; i++) { + int charValue = VarInt.readVInt(record.data, offset); + s[i] = (char) charValue; + offset += VarInt.sizeOfVInt(charValue); + } + + return new String(s); + } + + public byte[] readFieldBytes(int ordinal, String field) { + int offset = skipToField(ordinal, HollowObjectSchema.FieldType.BYTES, field); + if (offset == -1) { + return null; + } + + if (VarInt.readVNull(record.data, offset)) { + return null; + } + + int length = VarInt.readVInt(record.data, offset); + offset += VarInt.sizeOfVInt(length); + + byte[] b = new byte[length]; + for (int i = 0; i < length; i++) { + b[i] = record.data.get(offset++); + } + + return b; + } + + private int skipToField(int ordinal, HollowObjectSchema.FieldType fieldType, String field) { + int offset = getOrdinalOffset(ordinal); + + int schemaId = VarInt.readVInt(record.data, offset); + offset += VarInt.sizeOfVInt(schemaId); + + HollowSchema schema = record.schemaIdMapper.getSchema(schemaId); + if (schema.getSchemaType() != HollowSchema.SchemaType.OBJECT) { + throw new IllegalArgumentException(String.format("Ordinal %d is not an OBJECT type (found %s)", ordinal, schema.getSchemaType())); + } + HollowObjectSchema objectSchema = (HollowObjectSchema) schema; + + int fieldIndex = objectSchema.getPosition(field); + if (fieldIndex == -1) { + return -1; + } + + if (fieldType != objectSchema.getFieldType(fieldIndex)) { + throw new IllegalArgumentException(String.format("Field %s is not of type %s", field, fieldType)); + } + + for (int i = 0; i < fieldIndex; i++) { + offset += sizeOfFieldValue(objectSchema.getFieldType(i), offset); + } + + return offset; + } + + private int sizeOfOrdinal(int ordinal) { + int offset = getOrdinalOffset(ordinal); + int start = offset; + + int schemaId = VarInt.readVInt(record.data, offset); + offset += VarInt.sizeOfVInt(schemaId); + + HollowSchema schema = record.schemaIdMapper.getSchema(schemaId); + switch (schema.getSchemaType()) { + case OBJECT: { + HollowObjectSchema objectSchema = (HollowObjectSchema) schema; + for (int i = 0; i < objectSchema.numFields(); i++) { + offset += sizeOfFieldValue(objectSchema.getFieldType(i), offset); + } + break; + } + case LIST: + case SET: { + int size = VarInt.readVInt(record.data, offset); + offset += VarInt.sizeOfVInt(size); + for (int i = 0; i < size; i++) { + offset += VarInt.nextVLongSize(record.data, offset); + } + break; + } + case MAP: { + int size = VarInt.readVInt(record.data, offset); + offset += VarInt.sizeOfVInt(size); + for (int i = 0; i < size; i++) { + offset += VarInt.nextVLongSize(record.data, offset); // key + offset += VarInt.nextVLongSize(record.data, offset); // value + } + break; + } + } + + return offset - start; + } + + private int sizeOfFieldValue(HollowObjectSchema.FieldType fieldType, int offset) { + switch (fieldType) { + case INT: + case LONG: + case REFERENCE: + return VarInt.nextVLongSize(record.data, offset); + case BYTES: + case STRING: + if (VarInt.readVNull(record.data, offset)) { + return 1; + } + int fieldLength = VarInt.readVInt(record.data, offset); + return VarInt.sizeOfVInt(fieldLength) + fieldLength; + case BOOLEAN: + return 1; + case DOUBLE: + return 8; + case FLOAT: + return 4; + default: + throw new IllegalArgumentException("Unsupported field type: " + fieldType); + } + } +} diff --git a/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/flatrecords/traversal/FlatRecordTraversalListNode.java b/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/flatrecords/traversal/FlatRecordTraversalListNode.java index aacb936c9..8e2f4c7fb 100644 --- a/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/flatrecords/traversal/FlatRecordTraversalListNode.java +++ b/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/flatrecords/traversal/FlatRecordTraversalListNode.java @@ -2,59 +2,35 @@ import com.netflix.hollow.core.schema.HollowListSchema; import com.netflix.hollow.core.schema.HollowObjectSchema; -import com.netflix.hollow.core.util.IntList; -import com.netflix.hollow.core.write.objectmapper.flatrecords.FlatRecordReader; +import com.netflix.hollow.core.write.objectmapper.flatrecords.FlatRecordOrdinalReader; import java.util.AbstractList; import java.util.Map; public class FlatRecordTraversalListNode extends AbstractList implements FlatRecordTraversalNode { - private FlatRecordReader reader; - private IntList ordinalPositions; - private HollowListSchema schema; - private int[] elementOrdinals; + private final FlatRecordOrdinalReader reader; + private final HollowListSchema schema; + private final int[] elementOrdinals; + private Map commonSchemaMap; - @Override - public void reposition(FlatRecordReader reader, IntList ordinalPositions, int ordinal) { + public FlatRecordTraversalListNode(FlatRecordOrdinalReader reader, HollowListSchema schema, int ordinal) { this.reader = reader; - this.ordinalPositions = ordinalPositions; - - reader.resetTo(ordinalPositions.get(ordinal)); - schema = (HollowListSchema) reader.readSchema(); + this.schema = schema; - int size = reader.readCollectionSize(); + int size = reader.readSize(ordinal); elementOrdinals = new int[size]; - for (int i = 0; i < size; i++) { - elementOrdinals[i] = reader.readOrdinal(); - } - } - - @Override - public void setCommonSchema(Map commonSchema) { - this.commonSchemaMap = commonSchema; + reader.readListElementsInto(ordinal, elementOrdinals); } @Override - public int hashCode() { - int hashCode = 1; - for (FlatRecordTraversalNode e : this) { - FlatRecordTraversalObjectNode objectNode = (FlatRecordTraversalObjectNode) e; - if (objectNode != null && commonSchemaMap.containsKey(objectNode.getSchema().getName())) { - objectNode.setCommonSchema(commonSchemaMap); - hashCode = 31 * hashCode + objectNode.hashCode(); - } - else if (objectNode == null) { - hashCode = 31 * hashCode; - } - } - return hashCode; + public HollowListSchema getSchema() { + return schema; } - @Override - public HollowListSchema getSchema() { - return schema; + public void setCommonSchema(Map commonSchema) { + this.commonSchemaMap = commonSchema; } public FlatRecordTraversalObjectNode getObject(int index) { @@ -82,11 +58,27 @@ public FlatRecordTraversalNode get(int index) { if (elementOrdinal == -1) { return null; } - return createAndRepositionNode(reader, ordinalPositions, elementOrdinal); + return createNode(reader, elementOrdinal); } @Override public int size() { return elementOrdinals.length; } + + @Override + public int hashCode() { + int hashCode = 1; + for (FlatRecordTraversalNode e : this) { + FlatRecordTraversalObjectNode objectNode = (FlatRecordTraversalObjectNode) e; + if (objectNode != null && commonSchemaMap.containsKey(objectNode.getSchema().getName())) { + objectNode.setCommonSchema(commonSchemaMap); + hashCode = 31 * hashCode + objectNode.hashCode(); + } + else if (objectNode == null) { + hashCode = 31 * hashCode; + } + } + return hashCode; + } } diff --git a/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/flatrecords/traversal/FlatRecordTraversalMapNode.java b/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/flatrecords/traversal/FlatRecordTraversalMapNode.java index e35b019cb..6028742f7 100644 --- a/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/flatrecords/traversal/FlatRecordTraversalMapNode.java +++ b/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/flatrecords/traversal/FlatRecordTraversalMapNode.java @@ -2,73 +2,40 @@ import com.netflix.hollow.core.schema.HollowMapSchema; import com.netflix.hollow.core.schema.HollowObjectSchema; -import com.netflix.hollow.core.util.IntList; -import com.netflix.hollow.core.write.objectmapper.flatrecords.FlatRecordReader; +import com.netflix.hollow.core.write.objectmapper.flatrecords.FlatRecordOrdinalReader; import java.util.AbstractMap; import java.util.AbstractSet; -import java.util.Arrays; -import java.util.Comparator; import java.util.Iterator; import java.util.Map; -import java.util.Objects; import java.util.Set; public class FlatRecordTraversalMapNode extends AbstractMap implements FlatRecordTraversalNode { - private FlatRecordReader reader; - private IntList ordinalPositions; - private HollowMapSchema schema; - private int[] keyOrdinals; - private int[] valueOrdinals; + private final FlatRecordOrdinalReader reader; + private final HollowMapSchema schema; + private final int[] keyOrdinals; + private final int[] valueOrdinals; + private Map commonSchemaMap; - @Override - public void reposition(FlatRecordReader reader, IntList ordinalPositions, int ordinal) { + public FlatRecordTraversalMapNode(FlatRecordOrdinalReader reader, HollowMapSchema schema, int ordinal) { this.reader = reader; - this.ordinalPositions = ordinalPositions; - - reader.resetTo(ordinalPositions.get(ordinal)); - schema = (HollowMapSchema) reader.readSchema(); + this.schema = schema; - int size = reader.readCollectionSize(); + int size = reader.readSize(ordinal); keyOrdinals = new int[size]; valueOrdinals = new int[size]; - int keyOrdinal = 0; - for (int i = 0; i < size; i++) { - keyOrdinal += reader.readOrdinal(); - keyOrdinals[i] = keyOrdinal; - valueOrdinals[i] = reader.readOrdinal(); - } + reader.readMapElementsInto(ordinal, keyOrdinals, valueOrdinals); } @Override - public void setCommonSchema(Map commonSchema) { - this.commonSchemaMap = commonSchema; - } - - @Override - public int hashCode() { - int h = 0; - Iterator> i = entrySet().iterator(); - while (i.hasNext()) { - Entry e = i.next(); - FlatRecordTraversalNode key = e.getKey(); - FlatRecordTraversalNode value = e.getValue(); - if(commonSchemaMap.containsKey(key.getSchema().getName())) { - key.setCommonSchema(commonSchemaMap); - h += (key == null ? 0 : key.hashCode()); - } - if(commonSchemaMap.containsKey(value.getSchema().getName())) { - value.setCommonSchema(commonSchemaMap); - h += (value == null ? 0 : value.hashCode()); - } - } - return h; + public HollowMapSchema getSchema() { + return schema; } @Override - public HollowMapSchema getSchema() { - return schema; + public void setCommonSchema(Map commonSchema) { + this.commonSchemaMap = commonSchema; } @Override @@ -114,7 +81,7 @@ public K getKey() { if (keyOrdinal == -1) { return null; } - return (K) createAndRepositionNode(reader, ordinalPositions, keyOrdinal); + return (K) createNode(reader, keyOrdinal); } @Override @@ -122,7 +89,7 @@ public V getValue() { if (valueOrdinal == -1) { return null; } - return (V) createAndRepositionNode(reader, ordinalPositions, valueOrdinal); + return (V) createNode(reader, valueOrdinal); } @Override @@ -132,31 +99,22 @@ public V setValue(V value) { }; } } - private static class MapEntry { - private final int key; - private final int value; - - public MapEntry(int key, int value) { - this.key = key; - this.value = value; - } - - @Override - public boolean equals(Object o) { - if (o == this) return true; - if (!(o instanceof MapEntry)) return false; - MapEntry other = (MapEntry) o; - return key == other.key && value == other.value; - } - - @Override - public int hashCode() { - return Objects.hash(key, value); - } - @Override - public String toString() { - return "MapEntry(" + key + ", " + value + ")"; + @Override + public int hashCode() { + int h = 0; + for (Entry e : entrySet()) { + FlatRecordTraversalNode key = e.getKey(); + FlatRecordTraversalNode value = e.getValue(); + if (commonSchemaMap.containsKey(key.getSchema().getName())) { + key.setCommonSchema(commonSchemaMap); + h += key.hashCode(); + } + if (commonSchemaMap.containsKey(value.getSchema().getName())) { + value.setCommonSchema(commonSchemaMap); + h += value.hashCode(); + } } + return h; } } diff --git a/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/flatrecords/traversal/FlatRecordTraversalNode.java b/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/flatrecords/traversal/FlatRecordTraversalNode.java index 9a4dd05b7..fd3d96b84 100644 --- a/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/flatrecords/traversal/FlatRecordTraversalNode.java +++ b/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/flatrecords/traversal/FlatRecordTraversalNode.java @@ -1,9 +1,11 @@ package com.netflix.hollow.core.write.objectmapper.flatrecords.traversal; +import com.netflix.hollow.core.schema.HollowListSchema; +import com.netflix.hollow.core.schema.HollowMapSchema; import com.netflix.hollow.core.schema.HollowObjectSchema; import com.netflix.hollow.core.schema.HollowSchema; -import com.netflix.hollow.core.util.IntList; -import com.netflix.hollow.core.write.objectmapper.flatrecords.FlatRecordReader; +import com.netflix.hollow.core.schema.HollowSetSchema; +import com.netflix.hollow.core.write.objectmapper.flatrecords.FlatRecordOrdinalReader; import java.util.Map; @@ -15,31 +17,19 @@ public interface FlatRecordTraversalNode { void setCommonSchema(Map commonSchema); - void reposition(FlatRecordReader reader, IntList ordinalPositions, int ordinal); - - default FlatRecordTraversalNode createAndRepositionNode(FlatRecordReader reader, IntList ordinalPositions, int ordinal) { - reader.pointer = ordinalPositions.get(ordinal); - HollowSchema schema = reader.readSchema(); - - FlatRecordTraversalNode node; + default FlatRecordTraversalNode createNode(FlatRecordOrdinalReader reader, int ordinal) { + HollowSchema schema = reader.readSchema(ordinal); switch (schema.getSchemaType()) { case OBJECT: - node = new FlatRecordTraversalObjectNode(); - break; + return new FlatRecordTraversalObjectNode(reader, (HollowObjectSchema) schema, ordinal); case LIST: - node = new FlatRecordTraversalListNode(); - break; + return new FlatRecordTraversalListNode(reader, (HollowListSchema) schema, ordinal); case SET: - node = new FlatRecordTraversalSetNode(); - break; + return new FlatRecordTraversalSetNode(reader, (HollowSetSchema) schema, ordinal); case MAP: - node = new FlatRecordTraversalMapNode(); - break; + return new FlatRecordTraversalMapNode(reader, (HollowMapSchema) schema, ordinal); default: throw new IllegalArgumentException("Unsupported schema type: " + schema.getSchemaType()); } - - node.reposition(reader, ordinalPositions, ordinal); - return node; } } \ No newline at end of file diff --git a/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/flatrecords/traversal/FlatRecordTraversalObjectNode.java b/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/flatrecords/traversal/FlatRecordTraversalObjectNode.java index b022c08b8..53a9e4cd1 100644 --- a/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/flatrecords/traversal/FlatRecordTraversalObjectNode.java +++ b/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/flatrecords/traversal/FlatRecordTraversalObjectNode.java @@ -1,45 +1,30 @@ package com.netflix.hollow.core.write.objectmapper.flatrecords.traversal; import com.netflix.hollow.core.schema.HollowObjectSchema; -import com.netflix.hollow.core.schema.HollowSchema; -import com.netflix.hollow.core.util.IntList; import com.netflix.hollow.core.write.objectmapper.flatrecords.FlatRecord; -import com.netflix.hollow.core.write.objectmapper.flatrecords.FlatRecordReader; +import com.netflix.hollow.core.write.objectmapper.flatrecords.FlatRecordOrdinalReader; import java.util.Arrays; import java.util.Map; import java.util.Objects; public class FlatRecordTraversalObjectNode implements FlatRecordTraversalNode { - private FlatRecordReader reader; - private IntList ordinalPositions; - private HollowObjectSchema schema; - private Map commonSchemaMap; - private int position; - - public FlatRecordTraversalObjectNode() {} - - public FlatRecordTraversalObjectNode(FlatRecord rec) { - FlatRecordReader reader = new FlatRecordReader(rec); - - IntList ordinalPositions = new IntList(); - while (reader.hasMore()) { - ordinalPositions.add(reader.pointer); - HollowSchema schema = reader.readSchema(); - reader.skipSchema(schema); - } + private final FlatRecordOrdinalReader reader; + private final HollowObjectSchema schema; + private final int ordinal; - reposition(reader, ordinalPositions, ordinalPositions.size() - 1); - } + private Map commonSchemaMap; - @Override - public void reposition(FlatRecordReader reader, IntList ordinalPositions, int ordinal) { + public FlatRecordTraversalObjectNode(FlatRecordOrdinalReader reader, HollowObjectSchema schema, int ordinal) { this.reader = reader; - this.ordinalPositions = ordinalPositions; + this.ordinal = ordinal; + this.schema = schema; + } - reader.resetTo(ordinalPositions.get(ordinal)); - schema = (HollowObjectSchema) reader.readSchema(); - position = reader.pointer; + public FlatRecordTraversalObjectNode(FlatRecord rec) { + this.reader = new FlatRecordOrdinalReader(rec); + this.ordinal = reader.getOrdinalCount() - 1; + this.schema = (HollowObjectSchema) reader.readSchema(ordinal); } @Override @@ -69,53 +54,53 @@ public FlatRecordTraversalMapNode getMapFieldNode(String field) { } public FlatRecordTraversalNode getFieldNode(String field) { - if (!skipToField(field)) { + HollowObjectSchema.FieldType fieldType = schema.getFieldType(field); + if (fieldType == null) { return null; } - if (schema.getFieldType(field) != HollowObjectSchema.FieldType.REFERENCE) { - throw new IllegalStateException("Cannot get child for non-reference field"); + if (fieldType != HollowObjectSchema.FieldType.REFERENCE) { + throw new IllegalArgumentException("Cannot get child for non-reference field"); } - int refOrdinal = reader.readOrdinal(); + int refOrdinal = reader.readFieldReference(ordinal, field); if (refOrdinal == -1) { return null; } - return createAndRepositionNode(reader, ordinalPositions, refOrdinal); + return createNode(reader, refOrdinal); } public Object getFieldValue(String field) { - if (!skipToField(field)) { + HollowObjectSchema.FieldType fieldType = schema.getFieldType(field); + if (fieldType == null) { return null; } - switch(schema.getFieldType(field)) { + + switch (fieldType) { case BOOLEAN: - return reader.readBoolean(); + return reader.readFieldBoolean(ordinal, field); case INT: - return reader.readInt(); + return reader.readFieldInt(ordinal, field); case LONG: - return reader.readLong(); + return reader.readFieldLong(ordinal, field); case FLOAT: - return reader.readFloat(); + return reader.readFieldFloat(ordinal, field); case DOUBLE: - return reader.readDouble(); + return reader.readFieldDouble(ordinal, field); case STRING: - return reader.readString(); + return reader.readFieldString(ordinal, field); case BYTES: - return reader.readBytes(); + return reader.readFieldBytes(ordinal, field); case REFERENCE: - throw new IllegalStateException("Cannot get leaf value for reference field"); + throw new IllegalArgumentException("Cannot get leaf value for reference field"); } return null; } public boolean getFieldValueBoolean(String field) { - if (!skipToField(field)) { - return false; - } - assertFieldType(field, HollowObjectSchema.FieldType.BOOLEAN); - return reader.readBoolean(); + Boolean b = reader.readFieldBoolean(ordinal, field); + return Boolean.TRUE.equals(b); } public Boolean getFieldValueBooleanBoxed(String field) { @@ -123,11 +108,7 @@ public Boolean getFieldValueBooleanBoxed(String field) { } public int getFieldValueInt(String field) { - if (!skipToField(field)) { - return Integer.MIN_VALUE; - } - assertFieldType(field, HollowObjectSchema.FieldType.INT); - return reader.readInt(); + return reader.readFieldInt(ordinal, field); } public Integer getFieldValueIntBoxed(String field) { @@ -139,11 +120,7 @@ public Integer getFieldValueIntBoxed(String field) { } public long getFieldValueLong(String field) { - if (!skipToField(field)) { - return Long.MIN_VALUE; - } - assertFieldType(field, HollowObjectSchema.FieldType.LONG); - return reader.readLong(); + return reader.readFieldLong(ordinal, field); } public Long getFieldValueLongBoxed(String field) { @@ -155,11 +132,7 @@ public Long getFieldValueLongBoxed(String field) { } public float getFieldValueFloat(String field) { - if (!skipToField(field)) { - return Float.NaN; - } - assertFieldType(field, HollowObjectSchema.FieldType.FLOAT); - return reader.readFloat(); + return reader.readFieldFloat(ordinal, field); } public Float getFieldValueFloatBoxed(String field) { @@ -171,11 +144,7 @@ public Float getFieldValueFloatBoxed(String field) { } public double getFieldValueDouble(String field) { - if (!skipToField(field)) { - return Double.NaN; - } - assertFieldType(field, HollowObjectSchema.FieldType.DOUBLE); - return reader.readDouble(); + return reader.readFieldDouble(ordinal, field); } public Double getFieldValueDoubleBoxed(String field) { @@ -187,93 +156,23 @@ public Double getFieldValueDoubleBoxed(String field) { } public String getFieldValueString(String field) { - if (!skipToField(field)) { - return null; - } - assertFieldType(field, HollowObjectSchema.FieldType.STRING); - return reader.readString(); + return reader.readFieldString(ordinal, field); } public byte[] getFieldValueBytes(String field) { - if (!skipToField(field)) { - return null; - } - assertFieldType(field, HollowObjectSchema.FieldType.BYTES); - return reader.readBytes(); + return reader.readFieldBytes(ordinal, field); } @Override public int hashCode() { HollowObjectSchema commonSchema = commonSchemaMap.get(schema.getName()); Object[] fields = new Object[commonSchema.numFields()]; - for(int i=0;i commonSchemaCache = new HashMap<>(); + private static final Map commonSchemaCache = new HashMap<>(); public static boolean equals(FlatRecordTraversalObjectNode left, FlatRecordTraversalObjectNode right) { if (left == null && right == null) { @@ -138,5 +137,4 @@ else if (left instanceof FlatRecordTraversalMapNode && right instanceof FlatReco } } } - } diff --git a/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/flatrecords/traversal/FlatRecordTraversalSetNode.java b/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/flatrecords/traversal/FlatRecordTraversalSetNode.java index 1cd728636..9ce73a085 100644 --- a/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/flatrecords/traversal/FlatRecordTraversalSetNode.java +++ b/hollow/src/main/java/com/netflix/hollow/core/write/objectmapper/flatrecords/traversal/FlatRecordTraversalSetNode.java @@ -2,64 +2,38 @@ import com.netflix.hollow.core.schema.HollowObjectSchema; import com.netflix.hollow.core.schema.HollowSetSchema; -import com.netflix.hollow.core.util.IntList; -import com.netflix.hollow.core.write.objectmapper.flatrecords.FlatRecordReader; +import com.netflix.hollow.core.write.objectmapper.flatrecords.FlatRecordOrdinalReader; import java.util.AbstractSet; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; import java.util.Iterator; -import java.util.List; import java.util.Map; -import java.util.Set; public class FlatRecordTraversalSetNode extends AbstractSet implements FlatRecordTraversalNode { - private FlatRecordReader reader; - private IntList ordinalPositions; - private HollowSetSchema schema; - private int[] elementOrdinals; + private final FlatRecordOrdinalReader reader; + private final HollowSetSchema schema; + private final int[] elementOrdinals; private Map commonSchemaMap; - @Override - public void reposition(FlatRecordReader reader, IntList ordinalPositions, int ordinal) { - this.reader = reader; - this.ordinalPositions = ordinalPositions; - reader.resetTo(ordinalPositions.get(ordinal)); - schema = (HollowSetSchema) reader.readSchema(); + public FlatRecordTraversalSetNode(FlatRecordOrdinalReader reader, HollowSetSchema schema, int ordinal) { + this.reader = reader; + this.schema = schema; - int size = reader.readCollectionSize(); + int size = reader.readSize(ordinal); elementOrdinals = new int[size]; - int elementOrdinal = 0; - for (int i = 0; i < size; i++) { - elementOrdinal += reader.readOrdinal(); - elementOrdinals[i] = elementOrdinal; - } + reader.readSetElementsInto(ordinal, elementOrdinals); } - @Override - public void setCommonSchema(Map commonSchema) { - this.commonSchemaMap = commonSchema; - } @Override public HollowSetSchema getSchema() { return schema; } @Override - public int hashCode() { - int h = 0; - Iterator i = iterator(); - while (i.hasNext()) { - FlatRecordTraversalNode obj = i.next(); - if (obj != null && commonSchemaMap.containsKey(obj.getSchema().getName())) - obj.setCommonSchema(commonSchemaMap); - h += obj.hashCode(); - } - return h; + public void setCommonSchema(Map commonSchema) { + this.commonSchemaMap = commonSchema; } + public Iterator objects() { return new IteratorImpl<>(); } @@ -86,6 +60,18 @@ public int size() { return elementOrdinals.length; } + @Override + public int hashCode() { + int h = 0; + for (FlatRecordTraversalNode obj : this) { + if (obj != null && commonSchemaMap.containsKey(obj.getSchema().getName())) { + obj.setCommonSchema(commonSchemaMap); + h += obj.hashCode(); + } + } + return h; + } + private class IteratorImpl implements Iterator { private int index = 0; @@ -100,7 +86,7 @@ public T next() { if (elementOrdinal == -1) { return null; } - return (T) createAndRepositionNode(reader, ordinalPositions, elementOrdinal); + return (T) createNode(reader, elementOrdinal); } } }