com.sangupta.passcode.HashStream.java Source code

Java tutorial

Introduction

Here is the source code for com.sangupta.passcode.HashStream.java

Source

/**
 *
 * PassCode - Password Generator
 * Copyright (c) 2014, Sandeep Gupta
 * 
 * http://sangupta.com/projects/passcode
 * 
 * Licensed 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 com.sangupta.passcode;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;

/**
 * A simple stream of bits that is obtained by generating
 * a binary-representation of a hash/string. Callee's may
 * then consume this continuous stream of bits as a stream
 * reading any number of arbitrary bits and converting them
 * to any required base.
 * 
 * @author sangupta
 *
 */
public class HashStream {

    private final String bits;

    private final Map<Integer, List<Integer>> bases = new HashMap<Integer, List<Integer>>();

    public HashStream(byte[] hash) {
        this.bits = asBinary(hash);
        //      System.out.println("Binary: " + bits);

        this.bases.put(2, map(this.bits, 2));
    }

    private List<Integer> map(String bits, int base) {
        if (base == 2) {
            List<Integer> list = new ArrayList<Integer>();
            for (int index = 0; index < bits.length(); index++) {
                list.add(Integer.parseInt(String.valueOf(bits.charAt(index)), 2));
            }

            return list;
        }

        throw new RuntimeException("not implemented");
    }

    public int generate(int n, int base, boolean inner) {
        int value = n;
        int k = (int) Math.ceil(Math.log(n) / Math.log(base));
        int r = (int) Math.pow(base, k) - n;

        while (value >= n) {
            List<Integer> chunk = this.shift(base, k);

            if (chunk == null) {
                return inner ? n : 0;
            }

            value = this.evaluate(chunk, base);
            if (value >= n) {
                if (r == 1d) {
                    continue;
                }

                this.push(r, value - n);
                value = this.generate(n, r, true);
            }
        }

        return value;
    }

    private int evaluate(List<Integer> chunk, int base) {
        int sum = 0;
        int i = chunk.size();

        while ((i--) > 0) {
            sum += chunk.get(i) * Math.pow(base, chunk.size() - (i + 1));
        }

        return sum;
    }

    private void push(int base, int value) {
        List<Integer> list = this.bases.get(base);
        if (list == null) {
            list = new ArrayList<Integer>();
            this.bases.put(base, list);
        }

        list.add(value);
    }

    private List<Integer> shift(int base, int k) {
        List<Integer> list = this.bases.get(base);
        if (list == null || list.size() < k) {
            return null;
        }

        return splice(list, 0, k);
    }

    private static String asBinary(byte[] hash) {
        String binary = "";
        for (byte b : hash) {
            int unsigned = b & 0xFF;
            String bin = Integer.toBinaryString(unsigned);
            bin = StringUtils.leftPad(bin, 8, '0');
            binary = binary + bin;
        }

        return binary;
    }

    private static List<Integer> splice(List<Integer> list, int index, int howMany) {
        List<Integer> newList = new ArrayList<Integer>();
        final int end = Math.min(index + howMany, list.size());
        if (index > end) {
            return newList;
        }

        for (int i = index; i < end; i++) {
            newList.add(list.remove(index));
        }

        return newList;
    }

}