io.hops.erasure_coding.SimpleRegeneratingCode.java Source code

Java tutorial

Introduction

Here is the source code for io.hops.erasure_coding.SimpleRegeneratingCode.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.hops.erasure_coding;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONException;

import java.util.ArrayList;
import java.util.List;

public class SimpleRegeneratingCode extends ErasureCode {
    public static final Log LOG = LogFactory.getLog(SimpleRegeneratingCode.class);
    private int stripeSize;
    private int paritySize;
    private int paritySizeSRC;
    private int paritySizeRS;
    private int simpleParityDegree;
    private int[] generatingPolynomial;
    private int PRIMITIVE_ROOT = 2;
    private int[] primitivePower;
    private GaloisField GF = GaloisField.getInstance();
    private int[] errSignature;
    private int[] dataBuff;
    private int[][] groupsTable;

    @Deprecated
    public SimpleRegeneratingCode(int stripeSize, int paritySize) {
        init(stripeSize, paritySize);
    }

    public SimpleRegeneratingCode() {

    }

    @Override
    public void init(Codec codec) {
        try {
            this.paritySizeSRC = codec.json.getInt("parity_length_src");
        } catch (JSONException e) {
            LOG.error("Exception", e);
        }
        init(codec.stripeLength, codec.parityLength);
        LOG.info(" Initialized " + SimpleRegeneratingCode.class + " stripeLength:" + codec.stripeLength
                + " parityLength:" + codec.parityLength + " SRC parities:" + paritySizeSRC);
    }

    private void init(int stripeSize, int paritySize) {
        this.stripeSize = stripeSize;
        this.paritySize = paritySize;
        this.paritySizeRS = paritySize - paritySizeSRC;

        assert (stripeSize + paritySizeRS < GF.getFieldSize());
        assert (paritySize >= paritySizeSRC);

        // The degree of a simple parity is the number of locations
        // combined into the single parity. The degree is a function
        // of the RS-stripe (stripe + RS parity) length.
        // (The number of SRC groups is paritySizeSRC + 1, because
        // one SRC parity is implied -- not stored).
        simpleParityDegree = (int) Math.ceil((double) (stripeSize + paritySizeRS) / (double) (paritySizeSRC + 1));

        while (simpleParityDegree * paritySizeSRC >= stripeSize + paritySizeRS) {
            LOG.info("\nInvalid code parameters." + " Reducing SRC parities to " + (paritySizeSRC - 1)
                    + " Increasing RS parities to " + (paritySizeRS + 1));
            this.paritySizeSRC--;
            this.paritySizeRS++;
            simpleParityDegree = (int) Math
                    .ceil((double) (stripeSize + paritySizeRS) / (double) (paritySizeSRC + 1));
        }

        this.errSignature = new int[paritySizeRS];
        this.dataBuff = new int[paritySizeRS + stripeSize];

        this.primitivePower = new int[stripeSize + paritySizeRS];
        // compute powers of the primitive root
        for (int i = 0; i < stripeSize + paritySizeRS; i++) {
            primitivePower[i] = GF.power(PRIMITIVE_ROOT, i);
        }

        // compute generating polynomial
        int[] gen = { 1 };
        int[] poly = new int[2];
        for (int i = 0; i < paritySizeRS; i++) {
            poly[0] = primitivePower[i];
            poly[1] = 1;
            gen = GF.multiply(gen, poly);
        }

        // generating polynomial has all generating roots
        generatingPolynomial = gen;

        // groupsTable[][]
        // groupsTable[loc]: the SRC group neighbors of location loc.
        groupsTable = new int[paritySize + stripeSize][];
        for (int i = 0; i < groupsTable.length; i++) {
            List<Integer> locationsInGroup = getSRCGroupNeighbors(i);
            groupsTable[i] = new int[locationsInGroup.size()];
            int k = 0;
            for (int loc : locationsInGroup) {
                groupsTable[i][k++] = loc;
            }
        }
    }

