    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      https://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.collections4.bloomfilter;
    018
    019import java.util.Arrays;
    020import java.util.BitSet;
    021import java.util.Objects;
    022import java.util.function.IntPredicate;
    023import java.util.function.LongPredicate;
    024
    025/**
    026 * An object that produces indices of a Bloom filter.
    027 * <p><em>
    028 * The default implementation of {@code asIndexArray} is slow. Implementers should reimplement the
    029 * method where possible.</em></p>
    030 *
    031 * @since 4.5.0-M2
    032 */
    033@FunctionalInterface
    034public interface IndexExtractor {
    035
    036    /**
    037     * Creates an IndexExtractor from a {@code BitMapExtractor}.
    038     *
    039     * @param bitMapExtractor The {@code BitMapExtractor}
    040     * @return A new {@code IndexExtractor}.
    041     */
    042    static IndexExtractor fromBitMapExtractor(final BitMapExtractor bitMapExtractor) {
    043        Objects.requireNonNull(bitMapExtractor, "bitMapExtractor");
    044        return consumer -> {
    045            final LongPredicate longPredicate = new LongPredicate() {
    046                int wordIdx;
    047
    048                @Override
    049                public boolean test(long word) {
    050                    int i = wordIdx;
    051                    while (word != 0) {
    052                        if ((word & 1) == 1 && !consumer.test(i)) {
    053                            return false;
    054                        }
    055                        word >>>= 1;
    056                        i++;
    057                    }
    058                    wordIdx += 64;
    059                    return true;
    060                }
    061            };
    062            return bitMapExtractor.processBitMaps(longPredicate::test);
    063        };
    064    }
    065
    066    /**
    067     * Creates an IndexExtractor from an array of integers.
    068     *
    069     * @param values The index values
    070     * @return An IndexExtractor that uses the values.
    071     */
    072    static IndexExtractor fromIndexArray(final int... values) {
    073        return new IndexExtractor() {
    074
    075            @Override
    076            public int[] asIndexArray() {
    077                return values.clone();
    078            }
    079
    080            @Override
    081            public boolean processIndices(final IntPredicate predicate) {
    082                for (final int value : values) {
    083                    if (!predicate.test(value)) {
    084                        return false;
    085                    }
    086                }
    087                return true;
    088            }
    089        };
    090    }
    091
    092    /**
    093     * Return a copy of the IndexExtractor data as an int array.
    094     *
    095     * <p>Indices ordering and uniqueness is not guaranteed.</p>
    096     *
    097     * <p><em>
    098     * The default implementation of this method creates an array and populates
    099     * it.  Implementations that have access to an index array should consider
    100     * returning a copy of that array if possible.
    101     * </em></p>
    102     *
    103     * @return An int array of the data.
    104     */
    105    default int[] asIndexArray() {
    106        final class Indices {
    107            private int[] data = new int[32];
    108            private int size;
    109
    110            boolean add(final int index) {
    111                data = IndexUtils.ensureCapacityForAdd(data, size);
    112                data[size++] = index;
    113                return true;
    114            }
    115
    116            int[] toArray() {
    117                // Edge case to avoid a large array copy
    118                return size == data.length ? data : Arrays.copyOf(data, size);
    119            }
    120        }
    121        final Indices indices = new Indices();
    122        processIndices(indices::add);
    123        return indices.toArray();
    124    }
    125
    126    /**
    127     * Each index is passed to the predicate. The predicate is applied to each
    128     * index value, if the predicate returns {@code false} the execution is stopped, {@code false}
    129     * is returned, and no further indices are processed.
    130     *
    131     * <p>Any exceptions thrown by the action are relayed to the caller.</p>
    132     *
    133     * <p>Indices ordering and uniqueness is not guaranteed.</p>
    134     *
    135     * @param predicate The action to be performed for each non-zero bit index.
    136     * @return {@code true} if all indexes return true from consumer, {@code false} otherwise.
    137     * @throws NullPointerException if the specified action is null
    138     */
    139    boolean processIndices(IntPredicate predicate);
    140
    141    /**
    142     * Creates an IndexExtractor comprising the unique indices for this extractor.
    143     *
    144     * <p>By default creates a new extractor with some overhead to remove
    145     * duplicates.  IndexExtractors that return unique indices by default
    146     * should override this to return {@code this}.</p>
    147     *
    148     * <p>The default implementation will filter the indices from this instance
    149     * and return them in ascending order.</p>
    150     *
    151     * @return The IndexExtractor of unique values.
    152     * @throws IndexOutOfBoundsException if any index is less than zero.
    153     */
    154    default IndexExtractor uniqueIndices() {
    155        final BitSet bitSet = new BitSet();
    156        processIndices(i -> {
    157            bitSet.set(i);
    158            return true;
    159        });
    160
    161        return new IndexExtractor() {
    162            @Override
    163            public boolean processIndices(final IntPredicate predicate) {
    164                for (int idx = bitSet.nextSetBit(0); idx >= 0; idx = bitSet.nextSetBit(idx + 1)) {
    165                    if (!predicate.test(idx)) {
    166                        return false;
    167                    }
    168                }
    169                return true;
    170            }
    171
    172            @Override
    173            public IndexExtractor uniqueIndices() {
    174                return this;
    175            }
    176        };
    177    }
    178}