// Copyright 2013 Thomas Müller
// This file is part of MarMoT, which is licensed under GPLv3.
package marmot.util;
import java.io.Serializable;
import java.util.Arrays;
import marmot.core.Feature;
public class Encoder implements Serializable {
private static final long serialVersionUID = 1L;
private int[] bytes_;
private short current_array_length_;
private short current_bit_index_;
private State state_;
public static class State implements Serializable {
private static final long serialVersionUID = 1L;
private short array_length_;
private short bit_index_;
private int byte_;
}
public Encoder(int capacity) {
bytes_ = new int[capacity];
state_ = new State();
reset();
}
public static int bitsNeeded(int max_value) {
int num_bits = 1;
while (true) {
max_value /= 2;
if (max_value == 0) {
break;
}
num_bits += 1;
}
return num_bits;
}
public void append(boolean value) {
append(value ? 0 : 1, 1);
}
public void append(int value, int bits_needed) {
assert value >= 0;
assert bitsNeeded(value) <= bits_needed : value;
while (bits_needed != 0) {
if (current_bit_index_ == Integer.SIZE) {
current_array_length_ ++;
current_bit_index_ = 0;
}
int bits_left = Integer.SIZE - current_bit_index_;
int bits = Math.min(bits_left, bits_needed);
int mask = (2 << (bits - 1)) - 1;
int b = value & mask;
value >>= bits;
bits_needed -= bits;
bytes_[current_array_length_ - 1] += b << current_bit_index_;
current_bit_index_ += bits;
}
assert value == 0;
}
public Feature getFeature() {
Feature feature = new Feature(bytes_.length);
copyToFeature(feature);
return feature;
}
public void reset() {
current_array_length_ = 0;
current_bit_index_ = Integer.SIZE;
storeState();
Arrays.fill(bytes_, 0);
}
public Feature getFeature(boolean flag) {
return getFeature();
}
public void storeState() {
storeState(state_);
}
public void storeState(State state) {
state.array_length_ = current_array_length_;
state.bit_index_ = current_bit_index_;
if (current_array_length_ > 0)
state.byte_ = bytes_[current_array_length_ - 1];
else
state.byte_ = 0;
}
public void restoreState() {
restoreState(state_);
}
public void restoreState(State state) {
Arrays.fill(bytes_, state.array_length_ , current_array_length_, 0);
current_array_length_ = state.array_length_;
current_bit_index_ = state.bit_index_;
if (current_array_length_ > 0)
bytes_[current_array_length_ - 1] = state.byte_;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(bytes_);
result = prime * result + current_array_length_;
result = prime * result + current_bit_index_;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Encoder other = (Encoder) obj;
if (!Arrays.equals(bytes_, other.bytes_))
return false;
if (current_array_length_ != other.current_array_length_)
return false;
if (current_bit_index_ != other.current_bit_index_)
return false;
return true;
}
@Override
public String toString() {
return "Encoder [bytes_=" + Arrays.toString(bytes_)
+ ", current_array_length_=" + current_array_length_
+ ", current_bit_index_=" + current_bit_index_
+ ", stored_array_length_=" + state_.array_length_
+ ", stored_bit_index_=" + state_.bit_index_
+ ", stored_byte_=" + state_.byte_ + "]";
}
public int getCapacity() {
return bytes_.length;
}
public void copyToFeature(Feature feature) {
System.arraycopy(bytes_, 0, feature.getBytes(), 0, bytes_.length);
feature.setArrayLength(current_array_length_);
feature.setBitIndex(current_bit_index_);
}
public int getCurrentLength() {
return current_array_length_;
}
}