org.grouplens.lenskit.data.dao.packed.BinaryIndexTable.java Source code

Java tutorial

Introduction

Here is the source code for org.grouplens.lenskit.data.dao.packed.BinaryIndexTable.java

Source

/*
 * LensKit, an open source recommender systems toolkit.
 * Copyright 2010-2014 LensKit Contributors.  See CONTRIBUTORS.md.
 * Work on LensKit has been funded by the National Science Foundation under
 * grants IIS 05-34939, 08-08692, 08-12148, and 10-17697.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package org.grouplens.lenskit.data.dao.packed;

import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.longs.LongSet;
import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.grouplens.lenskit.collections.CopyingFastCollection;
import org.grouplens.lenskit.collections.LongKeyDomain;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.concurrent.ThreadSafe;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.Collection;
import java.util.Iterator;

/**
 * An index table from a byte buffer.
 *
 * @author <a href="http://www.grouplens.org">GroupLens Research</a>
 */
@ThreadSafe
class BinaryIndexTable implements Serializable {
    static final int TABLE_ENTRY_SIZE = BinaryFormat.LONG_SIZE + 2 * BinaryFormat.INT_SIZE;

    private static final long serialVersionUID = -1L;
    private static final Logger logger = LoggerFactory.getLogger(BinaryIndexTable.class);
    private final LongKeyDomain keys;
    private final int[] offsets;
    private final int[] sizes;
    private final IntBuffer buffer;

    private BinaryIndexTable(LongKeyDomain keytbl, int[] offtbl, int[] sztbl, IntBuffer buf) {
        assert offtbl.length == keytbl.domainSize();
        assert sztbl.length == keytbl.domainSize();
        keys = keytbl;
        offsets = offtbl;
        sizes = sztbl;
        buffer = buf;
    }

    /**
     * Create a binary index table.
     * @param nentries The number of entries in the table.
     * @param buffer The table buffer.  Its position will be advanced to the end of the table.
     * @return The index table.
     */
    public static BinaryIndexTable fromBuffer(int nentries, ByteBuffer buffer) {
        logger.debug("reading table of {} entries", nentries);
        long[] keys = new long[nentries];
        int[] offsets = new int[nentries];
        int[] sizes = new int[nentries];
        int nextExpectedOffset = 0;
        for (int i = 0; i < nentries; i++) {
            keys[i] = buffer.getLong();
            if (i > 0 && keys[i - 1] >= keys[i]) {
                logger.error("key {} is not greater than previous key {}", keys[i], keys[i - 1]);
                throw new IllegalArgumentException("corrupted index table");
            }
            offsets[i] = buffer.getInt();
            sizes[i] = buffer.getInt();
            if (offsets[i] != nextExpectedOffset) {
                logger.error("expected offset {}, got {}", nextExpectedOffset, offsets[i]);
                throw new IllegalArgumentException("corrupted index table");
            }
            nextExpectedOffset += sizes[i];
        }
        if (buffer.remaining() < nextExpectedOffset) {
            throw new IllegalArgumentException("buffer not large enough");
        }
        int end = buffer.position() + nextExpectedOffset * 4;
        ByteBuffer dup = buffer.duplicate();
        dup.limit(end);
        buffer.position(end);
        LongKeyDomain dom = LongKeyDomain.wrap(keys, keys.length, true);
        return new BinaryIndexTable(dom, offsets, sizes, dup.asIntBuffer());
    }

    public LongSet getKeys() {
        return keys.activeSetView();
    }

    /**
     * Get the position list for a key.
     * @param key The key.
     * @return The position list.
     */
    public IntList getEntry(long key) {
        int idx = keys.getIndex(key);
        if (idx < 0) {
            return null;
        }

        return getEntryInternal(idx);
    }

    private IntList getEntryInternal(int idx) {
        int offset = offsets[idx];
        int size = sizes[idx];
        IntBuffer buf = buffer.duplicate();
        buf.position(offset).limit(offset + size);
        return BufferBackedIntList.create(buf);
    }

    public Collection<Pair<Long, IntList>> entries() {
        return new EntryCollection();
    }

    private Object writeReplace() throws ObjectStreamException {
        return new SerialProxy(keys, offsets, sizes, buffer);
    }

    private Object readObject(ObjectInputStream in) throws IOException {
        throw new InvalidObjectException("index table must use serial proxy");
    }

    private class EntryCollection extends CopyingFastCollection<Pair<Long, IntList>> {
        @Override
        public int size() {
            return keys.domainSize();
        }

        @Override
        public Iterator<Pair<Long, IntList>> fastIterator() {
            return new FastIterImpl();
        }

        @Override
        protected Pair<Long, IntList> copy(Pair<Long, IntList> elt) {
            return Pair.of(elt.getLeft(), elt.getRight());
        }
    }

    private class FastIterImpl implements Iterator<Pair<Long, IntList>> {
        int pos = 0;
        MutablePair<Long, IntList> pair = new MutablePair<Long, IntList>();

        @Override
        public boolean hasNext() {
            return pos < keys.domainSize();
        }

        @Override
        public Pair<Long, IntList> next() {
            int i = pos;
            pos += 1;
            pair.setLeft(keys.getKey(i));
            pair.setRight(getEntryInternal(i));
            return pair;
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException("remove");
        }
    }

    private static class SerialProxy implements Serializable {
        private static final long serialVersionUID = 1L;

        private final long[] keys;
        private final int[] offsets;
        private final int[] sizes;
        private transient IntBuffer buffer;

        private SerialProxy(LongKeyDomain keys, int[] offsets, int[] sizes, IntBuffer buffer) {
            this.keys = keys.activeSetView().toLongArray();
            this.offsets = offsets;
            this.sizes = sizes;
            this.buffer = buffer.duplicate();
            this.buffer.clear();
        }

        private void writeObject(ObjectOutputStream out) throws IOException {
            out.defaultWriteObject();
            out.writeInt(buffer.limit());
            while (buffer.hasRemaining()) {
                out.writeInt(buffer.get());
            }
        }

        private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
            in.defaultReadObject();
            int size = in.readInt();
            ByteBuffer store = ByteBuffer.allocateDirect(size * 4);
            buffer = store.asIntBuffer();
            assert buffer.remaining() == size;
            while (buffer.hasRemaining()) {
                buffer.put(in.readInt());
            }
            buffer.clear();
        }

        private Object readResolve() throws ObjectStreamException {
            if (keys.length != offsets.length || keys.length != sizes.length) {
                throw new InvalidObjectException("arrays not the same length");
            }
            return new BinaryIndexTable(LongKeyDomain.wrap(keys, keys.length, true), offsets, sizes,
                    buffer.duplicate());
        }
    }
}