    @Override
    public void encode(int[] message, int[] parity) {
        assert (message.length == stripeSize && parity.length == paritySize);
        // initialize data buffer
        for (int i = 0; i < paritySizeRS; i++) {
            dataBuff[i] = 0;
        }

        // put message in the data buffer
        for (int i = 0; i < stripeSize; i++) {
            dataBuff[i + paritySizeRS] = message[i];
        }

        // calculate RS parities and copy into parity[]
        GF.remainder(dataBuff, generatingPolynomial);
        for (int i = 0; i < paritySizeRS; i++) {
            parity[i + paritySizeSRC] = dataBuff[i];
        }

        // restore message in dataBuff
        for (int i = 0; i < stripeSize; i++) {
            dataBuff[i + paritySizeRS] = message[i];
        }

        // compute the SRC parities and store into parity[]
        for (int i = 0; i < paritySizeSRC; i++) {
            parity[i] = 0;
            for (int j = simpleParityDegree * i; j < simpleParityDegree * (i + 1); j++) {
                parity[i] = GF.add(dataBuff[j], parity[i]);
            }
        }
    }

    /*
     * Perform Reed Solomon decoding.
     */
    private void decodeReedSolomon(int[] data, int[] erasedLocations, int[] erasedValues) {

        if (erasedLocations.length == 0) {
            return;
        }

        assert (erasedLocations.length == erasedValues.length);
        assert (erasedLocations.length <= paritySizeRS);

        for (int i = 0; i < erasedLocations.length; i++) {
            data[erasedLocations[i]] = 0;
        }

        for (int i = 0; i < erasedLocations.length; i++) {
            errSignature[i] = primitivePower[erasedLocations[i]];
            erasedValues[i] = GF.substitute(data, primitivePower[i]);
        }

        GF.solveVandermondeSystem(errSignature, erasedValues, erasedLocations.length);
    }

    /*
     * Performs Reed Solomon decoding, assuming that all positions not included in
     * erasedLocations are available.
     */
    @Override
    public void decode(int[] data, int[] erasedLocations, int[] erasedValues) {
        decodeReedSolomon(data, erasedLocations, erasedValues);
    }

    @Override
    public void decode(int[] data, int[] erasedLocations, int[] erasedValues, int[] locationsToRead,
            int[] locationsNotToRead) {

        assert (erasedLocations.length == erasedValues.length);

        // CASE 1 : SINGLE ERASURE
        // If only one erasure is passed, perform a quick repair using the
        // local group (locationsToRead).

        if (erasedLocations.length == 1) {
            erasedValues[0] = 0;
            for (int i = 0; i < locationsToRead.length; i++) {
                erasedValues[0] = GF.add(data[locationsToRead[i]], erasedValues[0]);
            }
            return;
        }

        // CASE 2 : MULTIPLE ERASURES - NO CONFLICT

        if (!groupConflict(erasedLocations)) {
            for (int i = 0; i < erasedLocations.length; i++) {
                int[] singleErasedLocation = new int[1];
                singleErasedLocation[0] = erasedLocations[i];
                int[] singleErasedValue = new int[1];
                singleErasedValue[0] = 0;

                decode(data, singleErasedLocation, singleErasedValue, groupsTable[erasedLocations[i]], null);

                erasedValues[i] = singleErasedValue[0];
            }
            return;
        }

        // CASE 3 : MULTIPLE ERASURES - CONFLICT
        // According to locationsToReadForDecode(), locationsToRead should
        // be of length equal to stripeSize for RS decoding.

        assert (locationsToRead.length == stripeSize);
        assert (locationsNotToRead.length == stripeSize + paritySize - locationsToRead.length);

        // count the number of src parities that are erased
        int numOferasedSRCparities = 0;
        for (int i = 0; i < erasedLocations.length; i++) {
            if (erasedLocations[i] < paritySizeSRC) {
                numOferasedSRCparities++;
            }
        }

        int[] dataRS = new int[paritySizeRS + stripeSize];
        for (int i = 0; i < paritySizeRS + stripeSize; i++) {
            dataRS[i] = data[i + paritySizeSRC];
        }

        /*
         * erasedLocationsRS contains actual erased locations of the RS stripe
         * plus some locations that are not supposed to be read.
         */
        int[] erasedLocationsRS = new int[locationsNotToRead.length - this.paritySizeSRC];
        int k = 0;
        for (int i = 0; i < locationsNotToRead.length; i++) {
            if (locationsNotToRead[i] >= paritySizeSRC) {
                erasedLocationsRS[k++] = locationsNotToRead[i] - paritySizeSRC;
            }
        }

        int[] erasedValuesRS = new int[erasedLocationsRS.length];

        decodeReedSolomon(dataRS, erasedLocationsRS, erasedValuesRS);

        for (int i = 0; i < erasedLocationsRS.length; i++) {
            data[paritySizeSRC + erasedLocationsRS[i]] = erasedValuesRS[i];
        }

        // now that the RS part is all fixed, fix the simple parities
        for (int i = 0; i < erasedLocations.length; i++) {
            if (erasedLocations[i] < paritySizeSRC) {
                int par = erasedLocations[i];
                data[par] = 0;
                for (int j = 0; j < groupsTable[erasedLocations[i]].length; j++) {
                    data[par] = GF.add(data[groupsTable[erasedLocations[i]][j]], data[par]);
                }
            }
        }

        for (int i = 0; i < erasedLocations.length; i++) {
            erasedValues[i] = data[erasedLocations[i]];
        }

        return;

    }

