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

org.neo4j.kernel.impl.util.collection.MutableLinearProbeLongHashSet Maven / Gradle / Ivy

Go to download

Neo4j kernel is a lightweight, embedded Java database designed to store data structured as graphs rather than tables. For more information, see http://neo4j.org.

There is a newer version: 5.25.1
Show newest version
/*
 * Copyright (c) "Neo4j"
 * Neo4j Sweden AB [https://neo4j.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
package org.neo4j.kernel.impl.util.collection;

import static java.util.Objects.requireNonNull;
import static org.neo4j.util.Preconditions.requirePowerOfTwo;

import org.eclipse.collections.api.LongIterable;
import org.eclipse.collections.api.block.procedure.primitive.LongProcedure;
import org.eclipse.collections.api.iterator.MutableLongIterator;
import org.eclipse.collections.api.multimap.MutableMultimap;
import org.eclipse.collections.api.set.primitive.ImmutableLongSet;
import org.eclipse.collections.api.set.primitive.LongSet;
import org.eclipse.collections.api.set.primitive.MutableLongSet;
import org.eclipse.collections.impl.factory.Multimaps;
import org.eclipse.collections.impl.set.mutable.primitive.SynchronizedLongSet;
import org.eclipse.collections.impl.set.mutable.primitive.UnmodifiableLongSet;
import org.neo4j.graphdb.Resource;
import org.neo4j.memory.MemoryTracker;
import org.neo4j.util.VisibleForTesting;

/**
 * Off heap implementation of long hash set.
 * 
    *
  • It is not thread-safe *
  • It has to be closed to prevent native memory leakage *
  • Iterators returned by this set are fail-fast *
*/ class MutableLinearProbeLongHashSet extends AbstractLinearProbeLongHashSet implements MutableLongSet, Resource { static final int DEFAULT_CAPACITY = 32; static final int REMOVALS_RATIO = 4; private static final double LOAD_FACTOR = 0.75; private final MemoryAllocator allocator; private final MemoryTracker memoryTracker; private final MutableMultimap frozenCopies = Multimaps.mutable.list.empty(); private int resizeOccupancyThreshold; private int resizeRemovalsThreshold; private int removals; private boolean frozen; MutableLinearProbeLongHashSet(MemoryAllocator allocator, MemoryTracker memoryTracker) { this.allocator = requireNonNull(allocator); this.memoryTracker = memoryTracker; allocateMemory(DEFAULT_CAPACITY); } @Override public boolean add(long element) { ++modCount; if (element == 0) { final boolean hadZero = hasZero; hasZero = true; return hadZero != hasZero; } if (element == 1) { final boolean hadOne = hasOne; hasOne = true; return hadOne != hasOne; } return addToMemory(element); } @Override public boolean addAll(long... elements) { ++modCount; final int prevSize = size(); for (final long element : elements) { add(element); } return prevSize != size(); } @Override public boolean addAll(LongIterable elements) { ++modCount; final int prevSize = size(); elements.forEach(this::add); return prevSize != size(); } @Override public boolean remove(long element) { ++modCount; if (element == 0) { final boolean hadZero = hasZero; hasZero = false; return hadZero != hasZero; } if (element == 1) { final boolean hadOne = hasOne; hasOne = false; return hadOne != hasOne; } return removeFromMemory(element); } @Override public boolean removeAll(LongIterable elements) { ++modCount; final int prevSize = size(); elements.forEach(this::remove); return prevSize != size(); } @Override public boolean removeAll(long... elements) { ++modCount; final int prevSize = size(); for (final long element : elements) { remove(element); } return prevSize != size(); } @Override public boolean retainAll(LongIterable elements) { throw new UnsupportedOperationException(); } @Override public boolean retainAll(long... source) { throw new UnsupportedOperationException(); } @Override public void clear() { ++modCount; copyIfFrozen(); memory.clear(); hasZero = false; hasOne = false; elementsInMemory = 0; removals = 0; } @Override public MutableLongIterator longIterator() { return new FailFastIterator(); } @Override public void close() { ++modCount; if (memory != null) { frozenCopies.forEachKeyMultiValues((mem, copies) -> { mem.free(memoryTracker); copies.forEach(FrozenCopy::invalidate); }); if (!frozenCopies.containsKey(memory)) { memory.free(memoryTracker); } memory = null; frozenCopies.clear(); } } @Override public MutableLongSet tap(LongProcedure procedure) { each(procedure); return this; } @Override public MutableLongSet with(long element) { add(element); return this; } @Override public MutableLongSet without(long element) { remove(element); return this; } @Override public MutableLongSet withAll(LongIterable elements) { addAll(elements); return this; } @Override public MutableLongSet withoutAll(LongIterable elements) { removeAll(elements); return this; } @Override public MutableLongSet asUnmodifiable() { return new UnmodifiableLongSet(this); } @Override public MutableLongSet asSynchronized() { return new SynchronizedLongSet(this); } @Override public LongSet freeze() { frozen = true; final FrozenCopy frozenCopy = new FrozenCopy(); frozenCopies.put(memory, frozenCopy); return frozenCopy; } @Override public ImmutableLongSet toImmutable() { throw new UnsupportedOperationException(); } private boolean removeFromMemory(long element) { final int idx = indexOf(element); final long valueAtIdx = memory.readLong((long) idx * Long.BYTES); if (valueAtIdx != element) { return false; } copyIfFrozen(); memory.writeLong((long) idx * Long.BYTES, REMOVED); --elementsInMemory; ++removals; if (removals >= resizeRemovalsThreshold) { rehashWithoutGrow(); } return true; } private boolean addToMemory(long element) { final int idx = indexOf(element); final long valueAtIdx = valueAt(idx); if (valueAtIdx == element) { return false; } if (valueAtIdx == REMOVED) { --removals; } copyIfFrozen(); memory.writeLong((long) idx * Long.BYTES, element); ++elementsInMemory; if (elementsInMemory >= resizeOccupancyThreshold) { growAndRehash(); } return true; } @VisibleForTesting void growAndRehash() { final int newCapacity = capacity * 2; if (newCapacity < capacity) { throw new RuntimeException("LongSet reached capacity limit"); } rehash(newCapacity); } @VisibleForTesting void rehashWithoutGrow() { rehash(capacity); } private void allocateMemory(int newCapacity) { requirePowerOfTwo(newCapacity); capacity = newCapacity; resizeOccupancyThreshold = (int) (newCapacity * LOAD_FACTOR); resizeRemovalsThreshold = newCapacity / REMOVALS_RATIO; memory = allocator.allocate((long) newCapacity * Long.BYTES, true, memoryTracker); } private void rehash(int newCapacity) { final int prevCapacity = capacity; final Memory prevMemory = memory; elementsInMemory = 0; removals = 0; allocateMemory(newCapacity); for (int i = 0; i < prevCapacity; i++) { final long value = prevMemory.readLong((long) i * Long.BYTES); if (isRealValue(value)) { add(value); } } prevMemory.free(memoryTracker); } private void copyIfFrozen() { if (frozen) { frozen = false; memory = memory.copy(memoryTracker); } } class FrozenCopy extends AbstractLinearProbeLongHashSet { FrozenCopy() { super(MutableLinearProbeLongHashSet.this); } @Override public LongSet union(LongSet set) { throw new UnsupportedOperationException(); } @Override public LongSet intersect(LongSet set) { throw new UnsupportedOperationException(); } @Override public LongSet difference(LongSet set) { throw new UnsupportedOperationException(); } @Override public LongSet symmetricDifference(LongSet set) { throw new UnsupportedOperationException(); } @Override public LongSet freeze() { return this; } @Override public ImmutableLongSet toImmutable() { throw new UnsupportedOperationException(); } void invalidate() { ++FrozenCopy.this.modCount; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy