/* * Licensed to GraphHopper GmbH under one or more contributor * license agreements. See the NOTICE file distributed with this work for * additional information regarding copyright ownership. * * GraphHopper GmbH 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 com.graphhopper.storage; import com.graphhopper.routing.util.EdgeFilter; import com.graphhopper.util.BitUtil; import com.graphhopper.util.EdgeIterator; import com.graphhopper.util.EdgeIteratorState; /** * @author Peter Karich */ abstract class EdgeAccess { static final int NO_NODE = -1; // distance of around +-1000 000 meter are ok private static final double INT_DIST_FACTOR = 1000d; static double MAX_DIST = (Integer.MAX_VALUE - 1) / INT_DIST_FACTOR; final DataAccess edges; private final BitUtil bitUtil; int E_NODEA, E_NODEB, E_LINKA, E_LINKB, E_DIST, E_FLAGS; private boolean flagsSizeIsLong; EdgeAccess(DataAccess edges, BitUtil bitUtil) { this.edges = edges; this.bitUtil = bitUtil; } final void init(int E_NODEA, int E_NODEB, int E_LINKA, int E_LINKB, int E_DIST, int E_FLAGS, boolean flagsSizeIsLong) { this.E_NODEA = E_NODEA; this.E_NODEB = E_NODEB; this.E_LINKA = E_LINKA; this.E_LINKB = E_LINKB; this.E_DIST = E_DIST; this.E_FLAGS = E_FLAGS; this.flagsSizeIsLong = flagsSizeIsLong; } abstract BaseGraph.EdgeIterable createSingleEdge(EdgeFilter edgeFilter); abstract long toPointer(int edgeOrShortcutId); abstract boolean isInBounds(int edgeOrShortcutId); abstract long reverseFlags(long edgePointer, long flags); abstract int getEdgeRef(int nodeId); abstract void setEdgeRef(int nodeId, int edgeId); abstract int getEntryBytes(); final void invalidateEdge(long edgePointer) { edges.setInt(edgePointer + E_NODEA, NO_NODE); } final void setDist(long edgePointer, double distance) { edges.setInt(edgePointer + E_DIST, distToInt(distance)); } /** * Translates double distance to integer in order to save it in a DataAccess object */ private int distToInt(double distance) { int integ = (int) (distance * INT_DIST_FACTOR); if (integ < 0) throw new IllegalArgumentException("Distance cannot be negative: " + distance); if (integ >= Integer.MAX_VALUE) return Integer.MAX_VALUE; // throw new IllegalArgumentException("Distance too large leading to overflowed integer (#435): " + distance + " "); return integ; } /** * returns distance (already translated from integer to double) */ final double getDist(long pointer) { int val = edges.getInt(pointer + E_DIST); // do never return infinity even if INT MAX, see #435 return val / INT_DIST_FACTOR; } final long getFlags_(long edgePointer, boolean reverse) { int low = edges.getInt(edgePointer + E_FLAGS); long resFlags = low; if (flagsSizeIsLong) { int high = edges.getInt(edgePointer + E_FLAGS + 4); resFlags = bitUtil.combineIntsToLong(low, high); } if (reverse) resFlags = reverseFlags(edgePointer, resFlags); return resFlags; } final long setFlags_(long edgePointer, boolean reverse, long flags) { if (reverse) flags = reverseFlags(edgePointer, flags); edges.setInt(edgePointer + E_FLAGS, bitUtil.getIntLow(flags)); if (flagsSizeIsLong) edges.setInt(edgePointer + E_FLAGS + 4, bitUtil.getIntHigh(flags)); return flags; } /** * Write new edge between nodes fromNodeId, and toNodeId both to nodes index and edges index */ final int internalEdgeAdd(int newEdgeId, int fromNodeId, int toNodeId) { writeEdge(newEdgeId, fromNodeId, toNodeId, EdgeIterator.NO_EDGE, EdgeIterator.NO_EDGE); connectNewEdge(fromNodeId, newEdgeId); if (fromNodeId != toNodeId) connectNewEdge(toNodeId, newEdgeId); return newEdgeId; } final int getOtherNode(int nodeThis, long edgePointer) { int nodeA = edges.getInt(edgePointer + E_NODEA); if (nodeA == nodeThis) // return b return edges.getInt(edgePointer + E_NODEB); // return a return nodeA; } private long _getLinkPosInEdgeArea(int nodeThis, int nodeOther, long edgePointer) { return nodeThis <= nodeOther ? edgePointer + E_LINKA : edgePointer + E_LINKB; } final int getEdgeRef(int nodeThis, int nodeOther, long edgePointer) { return edges.getInt(_getLinkPosInEdgeArea(nodeThis, nodeOther, edgePointer)); } final void connectNewEdge(int fromNode, int newOrExistingEdge) { int edge = getEdgeRef(fromNode); if (edge > EdgeIterator.NO_EDGE) { long edgePointer = toPointer(newOrExistingEdge); int otherNode = getOtherNode(fromNode, edgePointer); long lastLink = _getLinkPosInEdgeArea(fromNode, otherNode, edgePointer); edges.setInt(lastLink, edge); } setEdgeRef(fromNode, newOrExistingEdge); } final long writeEdge(int edgeId, int nodeThis, int nodeOther, int nextEdge, int nextEdgeOther) { if (nodeThis > nodeOther) { int tmp = nodeThis; nodeThis = nodeOther; nodeOther = tmp; tmp = nextEdge; nextEdge = nextEdgeOther; nextEdgeOther = tmp; } if (edgeId < 0 || edgeId == EdgeIterator.NO_EDGE) throw new IllegalStateException("Cannot write edge with illegal ID:" + edgeId + "; nodeThis:" + nodeThis + ", nodeOther:" + nodeOther); long edgePointer = toPointer(edgeId); edges.setInt(edgePointer + E_NODEA, nodeThis); edges.setInt(edgePointer + E_NODEB, nodeOther); edges.setInt(edgePointer + E_LINKA, nextEdge); edges.setInt(edgePointer + E_LINKB, nextEdgeOther); return edgePointer; } /** * This method disconnects the specified edge from the list of edges of the specified node. It * does not release the freed space to be reused. * <p> * * @param edgeToUpdatePointer if it is negative then the nextEdgeId will be saved to refToEdges * of nodes */ final long internalEdgeDisconnect(int edgeToRemove, long edgeToUpdatePointer, int baseNode, int adjNode) { long edgeToRemovePointer = toPointer(edgeToRemove); // an edge is shared across the two nodes even if the edge is not in both directions // so we need to know two edge-pointers pointing to the edge before edgeToRemovePointer int nextEdgeId = getEdgeRef(baseNode, adjNode, edgeToRemovePointer); if (edgeToUpdatePointer < 0) { setEdgeRef(baseNode, nextEdgeId); } else { // adjNode is different for the edge we want to update with the new link long link = edges.getInt(edgeToUpdatePointer + E_NODEA) == baseNode ? edgeToUpdatePointer + E_LINKA : edgeToUpdatePointer + E_LINKB; edges.setInt(link, nextEdgeId); } return edgeToRemovePointer; } final EdgeIteratorState getEdgeProps(int edgeId, int adjNode) { if (edgeId <= EdgeIterator.NO_EDGE) throw new IllegalStateException("edgeId invalid " + edgeId + ", " + this); BaseGraph.EdgeIterable edge = createSingleEdge(EdgeFilter.ALL_EDGES); if (edge.init(edgeId, adjNode)) return edge; // if edgeId exists but adjacent nodes do not match return null; } }