    @Override
    public int stripeSize() {
        return this.stripeSize;
    }

    @Override
    public int paritySize() {
        return this.paritySize;
    }

    @Override
    public int symbolSize() {
        return (int) Math.round(Math.log(GF.getFieldSize()) / Math.log(2));
    }

    /**
     * Figure out which locations need to be read to decode erased locations. The
     * locations are specified as integers in the range [ 0, stripeSize() +
     * paritySize() ). Values in the range [ 0, paritySize() ) represent parity
     * data. Values in the range [ paritySize(), paritySize() + stripeSize() )
     * represent message data.
     *
     * @param erasedLocations
     *     The erased locations.
     * @return The locations to read.
     */
    @Override
    public List<Integer> locationsToReadForDecode(List<Integer> erasedLocations) throws TooManyErasedLocations {

        //LOG.info("Erased locations: "+erasedLocations.toString());

        List<Integer> locationsToRead;

        // If only one location is erased, return its local (src) group
        if (erasedLocations.size() == 1) {
            //locationsToRead = computeSRCGroupLocations(erasedLocations.get(0));
            int loc = erasedLocations.get(0);
            locationsToRead = new ArrayList<Integer>(groupsTable[loc].length);
            for (int i = 0; i < groupsTable[loc].length; i++) {
                locationsToRead.add(groupsTable[loc][i]);
            }
            //LOG.info("locations to read: "+locationsToRead.toString());
            return locationsToRead;
        }

        // If more than one location are erased, check if they belong to the same group.
        // If they do not belong to same group (- no conflict), add locations of each
        // separate group.
        int[] erasedLocationsArray = new int[erasedLocations.size()];
        for (int i = 0; i < erasedLocations.size(); i++) {
            erasedLocationsArray[i] = erasedLocations.get(i);
        }

        if (!groupConflict(erasedLocationsArray)) {
            // we expect approximately simpleParityDegree locations to be read for each
            // erased location.
            locationsToRead = new ArrayList<Integer>(erasedLocations.size() * simpleParityDegree);
            // add unique locations
            for (int loc : erasedLocations) {
                for (int i = 0; i < groupsTable[loc].length; i++) {
                    if (!locationsToRead.contains(groupsTable[loc][i])) {
                        locationsToRead.add(groupsTable[loc][i]);
                    }
                }
            }
            return locationsToRead;
        }

        // If more than one location is erased and there is at least a pair belonging to the
        // same group, we will have to perform Reed Solomon (RS) decoding. Hence, read
        // locations that are necessary for RS decoding.
        locationsToRead = new ArrayList<Integer>(stripeSize());
        int limit = stripeSize() + paritySize();

        //Loop through all possible locations in the stripe, omitting the SRC parities.
        for (int loc = paritySizeSRC; loc < limit; loc++) {
            //Is the location good.
            if (erasedLocations.indexOf(loc) == -1) {
                locationsToRead.add(loc);
                if (stripeSize() == locationsToRead.size()) {
                    break;
                }
            }
        }
        // If we are are not able to fill up the locationsToRead list,
        // we did not find enough good locations. Throw TooManyErasedLocations.
        if (locationsToRead.size() != stripeSize()) {
            String locationsStr = "";
            for (Integer erasedLocation : erasedLocations) {
                locationsStr += " " + erasedLocation;
            }
            throw new TooManyErasedLocations("Locations " + locationsStr);
        }
        return locationsToRead;
    }

