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

com.swirlds.merkledb.collections.ThreeLongsList Maven / Gradle / Ivy

/*
 * Copyright (C) 2021-2024 Hedera Hashgraph, LLC
 *
 * 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 com.swirlds.merkledb.collections;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Provides auto-expanding storage for triples of longs, up to some maximum capacity. The triples
 * are stored in "chunks" of configurable size, and retrieved by using an integer index.
 *
 * Allocated chunks are never released; but the existing chunks may be re-used by calling
 * {@link ThreeLongsList#clear()} and then storing more triples starting again at index 0.
 *
 * Important: This class is not thread-safe, and by default has a capacity of only 4k triples (12k total longs).
 */
public class ThreeLongsList {
    private static final int LONGS_PER_TRIPLE = 3;
    private static final int SECOND_LONG_OFFSET = 1;
    private static final int THIRD_LONG_OFFSET = 2;

    public static final int SMALL_MAX_TRIPLES = 4_000;
    public static final int SMALL_TRIPLES_PER_CHUNK = 1_000;

    private final int maxTriples;
    private final int triplesPerChunk;
    private final List data = new ArrayList<>();

    private int numChunks = 0;
    private int nextIndex = 0;

    /**
     * Construct a new ThreeLongsList with the default max and initial capacities.
     */
    public ThreeLongsList() {
        this(SMALL_MAX_TRIPLES, SMALL_TRIPLES_PER_CHUNK);
    }

    /**
     * Construct a new ThreeLongsList with the given max capacity and default
     * initial triples per chunk.
     *
     * @param maxTriples
     * 		the maximum number of triplets that can be stored
     */
    public ThreeLongsList(final int maxTriples) {
        this(maxTriples, Math.min(maxTriples, SMALL_TRIPLES_PER_CHUNK));
    }

    /**
     * Construct a new ThreeLongsList with the given max capacity and triples per chunk.
     *
     * @param maxTriples
     * 		the maximum number of triplets that can be stored
     * @throws IllegalArgumentException
     * 		if triplesPerChunk is non-positive or greater than maxTriples
     */
    public ThreeLongsList(final int maxTriples, final int triplesPerChunk) {
        if (triplesPerChunk < 1 || maxTriples < triplesPerChunk) {
            throw new IllegalArgumentException("Cannot construct ThreeLongsList with " + maxTriples
                    + " max triples and " + triplesPerChunk + " triples per chunk");
        }
        this.maxTriples = maxTriples;
        this.triplesPerChunk = triplesPerChunk;
    }

    /**
     * Adds a triple of longs.
     *
     * @param l1
     * 		first long to add
     * @param l2
     * 		second long to add
     * @param l3
     * 		third long to add
     * @throws IllegalStateException
     * 		if the list size would exceed its max capacity
     */
    public void add(final long l1, final long l2, final long l3) {
        if (nextIndex == maxTriples) {
            throw new IllegalStateException("Cannot store any more triples, reached max capacity of " + maxTriples);
        }

        final int chunkIndex = nextIndex / triplesPerChunk;
        int subIndex = nextIndex % triplesPerChunk;
        if (subIndex == 0 && numChunks == chunkIndex) {
            final long[] nextChunk = new long[LONGS_PER_TRIPLE * triplesPerChunk];
            data.add(nextChunk);
            numChunks++;
        }

        final long[] chunk = data.get(chunkIndex);
        subIndex *= LONGS_PER_TRIPLE;
        chunk[subIndex] = l1;
        chunk[subIndex + SECOND_LONG_OFFSET] = l2;
        chunk[subIndex + THIRD_LONG_OFFSET] = l3;

        nextIndex++;
    }

    /**
     * Get long triplet at given index
     *
     * @param index
     * 		the index to get
     * @return array of the three longs in triplet
     * @throws IndexOutOfBoundsException
     * 		if index is negative or not less than nextIndex
     */
    public long[] get(final int index) {
        if (index < 0 || index >= nextIndex) {
            throw new IndexOutOfBoundsException("Index " + index + " unusable with " + nextIndex + " triples stored");
        }
        final int chunkIndex = index / triplesPerChunk;
        final long[] chunk = data.get(chunkIndex);
        final int subIndex = (index % triplesPerChunk) * LONGS_PER_TRIPLE;
        return Arrays.copyOfRange(chunk, subIndex, subIndex + LONGS_PER_TRIPLE);
    }

    /**
     * Clear contents leaving capacity the same.
     */
    public void clear() {
        nextIndex = 0;
    }

    /**
     * For each to iterate over all triples stored.
     *
     * @param handler
     * 		callback to receieve each triple.
     */
    public  void forEach(final ThreeLongFunction handler) throws T {
        int index = 0;
        for (final long[] chunk : data) {
            for (int j = 0, k = 0; j < triplesPerChunk; j++, k += LONGS_PER_TRIPLE) {
                if (index == nextIndex) {
                    return;
                }
                handler.process(chunk[k], chunk[k + SECOND_LONG_OFFSET], chunk[k + THIRD_LONG_OFFSET]);
                index++;
            }
        }
    }

    /**
     * Gets the number of long triplets actually stored in this list (that is, the number
     * of times that {@link ThreeLongsList#add(long, long, long)} has been called since
     * the last call to {@link ThreeLongsList#clear()}.
     *
     * @return the number of triplets in the list
     */
    public int size() {
        return nextIndex;
    }

    /**
     * Gets the maximum number of triples that can be stored in this list.
     *
     * @return the max capacity of the list
     */
    public int capacity() {
        return maxTriples;
    }

    /**
     * Simple functional interface to process three longs. A Throwable of the given
     * type may be thrown.
     *
     * @param 
     *     Type of Throwable that may be thrown
     */
    @FunctionalInterface
    public interface ThreeLongFunction {
        void process(long l1, long l2, long l3) throws T;
    }

    int getTriplesPerChunk() {
        return triplesPerChunk;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy