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

com.sleepycat.je.tree.INLongRep 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.je.tree;

import java.util.Arrays;

import com.sleepycat.je.dbi.MemoryBudget;
import com.sleepycat.je.utilint.SizeofMarker;

/**
 * Holds an array of non-negative long values, one for each slot in an IN.
 *
 * Zero is the default value and is returned when no value has been set.
 *
 * The EMPTY_REP is used at first, and is mutated as necessary as values are
 * set.  A minimum number of bytes per value is used, based on the largest
 * value passed to set().
 *
 * Optionally, a sparse rep is used when a value is set for EMPTY_REP. Up to 4
 * values are stored along with their indexes. When the 5th values is set, the
 * rep is mutated to the default rep.
 *
 * This object calls IN.updateMemorySize to track the memory it uses.
 * EMPTY_REP uses no memory because it is a singleton.
 */
public abstract class INLongRep {

    public abstract long get(int idx);
    public abstract INLongRep set(int idx, long val, IN parent);
    public abstract INLongRep compact(IN parent, EmptyRep emptyRep);
    public abstract INLongRep clear(IN parent, EmptyRep emptyRep);
    public abstract boolean isEmpty();
    public abstract INLongRep copy(int from, int to, int n, IN parent);
    public abstract INLongRep resize(int capacity);
    public abstract long getMemorySize();

    /**
     * Initially empty (all values are zero) but will mutate as needed when
     * non-zero values are passed to set().
     */
    public static class EmptyRep extends INLongRep {

        final int minLength;
        final boolean allowSparseRep;

        public EmptyRep(int minLength, boolean allowSparseRep) {
            this.minLength = minLength;
            this.allowSparseRep = allowSparseRep;
        }

        @Override
        public INLongRep resize(int capacity) {
            return this;
        }

        @Override
        public long get(int idx) {
            return 0;
        }

        /**
         * When adding to the cache the EMPTY_REP is mutated into a
         * DefaultRep.
         */
        @Override
        public INLongRep set(int idx, long val, IN parent) {

            if (val == 0) {
                return this;
            }

            final INLongRep newCache;

            if (false /*TODO*/ && allowSparseRep) {
                newCache = new SparseRep(minLength);
            } else {
                newCache = new DefaultRep(parent.getMaxEntries(), minLength);
            }

            parent.updateMemorySize(getMemorySize(), newCache.getMemorySize());

            return newCache.set(idx, val, parent);
        }

        @Override
        public INLongRep compact(IN parent, EmptyRep emptyRep) {
            return this;
        }

        @Override
        public INLongRep clear(IN parent, EmptyRep emptyRep) {
            return this;
        }

        @Override
        public boolean isEmpty() {
            return true;
        }

        @Override
        public INLongRep copy(int from, int to, int n, IN parent) {
            return this;
        }

        /**
         * An EMPTY_REP has no JE cache memory overhead because there is only
         * one global instance.
         */
        @Override
        public long getMemorySize() {
            return 0;
        }
    }

    public static class DefaultRep extends INLongRep {

        /** Maximum value indexed by number of bytes. */
        private static long[] MAX_VALUE = {
            0x0L,
            0xFFL,
            0xFFFFL,
            0xFFFFFFL,
            0xFFFFFFFFL,
            0xFFFFFFFFFFL,
            0xFFFFFFFFFFFFL,
            0xFFFFFFFFFFFFFFL,
            0x7FFFFFFFFFFFFFFFL,
        };

        private final byte[] byteArray;
        final int bytesPerValue;

        public DefaultRep(int capacity, int nBytes) {
            assert capacity >= 1;
            assert nBytes >= 1;
            assert nBytes <= 8;

            bytesPerValue = nBytes;
            byteArray = new byte[capacity * bytesPerValue];
        }

        /* Only for use by the Sizeof utility. */
        public DefaultRep(@SuppressWarnings("unused") SizeofMarker marker) {
            bytesPerValue = 0;
            byteArray = null;
        }

        private DefaultRep(byte[] byteArray, int bytesPerValue) {
            this.byteArray = byteArray;
            this.bytesPerValue = bytesPerValue;
        }

        @Override
        public DefaultRep resize(int capacity) {
            return new DefaultRep(
                Arrays.copyOfRange(byteArray, 0, capacity * bytesPerValue),
                bytesPerValue);
        }

        @Override
        public long get(int idx) {

            int i = idx * bytesPerValue;
            final int end = i + bytesPerValue;

            long val = (byteArray[i] & 0xFF);

            for (i += 1; i < end; i += 1) {
                val <<= 8;
                val |= (byteArray[i] & 0xFF);
            }

            return val;
        }