    /*
     * Given a location loc, return a list with the other locations
     * belonging to the same SRC group as loc.
     */
    private List<Integer> getSRCGroupNeighbors(int loc) {
        int limit = stripeSize() + paritySize();

        /* A group is expected to have at most simpleParityDegree + 1
         * locations. groupLocations will contain the simpleParityDegree
         * neighbors of loc.
         */

        List<Integer> neighbors = new ArrayList<Integer>(simpleParityDegree);
        int group = getSRCGroup(loc);

        // CASE 1: Group with "stored" SRC parity.
        if (group < paritySizeSRC) {
            if (group != loc)
            // group equals the location of the SRC parity
            // Hence, add the location to the neighbors.
            {
                neighbors.add(group);
            }
            // add the rest neighbors (loc is excluded)
            for (int i = paritySizeSRC + group * simpleParityDegree; i < paritySizeSRC
                    + (group + 1) * simpleParityDegree; i++) {
                if (i != loc) {
                    neighbors.add(i);
                }
            }
        } // CASE 2: Group is the one with the "inferred" SRC parity.
        else {
            assert (loc >= paritySizeSRC);
            // All SRC parities are neighbors.
            for (int i = 0; i < paritySizeSRC; i++) {
                neighbors.add(i);
            }
            // Add the remaining (non SRC-parity) neighbors.
            for (int i = paritySizeSRC + group * simpleParityDegree; i < limit; i++) {
                if (i != loc) {
                    neighbors.add(i);
                }
            }
        }
        return neighbors;
    }

    /*
     * Return the id of the SRC group location loc belongs to.
     * Return -1 for invalid loc.
     */
    private int getSRCGroup(int loc) {
        int group = -1;
        if (0 <= loc && loc < paritySizeSRC) {
            group = loc;
        } else if (paritySizeSRC <= loc && loc < stripeSize + paritySize) {
            group = (int) (loc - paritySizeSRC) / simpleParityDegree;
        } else {
            group = -1;
        }

        return group;
    }

    /*
     * Check for conflict (- whether any two locations in locs
     * belong to the same SRC group.
     */
    private boolean groupConflict(int[] locs) {
        int[] groups = new int[paritySizeSRC + 1];
        for (int i = 0; i < groups.length; i++) {
            groups[i] = 0;
        }
        /*
         * if at least one position in locs is SRC parity,
         * mark the last group.
         */
        for (int i = 0; i < locs.length; i++) {
            if (locs[i] < paritySizeSRC) {
                groups[paritySizeSRC] = 1;
                break;
            }
        }

        for (int i = 0; i < locs.length; i++) {
            if (groups[getSRCGroup(locs[i])]++ > 0) {
                return true;
            }
        }
        return false;
    }
}