// Copyright 2017 JanusGraph Authors // // 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 org.janusgraph.graphdb.database.idhandling; import org.janusgraph.diskstorage.ReadBuffer; import org.janusgraph.diskstorage.StaticBuffer; import org.janusgraph.diskstorage.WriteBuffer; import org.janusgraph.diskstorage.util.BufferUtil; import org.janusgraph.diskstorage.util.StaticArrayBuffer; import org.janusgraph.diskstorage.util.WriteByteBuffer; import org.janusgraph.graphdb.idmanagement.IDManager; import org.janusgraph.graphdb.internal.RelationCategory; import org.apache.tinkerpop.gremlin.structure.Direction; import static org.janusgraph.graphdb.idmanagement.IDManager.VertexIDType.*; /** * @author Matthias Broecheler (me@matthiasb.com) */ public class IDHandler { public static final StaticBuffer MIN_KEY = BufferUtil.getLongBuffer(0); public static final StaticBuffer MAX_KEY = BufferUtil.getLongBuffer(-1); public static enum DirectionID { PROPERTY_DIR(0), //00b EDGE_OUT_DIR(2), //10b EDGE_IN_DIR(3); //11b private final int id; private DirectionID(int id) { this.id = id; } private int getRelationType() { return id >>> 1; } private int getDirectionInt() { return id & 1; } public RelationCategory getRelationCategory() { switch(this) { case PROPERTY_DIR: return RelationCategory.PROPERTY; case EDGE_IN_DIR: case EDGE_OUT_DIR: return RelationCategory.EDGE; default: throw new AssertionError(); } } public Direction getDirection() { switch(this) { case PROPERTY_DIR: case EDGE_OUT_DIR: return Direction.OUT; case EDGE_IN_DIR: return Direction.IN; default: throw new AssertionError(); } } private int getPrefix(boolean invisible, boolean systemType) { assert !systemType || invisible; // systemType implies invisible return ((systemType?0:invisible?2:1)<<1) + getRelationType(); } private static DirectionID getDirectionID(int relationType, int direction) { assert relationType >= 0 && relationType <= 1 && direction >= 0 && direction <= 1; return forId((relationType << 1) + direction); } private static DirectionID forId(int id) { switch(id) { case 0: return PROPERTY_DIR; case 2: return EDGE_OUT_DIR; case 3: return EDGE_IN_DIR; default: throw new AssertionError("Invalid id: " + id); } } } private static final int PREFIX_BIT_LEN = 3; public static int relationTypeLength(long relationTypeId) { assert relationTypeId > 0 && (relationTypeId << 1) > 0; //Check positive and no-overflow return VariableLong.positiveWithPrefixLength(IDManager.stripEntireRelationTypePadding(relationTypeId) << 1, PREFIX_BIT_LEN); } /** * The edge type is written as follows: [ Invisible & System (2 bit) | Relation-Type-ID (1 bit) | Relation-Type-Count (variable) | Direction-ID (1 bit)] * Would only need 1 bit to store relation-type-id, but using two so we can upper bound. * * * @param out * @param relationTypeId * @param dirID */ public static void writeRelationType(WriteBuffer out, long relationTypeId, DirectionID dirID, boolean invisible) { assert relationTypeId > 0 && (relationTypeId << 1) > 0; //Check positive and no-overflow long strippedId = (IDManager.stripEntireRelationTypePadding(relationTypeId) << 1) + dirID.getDirectionInt(); VariableLong.writePositiveWithPrefix(out, strippedId, dirID.getPrefix(invisible, IDManager.isSystemRelationTypeId(relationTypeId)), PREFIX_BIT_LEN); } public static StaticBuffer getRelationType(long relationTypeId, DirectionID dirID, boolean invisible) { WriteBuffer b = new WriteByteBuffer(relationTypeLength(relationTypeId)); IDHandler.writeRelationType(b, relationTypeId, dirID, invisible); return b.getStaticBuffer(); } public static RelationTypeParse readRelationType(ReadBuffer in) { long[] countPrefix = VariableLong.readPositiveWithPrefix(in, PREFIX_BIT_LEN); DirectionID dirID = DirectionID.getDirectionID((int) countPrefix[1] & 1, (int) (countPrefix[0] & 1)); long typeId = countPrefix[0] >>> 1; boolean isSystemType = (countPrefix[1]>>1)==0; if (dirID == DirectionID.PROPERTY_DIR) typeId = IDManager.getSchemaId(isSystemType?SystemPropertyKey:UserPropertyKey, typeId); else typeId = IDManager.getSchemaId(isSystemType?SystemEdgeLabel:UserEdgeLabel, typeId); return new RelationTypeParse(typeId,dirID); } public static class RelationTypeParse { public final long typeId; public final DirectionID dirID; public RelationTypeParse(long typeId, DirectionID dirID) { this.typeId = typeId; this.dirID = dirID; } } public static void writeInlineRelationType(WriteBuffer out, long relationTypeId) { long compressId = IDManager.stripRelationTypePadding(relationTypeId); VariableLong.writePositive(out, compressId); } public static long readInlineRelationType(ReadBuffer in) { long compressId = VariableLong.readPositive(in); return IDManager.addRelationTypePadding(compressId); } private static StaticBuffer getPrefixed(int prefix) { assert prefix < (1 << PREFIX_BIT_LEN) && prefix >= 0; byte[] arr = new byte[1]; arr[0] = (byte) (prefix << (Byte.SIZE - PREFIX_BIT_LEN)); return new StaticArrayBuffer(arr); } public static StaticBuffer[] getBounds(RelationCategory type, boolean systemTypes) { int start, end; switch (type) { case PROPERTY: start = DirectionID.PROPERTY_DIR.getPrefix(systemTypes,systemTypes); end = start; break; case EDGE: start = DirectionID.EDGE_OUT_DIR.getPrefix(systemTypes,systemTypes); end = start; break; case RELATION: start = DirectionID.PROPERTY_DIR.getPrefix(systemTypes,systemTypes); end = DirectionID.EDGE_OUT_DIR.getPrefix(systemTypes,systemTypes); break; default: throw new AssertionError("Unrecognized type:" + type); } end++; assert end > start; return new StaticBuffer[]{getPrefixed(start), getPrefixed(end)}; } }