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

com.sleepycat.util.keyrange.KeyRange Maven / Gradle / Ivy

The newest version!
/*-
 * Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
 *
 * This file was distributed by Oracle as part of a version of Oracle Berkeley
 * DB Java Edition made available at:
 *
 * http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
 *
 * Please see the LICENSE file included in the top-level directory of the
 * appropriate version of Oracle Berkeley DB Java Edition for a copy of the
 * license and additional information.
 */

package com.sleepycat.util.keyrange;

import java.util.Comparator;

import com.sleepycat.je.DatabaseEntry;

/**
 * Encapsulates a key range for use with a RangeCursor.
 */
public class KeyRange {

    /*
     * We can return the same byte[] for 0 length arrays.
     */
    public static final byte[] ZERO_LENGTH_BYTE_ARRAY = new byte[0];

    Comparator comparator;
    DatabaseEntry beginKey;
    DatabaseEntry endKey;
    boolean singleKey;
    boolean beginInclusive;
    boolean endInclusive;

    /**
     * Creates an unconstrained key range.
     */
    public KeyRange(Comparator comparator) {
        this.comparator = comparator;
    }

    /**
     * Creates a range for a single key.
     */
    public KeyRange subRange(DatabaseEntry key)
        throws KeyRangeException {

        if (!check(key)) {
            throw new KeyRangeException("singleKey out of range");
        }
        KeyRange range = new KeyRange(comparator);
        range.beginKey = key;
        range.endKey = key;
        range.beginInclusive = true;
        range.endInclusive = true;
        range.singleKey = true;
        return range;
    }

    /**
     * Creates a range that is the intersection of this range and the given
     * range parameters.
     */
    public KeyRange subRange(DatabaseEntry beginKey, boolean beginInclusive,
                             DatabaseEntry endKey, boolean endInclusive)
        throws KeyRangeException {

        if (beginKey == null) {
            beginKey = this.beginKey;
            beginInclusive = this.beginInclusive;
        } else if (!check(beginKey, beginInclusive)) {
            throw new KeyRangeException("beginKey out of range");
        }
        if (endKey == null) {
            endKey = this.endKey;
            endInclusive = this.endInclusive;
        } else if (!check(endKey, endInclusive)) {
            throw new KeyRangeException("endKey out of range");
        }
        KeyRange range = new KeyRange(comparator);
        range.beginKey = beginKey;
        range.endKey = endKey;
        range.beginInclusive = beginInclusive;
        range.endInclusive = endInclusive;
        return range;
    }

    /**
     * Returns whether this is a single-key range.
     */
    public final boolean isSingleKey() {
        return singleKey;
    }

    /**
     * Returns the key of a single-key range, or null if not a single-key
     * range.
     */
    public final DatabaseEntry getSingleKey() {

        return singleKey ? beginKey : null;
    }

    /**
     * Returns whether this range has a begin or end bound.
     */
    public final boolean hasBound() {

        return endKey != null || beginKey != null;
    }

    /**
     * Formats this range as a string for debugging.
     */
    @Override
    public String toString() {

        return "[KeyRange " + beginKey + ' ' + beginInclusive +
                              endKey + ' ' + endInclusive +
                              (singleKey ? " single" : "");
    }

    /**
     * Returns whether a given key is within range.
     */
    public boolean check(DatabaseEntry key) {

        if (singleKey) {
            return (compare(key, beginKey) == 0);
        } else {
            return checkBegin(key, true) && checkEnd(key, true);
        }
    }

    /**
     * Returns whether a given key is within range.
     */
    public boolean check(DatabaseEntry key, boolean inclusive) {

        if (singleKey) {
            return (compare(key, beginKey) == 0);
        } else {
            return checkBegin(key, inclusive) && checkEnd(key, inclusive);
        }
    }

