/* * 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.brooklyn.util.math; import java.math.BigInteger; import java.util.ArrayList; import java.util.BitSet; import java.util.List; import com.google.common.collect.Lists; import com.google.common.primitives.Bytes; /** represents an immutable ordered collection of bits with a known length * <p> * when converting to and from bytes and larger numbers, this representation * uses the least-significant first convention both for bits and for bytes (little endian) * <p> * (i.e. least significant byte is first, as is the least significant bit; * ninth element in this list is the least significant bit in the second byte, * so a list {0,0,0,0,0,0,0,0,1,0} represents 256) **/ public class BitList { private final BitSet bits; protected final int length; protected BitList(BitSet bits, int length) { assert length >= bits.length(); this.bits = bits; this.length = length; } public static BitList newInstance(BitSet bits, int length) { return new BitList(bits, length); } public int length() { return length; } public boolean get(int index) { if (index<0 || index>=length) throw new IndexOutOfBoundsException("Index "+index+" in "+this); return bits.get(index); } public static BitList newInstance(byte ...bytes) { BitSet bits = new BitSet(); for (int i=0; i < bytes.length*8; i++) if ((bytes[i/8] & (1 << (i%8))) > 0) bits.set(i); return newInstance(bits, bytes.length*8); } /** as {@link #newInstance(byte...)}, but accepting ints for convenience; * only the least significant 8 bits of the parameters are considered */ public static BitList newInstanceFromBytes(int ...bytes) { BitSet bits = new BitSet(); for (int i=0; i < bytes.length*8; i++) if ((bytes[i/8] & (1 << (i%8))) > 0) bits.set(i); return newInstance(bits, bytes.length*8); } public static BitList newInstance(List<Boolean> l) { BitSet bs = new BitSet(); for (int i=0; i<l.size(); i++) bs.set(i, l.get(i)); return new BitList(bs, l.size()); } public static BitList newInstance(boolean ...l) { BitSet bs = new BitSet(); for (int i=0; i<l.length; i++) bs.set(i, l[i]); return new BitList(bs, l.length); } public static BitList newInstance(BigInteger x) { BitSet bs = new BitSet(); for (int i=0; i<x.bitLength(); i++) if (x.testBit(i)) bs.set(i); return new BitList(bs, x.bitLength()); } /** * returns the bits converted to bytes, with least significant bit first * *and* first 8 bits in the first byte * <p> * NB this may be different to BitSet.valueOf available since java 7 (as late as that!) * which reverses the order of the bytes */ public byte[] asBytes() { byte[] bytes = new byte[(length+7)/8]; for (int i=0; i<bits.length(); i++) if (bits.get(i)) bytes[i/8] |= 1 << (i%8); return bytes; } public int[] asUnsignedBytes() { int[] bytes = new int[(length+7)/8]; for (int i=0; i<bits.length(); i++) if (bits.get(i)) bytes[i/8] |= 1 << (i%8); return bytes; } /** nb: BitSet forgets the length */ public BitSet asBitSet() { return (BitSet) bits.clone(); } public List<Boolean> asList() { List<Boolean> list = new ArrayList<Boolean>(); for (int i=0; i<length(); i++) { list.add(get(i)); } return list; } /** represents the result of this bit list logically ORred with the other */ public BitList orred(BitList other) { BitSet result = asBitSet(); result.or(other.asBitSet()); return new BitList(result, Math.max(length, other.length)); } /** represents the result of this bit list logically ANDed with the other */ public BitList anded(BitList other) { BitSet result = asBitSet(); result.and(other.asBitSet()); return new BitList(result, Math.max(length, other.length)); } /** represents the result of this bit list logically XORred with the other */ public BitList xorred(BitList other) { BitSet result = asBitSet(); result.xor(other.asBitSet()); return new BitList(result, Math.max(length, other.length)); } /** represents the result of this bit list logically notted */ public BitList notted() { BitSet result = asBitSet(); result.flip(0, length); return new BitList(result, length); } /** creates a new instance with the given length, either reducing the list or padding it with 0's * (at the end, in both cases) */ public BitList resized(int length) { BitSet b2 = asBitSet(); if (b2.length()>length) b2.clear(length, b2.length()); return newInstance(b2, length); } public BitList reversed() { BitSet b = new BitSet(); for (int from=bits.length()-1, to=length-bits.length(); from>=0; from--, to++) { if (get(from)) b.set(to); } return new BitList(b, length); } public int commonPrefixLength(BitList other) { int i=0; while (i<length && i<other.length) { if (get(i)!=other.get(i)) return i; i++; } return i; } /** true iff the length is 0; see also isZero */ public boolean isEmpty() { return length==0; } /** true iff all bits are 0 */ public boolean isZero() { return bits.cardinality()==0; } public BigInteger asBigInteger() { if (length==0) return BigInteger.ZERO; return new BigInteger(Bytes.toArray(Lists.reverse(asByteList()))); } public boolean[] asArray() { boolean[] result = new boolean[length]; for (int i=0; i<length; i++) result[i] = get(i); return result; } public List<Byte> asByteList() { return Bytes.asList(asBytes()); } /** returns value of this as a byte(ignoring any too-high bits) */ public byte byteValue() { return asBigInteger().byteValue(); } /** returns value of this as an integer (ignoring any too-high bits) */ public int intValue() { return asBigInteger().intValue(); } /** returns value of this as a long (ignoring any too-high bits) */ public long longValue() { return asBigInteger().longValue(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((bits == null) ? 0 : bits.hashCode()); result = prime * result + length; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; BitList other = (BitList) obj; if (bits == null) { if (other.bits != null) return false; } else if (!bits.equals(other.bits)) return false; if (length != other.length) return false; return true; } @Override public String toString() { StringBuilder sb = new StringBuilder(); for (int i=0; i<length; i++) { if (i%8==0 && i>0) sb.append(":"); //for readability sb.append(get(i) ? '1' : '0'); } return sb.toString(); } }