com.offbynull.peernetic.playground.chorddht.model.SuccessorTable.java Source code

Java tutorial

Introduction

Here is the source code for com.offbynull.peernetic.playground.chorddht.model.SuccessorTable.java

Source

/*
 * Copyright (c) 2013-2014, Kasra Faghihi, All rights reserved.
 * 
 * This library 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 3.0 of the License, or (at your option) any later version.
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.
 */
package com.offbynull.peernetic.playground.chorddht.model;

import com.offbynull.peernetic.common.identification.Id;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.Validate;

/**
 * Holds on to a list of successors. The maximum number of successors this implementation can hold is the bit size in the base pointer's id
 * limit. The list  is assumed to be recursively generated. For more information on how a  successor table operates, please refer to the
 * Chord research paper.
 * @param <A> address type
 * @author Kasra Faghihi
 */
public final class SuccessorTable<A> {
    /**
     * Base (self) pointer.
     */
    private InternalPointer basePtr;
    /**
     * List of successors.
     */
    private ArrayDeque<Pointer> table;
    /**
     * Maximum number of successors that can be held (equal to bit size of basePtr's id limit).
     */
    private int limit;

    /**
     * Constructs a {@link SuccessorTable} object.
     * @param basePtr base pointer
     * @throws NullPointerException if any arguments are {@code null}
     */
    public SuccessorTable(InternalPointer basePtr) {
        Validate.notNull(basePtr);
        Id baseId = basePtr.getId();
        Validate.isTrue(IdUtils.isUseableId(baseId)); // make sure satisfies 2^n-1

        this.basePtr = basePtr;
        limit = IdUtils.getBitLength(baseId);

        table = new ArrayDeque<>(limit);
        table.add(basePtr);
    }

    /**
     * Get the base pointer.
     * @return base pointer
     */
    public InternalPointer getBase() {
        return basePtr;
    }

    /**
     * Get the id from the base pointer. Equivalent to calling {@code getBase().getId()}.
     * @return base pointer id
     */
    public Id getBaseId() {
        return basePtr.getId();
    }

    /**
     * Trims all successors below {@code successor}, then adds {@code successor} to the beginning of the successor list. If
     * {@code successor} is the base pointer, all other successors will be cleared out.
     * @param successor new immediate successor
     * @throws NullPointerException if any arguments are {@code null}
     * @throws IllegalArgumentException if {@code successor}'s id has a different limit bit size than the base pointer's id
     */
    public void updateTrim(Pointer successor) {
        Validate.notNull(successor);

        Id baseId = basePtr.getId();
        Id successorId = successor.getId();

        Validate.isTrue(IdUtils.getBitLength(successorId) == limit);

        if (baseId.equals(successorId)) { // test above makes sure if this is true, successor will be of correct type
            table = new ArrayDeque<>();
            table.add(successor);
            return;
        }

        while (!table.isEmpty()) {
            Pointer ptr = table.removeFirst();

            Id ptrId = ptr.getId();
            if (Id.comparePosition(baseId, ptrId, successorId) > 0) {
                table.addFirst(ptr);
                break;
            }
        }

        table.addFirst(successor);

        if (table.size() > limit) {
            table.removeLast();
        }
    }

    /**
     * Updates this successor table so that the immediate successor is {@code successor} and the successors after it are {@code table}. If
     * {@code table} contains the base pointer in it, all pointers including and after the base pointer won't be added. If {@code table}'s
     * size exceeds the number of bits in base pointer's id, it'll be trimmed so that it doesn't exceed. The pointers in {@code table} must
     * be sorted. The point at which {@code table} loops around our base id is the point that it'll be trimmed.
     * @param successor immediate successor
     * @param table successors after {@code successor}
     * @throws NullPointerException if any arguments are {@code null} or contain {@code null}
     * @throws IllegalArgumentException if {@code successor}'s id has a different limit bit size than the base pointer's id, or if the ids
     * of any of the pointers in {@code table} have a different limit bit size than the base pointer's id
     */
    public void update(Pointer successor, List<Pointer> table) {
        Validate.notNull(successor);
        Validate.noNullElements(table);

        Id baseId = basePtr.getId();
        Id successorId = successor.getId();

        Validate.isTrue(IdUtils.getBitLength(successorId) == limit);

        if (table.size() > limit) {
            table = table.subList(0, limit);
        }

        Id lastId = successor.getId();
        int lastTableIdx = -1;
        int idx = 0;
        for (Pointer ptrSuccessor : table) {
            Id ptrSuccessorId = ptrSuccessor.getId();

            Validate.isTrue(IdUtils.getBitLength(ptrSuccessorId) == limit);

            if (baseId.equals(ptrSuccessorId)) {
                lastTableIdx = idx;
                break;
            }

            if (Id.comparePosition(baseId, ptrSuccessorId, lastId) <= 0) {
                lastTableIdx = idx;
                break;
            }

            lastId = ptrSuccessor.getId();

            idx++;
        }

        if (lastTableIdx != -1) {
            table = table.subList(0, lastTableIdx);
        }

        int len = table.size() + 1;
        ArrayDeque<Pointer> newTable = new ArrayDeque<>(len);

        newTable.add(successor);
        table.forEach(x -> {
            Id id = x.getId();
            if (id.equals(baseId)) {
                newTable.add(new InternalPointer(id)); // if referencing self, use internalpointer
            } else {
                newTable.add(x);
            }
        });

        this.table = newTable;
    }

    /**
     * Gets the immediate successor.
     * @return immediate successor
     */
    public Pointer getSuccessor() {
        return table.peekFirst();
    }

    /**
     * Moves to the next successor.
     * @throws IllegalStateException if there's only one successor in the
     * successor table
     */
    public void moveToNextSucessor() {
        if (table.size() == 1) {
            throw new IllegalStateException();
        }

        table.removeFirst();
    }

    /**
     * Checks to see if the immediate successor is pointing to base.
     * @return {@code true} if the immediate successor is base, {@code false}
     * otherwise
     */
    public boolean isPointingToBase() {
        Pointer ptr = table.getFirst();
        return ptr instanceof InternalPointer;
    }

    /**
     * Dumps out the successor table.
     * @return list of successor table
     */
    public List<Pointer> dump() {
        return new ArrayList<>(table);
    }
}