        /**
         * Mutates to a DefaultRep with a larger number of bytes if necessary
         * to hold the given value.
         */
        @Override
        public INLongRep set(int idx, long val, IN parent) {

            assert idx >= 0;
            assert idx < byteArray.length / bytesPerValue;
            assert val >= 0;

            /*
             * If the value can't be represented using bytesPerValue, mutate
             * to a cache with a larger number of bytes.
             */
            if (val > MAX_VALUE[bytesPerValue]) {

                final int capacity = byteArray.length / bytesPerValue;

                INLongRep newRep;

                if (getClass() == SparseRep.class) {
                    newRep = new SparseRep(bytesPerValue + 1);
                } else {
                    newRep = new DefaultRep(capacity, bytesPerValue + 1);
                }

                parent.updateMemorySize(
                    getMemorySize(), newRep.getMemorySize());

                /*
                 * Set new value in new cache, and copy other values from old
                 * cache.
                 */
                newRep = newRep.set(idx, val, parent);

                for (int i = 0; i < capacity; i += 1) {
                    if (i != idx) {
                        newRep = newRep.set(i, get(i), parent);
                    }
                }

                return newRep;
            }

            /* Set value in this cache. */
            int i = ((idx + 1) * bytesPerValue) - 1;
            final int end = i - bytesPerValue;

            byteArray[i] = (byte) (val & 0xFF);

            for (i -= 1; i > end; i -= 1) {
                val >>= 8;
                byteArray[i] = (byte) (val & 0xFF);
            }

            assert ((val & 0xFFFFFFFFFFFFFF00L) == 0) : val;

            return this;
        }

        @Override
        public INLongRep compact(IN parent, EmptyRep emptyRep) {

            if (isEmpty()) {
                return clear(parent, emptyRep);
            }

            return this;
        }

        @Override
        public INLongRep clear(IN parent, EmptyRep emptyRep) {

            parent.updateMemorySize(
                getMemorySize(), emptyRep.getMemorySize());

            return emptyRep;
        }

        @Override
        public boolean isEmpty() {

            for (byte b : byteArray) {
                if (b != 0) {
                    return false;
                }
            }

            return true;
        }

        @Override
        public INLongRep copy(int from, int to, int n, IN parent) {
            System.arraycopy(byteArray,
                             from * bytesPerValue,
                             byteArray,
                             to * bytesPerValue,
                             n * bytesPerValue);
            return this;
        }

        @Override
        public long getMemorySize() {
            return MemoryBudget.DEFAULT_LONG_REP_OVERHEAD +
                   MemoryBudget.byteArraySize(byteArray.length);
        }
    }

    public static class SparseRep extends DefaultRep {

        private static final int MAX_ENTRIES = 4;

        private final short[] idxs;

        public SparseRep(int nBytes) {

            super(MAX_ENTRIES, nBytes);

            idxs = new short[MAX_ENTRIES];
            Arrays.fill(idxs, (short) (-1));
        }

        /* Only for use by the Sizeof utility. */
        public SparseRep(@SuppressWarnings("unused") SizeofMarker marker) {
            super(marker);
            idxs = null;
        }

        @Override
        public SparseRep resize(int capacity) {
            return this;
        }

        @Override
        public long get(int idx) {

            for (int i = 0; i < idxs.length; i += 1) {
                if (idxs[i] == idx) {
                    return super.get(i);
                }
            }

            return 0;
        }

        @Override
        public INLongRep set(int idx, long val, IN parent) {

            int slot = -1;

            for (int i = 0; i < idxs.length; i++) {

                if (idxs[i] == idx) {
                    if (val == 0) {
                        idxs[i] = -1;
                    }
                    return super.set(i, val, parent);
                }

                if (slot < 0 && idxs[i] == -1) {
                    slot = i;
                }
            }

            if (val == 0) {
                return this;
            }

            /* Have a free slot, use it. */
            if (slot >= 0) {
                idxs[slot] = (short) idx;
                return super.set(slot, val, parent);
            }

            /* It's full, mutate it. */
            INLongRep newRep =
                new DefaultRep(parent.getMaxEntries(), bytesPerValue);

            parent.updateMemorySize(getMemorySize(), newRep.getMemorySize());

            for (int i = 0; i < idxs.length; i++) {
                if (idxs[i] != -1) {
                    newRep = newRep.set(idxs[i], super.get(i), parent);
                }
            }

            return newRep.set(idx, val, parent);
        }

        @Override
        public INLongRep compact(IN parent, EmptyRep emptyRep) {

            if (isEmpty()) {
                return clear(parent, emptyRep);
            }

            return this;
        }

        @Override
        public INLongRep clear(IN parent, EmptyRep emptyRep) {

            parent.updateMemorySize(
                getMemorySize(), emptyRep.getMemorySize());

            return emptyRep;
        }

        @Override
        public boolean isEmpty() {

            for (short idx : idxs) {
                if (idx != -1) {
                    return false;
                }
            }

            return true;
        }

        @Override
        public INLongRep copy(int from, int to, int n, IN parent) {

            INLongRep target = this;

            if ((to == from) || (n == 0)) {
                /* Nothing to do */
            } else if (to < from) {
                /* Copy ascending */
                for (int i = 0; i < n; i++) {
                    target = target.set(to++, get(from++), parent);
                }
            } else {
                /* to > from. Copy descending */
                from += n;
                to += n;
                for (int i = 0; i < n; i++) {
                    target = target.set(--to, get(--from), parent);
                }
            }

            return target;
        }

        @Override
        public long getMemorySize() {
            return super.getMemorySize() +
                MemoryBudget.SPARSE_LONG_REP_OVERHEAD -
                MemoryBudget.DEFAULT_KEYVALS_OVERHEAD +
                MemoryBudget.shortArraySize(idxs.length);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy