Package org.apache.accumulo.core.security

Source Code of org.apache.accumulo.core.security.ByteArrayTrie$State

/*
* 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 org.apache.accumulo.core.security;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.accumulo.core.util.ByteArrayComparator;

public class ByteArrayTrie {
 
  private ArrayList<Byte> objectTree;
  private byte[] tree;
 
  private static class State {
    int start;
    int size;
    boolean accept;
    boolean failed;
  }
 
  public ByteArrayTrie(Collection<byte[]> values) {
    create(values);
  }
 
  private void create(Collection<byte[]> values) {
   
    objectTree = new ArrayList<Byte>();
    // add a leaf state (shared)
    objectTree.add((byte) 0x0);
    objectTree.add((byte) 0x1);
   
    // create a sorted set of the byte[] values
    SortedSet<byte[]> sortedValues = new TreeSet<byte[]>(new ByteArrayComparator());
   
    sortedValues.addAll(values);
   
    // create the arraylist representation of the trie
    create(sortedValues, 1);
   
    // copy the arraylist representation to the byte[] representation
    tree = new byte[objectTree.size()];
    for (int i = 0; i < objectTree.size(); i++)
      tree[i] = objectTree.get(i);
    // free the reference to the ArrayList representation
    objectTree = null;
  }
 
  private final int indexBytes = 3;
 
  private void create(SortedSet<byte[]> values, int indexDepth) {
    // create a tree node
    // this method assumes that the byte[]s in values all have the same prefix up to indexDepth-1 bytes
   
    // partition the values into two sets: those with the same transition byte as the first record and those with other transition bytes
    SortedSet<byte[]> headValues = null;
    SortedSet<byte[]> tailValues = values;
   
    // calculate the size of this node from the number of unique transition bytes
    int numTransitions = 0;
    Byte lastByte = null;
    boolean accept = false;
    // figure out the number of transitions, and whether this state is accepting
    Iterator<byte[]> valueIter = tailValues.iterator();
    while (valueIter.hasNext()) {
      byte[] v = valueIter.next();
      if (v.length == indexDepth - 1) {
        // if there is an entry in values that requires no transitions from this state, then this is an accepting state
        accept = true;
        valueIter.remove();
        continue;
      }
      if (lastByte == null || lastByte.byteValue() != v[indexDepth - 1]) {
        lastByte = v[indexDepth - 1];
        numTransitions++;
      }
    }
   
    // start the first state
    int entryPosition = objectTree.size();
    // add space for the header
    objectTree.add((byte) numTransitions);
    if (accept)
      objectTree.add((byte) 0x1);
    else
      objectTree.add((byte) 0x0);
    // add space for the transitions
    for (int i = 0; i < numTransitions * (1 + indexBytes); i++) {
      objectTree.add((byte) 0x0);
    }
    int currentTransition = 0;
    while (tailValues != null && tailValues.size() > 0) {
      // find the transition byte for the first partition
      byte[] firstValue = tailValues.first();
      byte transitionByte = firstValue[indexDepth - 1];
      // calculate where to put the information for this transition edge
      int transitionLocation = entryPosition + 2 + ((1 + indexBytes) * currentTransition);
      // keep track of the byte on which we transition so that we can binary search for it later
      objectTree.set(transitionLocation, transitionByte);
      // determine whether there will be another partition, or if all remaining values have the same first transition from this state
      // this check is to avoid overflow when creating a partition point
      if (firstValue[indexDepth - 1] == (byte) 0xff) {
        // all remaining values have the same first transition
        headValues = tailValues;
        tailValues = null;
      } else {
        // there may be values that take another transition, so split the current value set
        // create a partition key that is just after all values with a transition of transitionByte
        byte[] head = new byte[indexDepth];
        for (int i = 0; i < indexDepth - 1; i++) {
          head[i] = firstValue[i];
        }
        head[indexDepth - 1] = (byte) ((firstValue[indexDepth - 1] & 0xff) + 1);
        // partition the remaining values based on the partition point
        headValues = tailValues.headSet(head);
        tailValues = tailValues.tailSet(head);
      }
      // process all the values with a transition of transitionByte
      // check to see if we can use the leaf state to save space
      if (headValues.size() == 1 && headValues.first().length == indexDepth) {
        // remove the value that transitions to the leaf state
        headValues.clear();
        // link to the leaf state
        objectTree.set(transitionLocation + 1, (byte) 0x0);
        objectTree.set(transitionLocation + 2, (byte) 0x0);
        objectTree.set(transitionLocation + 3, (byte) 0x0);
      } else {
        // link to a new state
        objectTree.set(transitionLocation + 1, (byte) (objectTree.size()));
        objectTree.set(transitionLocation + 2, (byte) (objectTree.size() >> 8));
        objectTree.set(transitionLocation + 3, (byte) (objectTree.size() >> 16));
        // create the new state
        create(headValues, indexDepth + 1);
      }
      // process the next transition
      currentTransition++;
    }
  }
 
  private void transitionState(byte val, State s) {
    if (s.failed)
      return;
    int i = 0;
    int j = s.size - 1;
    while (i <= j) {
      int k = (i + j) / 2;
      byte partKey = tree[s.start + ((1 + indexBytes) * k)];
      if (val > partKey) {
        i = k + 1;
      } else if (val < partKey) {
        j = k - 1;
      } else {
        int newStart = (tree[s.start + ((1 + indexBytes) * k) + 1] & 0xff) + ((tree[s.start + ((1 + indexBytes) * k) + 2] & 0xff) << 8)
            + ((tree[s.start + ((1 + indexBytes) * k) + 3] & 0xff) << 16);
        s.accept = (tree[newStart + 1] & 0xff) == 1;
        s.size = tree[newStart] & 0xff;
        s.start = newStart + 2;
        return;
      }
    }
    s.accept = false;
    s.failed = true;
  }
 
  State s = new State();
 
  public boolean check(byte[] value) {
    State s = new State();
    s.accept = tree[3] == (byte) 1;
    s.failed = false;
    s.size = tree[2] & 0xff;
    s.start = 4;
    for (int i = 0; i < value.length; i++) {
      transitionState(value[i], s);
    }
    return s.accept;
  }
 
  public void clearState() {
    s.accept = tree[3] == (byte) 1;
    s.failed = false;
    s.size = tree[2] & 0xff;
    s.start = 4;
  }
 
  public void transition(byte b) {
    transitionState(b, s);
  }
 
  public boolean check() {
    return s.failed == false && s.accept == true;
  }
 
}
TOP

Related Classes of org.apache.accumulo.core.security.ByteArrayTrie$State

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.