001/*
002 * Copyright 2013 Alex Kasko (alexkasko.com)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.alexkasko.unsafe.offheapstruct;
018
019import java.util.Comparator;
020
021/**
022 * Binary search implementation borrowed from {@code https://android.googlesource.com/platform/libcore/+/android-4.2.2_r1/luni/src/main/java/java/util/Arrays.java}
023 * and adapted to {@link com.alexkasko.unsafe.offheapstruct.OffHeapStructCollection} with comparators.
024 * Search methods are not static in this class to prevent internal comparator wrapper object creation on each search call.
025 * All operations on instances of this class are NOT thread-safe.
026 *
027 * @author alexkasko
028 * Date: 9/13/13
029 */
030public class OffHeapStructBinarySearchWithComparator {
031    private final OffHeapStructCollection col;
032    private final OffHeapStructComparator comp;
033
034    /**
035     * Constructor
036     *
037     * @param collection collection to search over
038     * @param comparator comparator to use for comparing provided structure while searching
039     */
040    public OffHeapStructBinarySearchWithComparator(OffHeapStructCollection collection, Comparator<OffHeapStructAccessor> comparator) {
041        if(null == collection) throw new IllegalArgumentException("Provided collection is null");
042        if(null == comparator) throw new IllegalArgumentException("Provided comparator is null");
043        this.col = collection;
044        this.comp = new OffHeapStructComparator(collection, comparator);
045    }
046
047    /**
048     * Performs a binary search for {@code value} in the ascending sorted off-heap struct collection
049     * comparing provided struct using comparator (both collection and comparator are bound to searcher instance).
050     * Searching in an unsorted collection has an undefined result. It's also undefined which element
051     * is found if there are multiple occurrences of the same element.
052     *
053     * @param struct the structure to find
054     * @return the non-negative index of the element, or a negative index which
055     *         is {@code -index - 1} where the element would be inserted.
056     */
057    public long binarySearch(byte[] struct) {
058        return binarySearch(0, col.size(), struct);
059    }
060
061    /**
062     * Performs a binary search for {@code value} in the ascending sorted off-heap struct collection
063     * comparing provided struct using comparator (both collection and comparator are bound to searcher instance).
064     * in the range specified by fromIndex (inclusive) and toIndex (exclusive).
065     * Searching in an unsorted collection has an undefined result. It's also undefined which element
066     * is found if there are multiple occurrences of the same element.
067     *
068     * @param startIndex the inclusive start index.
069     * @param endIndex   the exclusive end index.
070     * @param struct the structure to find
071     * @return the non-negative index of the element, or a negative index which
072     *         is {@code -index - 1} where the element would be inserted.
073     * @throws IllegalArgumentException {@code if (startIndex < 0 || startIndex > endIndex || endIndex > collection.size()}
074     */
075    public long binarySearch(long startIndex, long endIndex, byte[] struct) {
076        if (startIndex < 0 || startIndex > endIndex || endIndex > col.size()) {
077            throw new IllegalArgumentException("Illegal input, collection size: [" + col.size() + "], " +
078                    "startIndex: [" + startIndex + "], endIndex: [" + endIndex + "]");
079        }
080        if(null == struct) throw new IllegalArgumentException("Provided struct is null");
081
082        int compres;
083        long lo = startIndex;
084        long hi = endIndex - 1;
085        while (lo <= hi) {
086            long mid = (lo + hi) >>> 1;
087            compres = comp.compare(mid, struct);
088            if (compres < 0) {
089                lo = mid + 1;
090            } else if (compres > 0) {
091                hi = mid - 1;
092            } else {
093                return mid;  // value found
094            }
095        }
096        return ~lo;  // value not present
097    }
098
099    /**
100     * Performs a binary search for {@code value} in the ascending sorted off-heap struct collection
101     * comparing provided struct using comparator (both collection and comparator are bound to searcher instance).
102     * Returns range of indices having given value or empty range.
103     * Searching in an unsorted collection has an undefined result. It's also undefined which element
104     * is found if there are multiple occurrences of the same element.
105     *
106     * @param struct the structure to find
107     * @param out range instance, will be set with start/end indices having given value or with empty value
108     */
109    public void binarySearchRange(byte[] struct, OffHeapStructBinarySearch.IndexRange out) {
110        binarySearchRange(0, col.size(), struct, out);
111    }
112
113    /**
114     * Performs a binary search for {@code value} in the ascending sorted off-heap struct collection
115     * comparing provided struct using comparator (both collection and comparator are bound to searcher instance).
116     * Returns range of indices having given value or empty range.
117     * Searching in an unsorted collection has an undefined result. It's also undefined which element
118     * is found if there are multiple occurrences of the same element.
119     *
120     * @param startIndex the inclusive start index.
121     * @param endIndex   the exclusive end index.
122     * @param struct the structure to find
123     * @param out range instance, will be set with start/end indices having given value or with empty value
124     */
125    public void binarySearchRange(long startIndex, long endIndex, byte[] struct, OffHeapStructBinarySearch.IndexRange out) {
126        long ind = binarySearch(startIndex, endIndex, struct);
127        if(ind < 0) {
128            out.setEmpty(ind);
129            return;
130        }
131        long from = ind;
132        while (from >= startIndex && comp.eq(from, struct)) {
133            from -= 1;
134        }
135        from += 1;
136        long to = ind;
137        while (to < endIndex && comp.eq(to, struct)) {
138            to += 1;
139        }
140        to -= 1;
141        out.set(from, to);
142    }
143
144    /**
145     * {@inheritDoc}
146     */
147    @Override
148    public String toString() {
149        final StringBuilder sb = new StringBuilder();
150        sb.append("OffHeapStructBinarySearchWithComparator");
151        sb.append("{col=").append(col);
152        sb.append(", comp=").append(comp);
153        sb.append('}');
154        return sb.toString();
155    }
156}