    /**
     * Returns whether the given key is within range with respect to the
     * beginning of the range.
     *
     * 

The inclusive parameter should be true for checking a key read from * the database; this will require that the key is within range. When * inclusive=false the key is allowed to be equal to the beginKey for the * range; this is used for checking a new exclusive bound of a * sub-range.

* *

Note that when inclusive=false and beginInclusive=true our check is * not exactly correct because in theory we should allow the key to be "one * less" than the existing bound; however, checking for "one less" is * impossible so we do the best we can and test the bounds * conservatively.

*/ public boolean checkBegin(DatabaseEntry key, boolean inclusive) { if (beginKey == null) { return true; } else if (!beginInclusive && inclusive) { return compare(key, beginKey) > 0; } else { return compare(key, beginKey) >= 0; } } /** * Returns whether the given key is within range with respect to the * end of the range. See checkBegin for details. */ public boolean checkEnd(DatabaseEntry key, boolean inclusive) { if (endKey == null) { return true; } else if (!endInclusive && inclusive) { return compare(key, endKey) < 0; } else { return compare(key, endKey) <= 0; } } /** * Compares two keys, using the user comparator if there is one. */ public int compare(DatabaseEntry key1, DatabaseEntry key2) { if (comparator != null) { return comparator.compare(getByteArray(key1), getByteArray(key2)); } else { return compareBytes (key1.getData(), key1.getOffset(), key1.getSize(), key2.getData(), key2.getOffset(), key2.getSize()); } } /** * Copies a byte array. */ public static byte[] copyBytes(byte[] bytes) { byte[] a = new byte[bytes.length]; System.arraycopy(bytes, 0, a, 0, a.length); return a; } /** * Compares two keys as unsigned byte arrays, which is the default * comparison used by JE/DB. */ public static int compareBytes(byte[] data1, int offset1, int size1, byte[] data2, int offset2, int size2) { for (int i = 0; i < size1 && i < size2; i++) { int b1 = 0xFF & data1[offset1 + i]; int b2 = 0xFF & data2[offset2 + i]; if (b1 < b2) return -1; else if (b1 > b2) return 1; } if (size1 < size2) return -1; else if (size1 > size2) return 1; else return 0; } /** * Compares two byte arrays for equality. */ public static boolean equalBytes(byte[] data1, int offset1, int size1, byte[] data2, int offset2, int size2) { if (size1 != size2) { return false; } for (int i = 0; i < size1; i += 1) { if (data1[i + offset1] != data2[i + offset2]) { return false; } } return true; } /** * Returns a copy of an entry. */ public static DatabaseEntry copy(DatabaseEntry from) { return new DatabaseEntry(getByteArray(from)); } /** * Copies one entry to another. */ public static void copy(DatabaseEntry from, DatabaseEntry to) { to.setData(getByteArray(from)); to.setOffset(0); } /** * Returns an entry's byte array, copying it if the entry offset is * non-zero. */ public static byte[] getByteArray(DatabaseEntry entry) { return getByteArrayInternal(entry, Integer.MAX_VALUE); } public static byte[] getByteArray(DatabaseEntry entry, int maxBytes) { return getByteArrayInternal(entry, maxBytes); } private static byte[] getByteArrayInternal(DatabaseEntry entry, int maxBytes) { byte[] bytes = entry.getData(); if (bytes == null) return null; int size = Math.min(entry.getSize(), maxBytes); byte[] data; if (size == 0) { data = ZERO_LENGTH_BYTE_ARRAY; } else { data = new byte[size]; System.arraycopy(bytes, entry.getOffset(), data, 0, size); } return data; } /** * Returns the two DatabaseEntry objects have the same data value. */ public static boolean equalBytes(DatabaseEntry e1, DatabaseEntry e2) { if (e1 == null && e2 == null) { return true; } if (e1 == null || e2 == null) { return false; } byte[] d1 = e1.getData(); byte[] d2 = e2.getData(); int s1 = e1.getSize(); int s2 = e2.getSize(); int o1 = e1.getOffset(); int o2 = e2.getOffset(); if (d1 == null && d2 == null) { return true; } if (d1 == null || d2 == null) { return false; } if (s1 != s2) { return false; } for (int i = 0; i < s1; i += 1) { if (d1[o1 + i] != d2[o2 + i]) { return false; } } return true; } /** * Converts the byte array of this thang to space-separated integers, * and suffixed by the record number if applicable. * * @param dbt the thang to convert. * * @return the resulting string. */ public static String toString(DatabaseEntry dbt) { int len = dbt.getOffset() + dbt.getSize(); StringBuilder buf = new StringBuilder(len * 2); byte[] data = dbt.getData(); for (int i = dbt.getOffset(); i < len; i++) { String num = Integer.toHexString(data[i]); if (num.length() < 2) buf.append('0'); buf.append(num); } return buf.toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy