All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.graphhopper.routing.ev.UnsignedIntEncodedValue Maven / Gradle / Ivy

/*
 *  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.routing.ev;

import com.graphhopper.routing.util.EncodingManager;
import com.graphhopper.storage.IntsRef;
import com.graphhopper.util.Helper;

import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;

/**
 * Implementation of the IntEncodedValue via a limited number of bits and without a sign. It introduces handling
 * of "backward"- and "forward"-edge information.
 */
public class UnsignedIntEncodedValue implements IntEncodedValue {

    private final String name;

    /**
     * There are multiple int values possible per edge. Here we specify the index into this integer array.
     */
    private int fwdDataIndex;
    private int bwdDataIndex;
    private final boolean storeTwoDirections;
    final int bits;
    int maxValue;
    int fwdShift = -1;
    int bwdShift = -1;
    int fwdMask;
    int bwdMask;

    /**
     * This constructor reserves the specified number of bits in the underlying data structure or twice the amount if
     * storeTwoDirections is true.
     *
     * @param storeTwoDirections if true this EncodedValue can store different values for the forward and backward
     *                           direction.
     */
    public UnsignedIntEncodedValue(String name, int bits, boolean storeTwoDirections) {
        if (!EncodingManager.isValidEncodedValue(name))
            throw new IllegalArgumentException("EncodedValue name wasn't valid: " + name + ". Use lower case letters, underscore and numbers only.");
        if (bits <= 0)
            throw new IllegalArgumentException(name + ": bits cannot be zero or negative");
        if (bits > 31)
            throw new IllegalArgumentException(name + ": at the moment the number of reserved bits cannot be more than 31");
        this.bits = bits;
        this.name = name;
        this.storeTwoDirections = storeTwoDirections;
    }

    @Override
    public final int init(EncodedValue.InitializerConfig init) {
        if (isInitialized())
            throw new IllegalStateException("Cannot call init multiple times");

        init.next(bits);
        this.fwdMask = init.bitMask;
        this.fwdDataIndex = init.dataIndex;
        this.fwdShift = init.shift;
        if (storeTwoDirections) {
            init.next(bits);
            this.bwdMask = init.bitMask;
            this.bwdDataIndex = init.dataIndex;
            this.bwdShift = init.shift;
        }

        this.maxValue = (1 << bits) - 1;
        return storeTwoDirections ? 2 * bits : bits;
    }

    boolean isInitialized() {
        return fwdMask != 0;
    }

    private void checkValue(int value) {
        if (!isInitialized())
            throw new IllegalStateException("EncodedValue " + getName() + " not initialized");
        if (value > maxValue)
            throw new IllegalArgumentException(name + " value too large for encoding: " + value + ", maxValue:" + maxValue);
        if (value < 0)
            throw new IllegalArgumentException("negative value for " + name + " not allowed! " + value);
    }

    @Override
    public final void setInt(boolean reverse, IntsRef ref, int value) {
        checkValue(value);
        uncheckedSet(reverse, ref, value);
    }

    final void uncheckedSet(boolean reverse, IntsRef ref, int value) {
        if (reverse && !storeTwoDirections)
            throw new IllegalArgumentException(getName() + ": value for reverse direction would overwrite forward direction. Enable storeTwoDirections for this EncodedValue or don't use setReverse");

        if (reverse) {
            int flags = ref.ints[bwdDataIndex + ref.offset];
            // clear value bits
            flags &= ~bwdMask;
            ref.ints[bwdDataIndex + ref.offset] = flags | (value << bwdShift);
        } else {
            int flags = ref.ints[fwdDataIndex + ref.offset];
            flags &= ~fwdMask;
            ref.ints[fwdDataIndex + ref.offset] = flags | (value << fwdShift);
        }
    }

    @Override
    public final int getInt(boolean reverse, IntsRef ref) {
        int flags;
        // if we do not store both directions ignore reverse == true for convenient reading
        if (storeTwoDirections && reverse) {
            flags = ref.ints[bwdDataIndex + ref.offset];
            return (flags & bwdMask) >>> bwdShift;
        } else {
            flags = ref.ints[fwdDataIndex + ref.offset];
            return (flags & fwdMask) >>> fwdShift;
        }
    }

    @Override
    public int getMaxInt() {
        return maxValue;
    }

    @Override
    public final boolean isStoreTwoDirections() {
        return storeTwoDirections;
    }

    @Override
    public final String getName() {
        return name;
    }

    @Override
    public final String toString() {
        return getName() + "|version=" + getVersion() + "|bits=" + bits + "|index=" + fwdDataIndex + "|shift=" + fwdShift + "|store_both_directions=" + storeTwoDirections;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        UnsignedIntEncodedValue that = (UnsignedIntEncodedValue) o;
        return fwdDataIndex == that.fwdDataIndex &&
                bwdDataIndex == that.bwdDataIndex &&
                bits == that.bits &&
                maxValue == that.maxValue &&
                fwdShift == that.fwdShift &&
                bwdShift == that.bwdShift &&
                fwdMask == that.fwdMask &&
                bwdMask == that.bwdMask &&
                storeTwoDirections == that.storeTwoDirections &&
                Objects.equals(name, that.name);
    }

    @Override
    public final int hashCode() {
        return getVersion();
    }

    @Override
    public int getVersion() {
        int val = Helper.staticHashCode(name);
        val = 31 * val + (storeTwoDirections ? 1231 : 1237);
        return staticHashCode(val, fwdDataIndex, bwdDataIndex, bits, maxValue, fwdShift, bwdShift, fwdMask, bwdMask);
    }

    /**
     * Produces a static hashcode for an integer arrays that is platform independent and still compatible to the default
     * of openjdk.
     *
     * @see Arrays#hashCode(int[])
     */
    static int staticHashCode(int... vals) {
        if (vals == null)
            return 0;
        int len = vals.length;
        int val = 1;
        for (int idx = 0; idx < len; ++idx) {
            val = 31 * val + vals[idx];
        }

        return val;
    }

    /**
     * Produces a static hashcode for an Enum arrays that is platform independent and still compatible to the default
     * of openjdk.
     */
    static int staticHashCode(Enum... vals) {
        if (vals == null)
            return 0;
        int len = vals.length;
        int val = 1;
        for (int idx = 0; idx < len; ++idx) {
            val = 31 * val + vals[idx].ordinal();
        }

        return val;
    }

    /**
     * Produces a static hashcode for a collection of Strings that is platform independent and still compatible to the default
     * of openjdk.
     */
    static int staticHashCode(Collection vals) {
        if (vals == null)
            return 0;
        int val = 1;
        for (String str : vals) {
            val = 31 * val + Helper.staticHashCode(str);
        }

        return val;
    }

    /**
     * Produces a static hashcode for an integer arrays that is platform independent and still compatible to the default
     * of openjdk
     *
     * @see Double#hashCode
     */
    static int staticHashCode(double val) {
        long var2 = Double.doubleToLongBits(val);
        return (int) (var2 ^ var2 >>> 32);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy