mirror of
https://github.com/MarginaliaSearch/MarginaliaSearch.git
synced 2025-02-25 05:38:59 +00:00
92 lines
2.6 KiB
Java
92 lines
2.6 KiB
Java
package nu.marginalia.sequence;
|
|
|
|
import it.unimi.dsi.fastutil.ints.IntIterator;
|
|
import it.unimi.dsi.fastutil.ints.IntList;
|
|
import nu.marginalia.sequence.io.BitReader;
|
|
import nu.marginalia.sequence.io.BitWriter;
|
|
|
|
import java.nio.ByteBuffer;
|
|
|
|
/** Implement coding and decoding of sequences of integers using the Elias Gamma code
|
|
*
|
|
* https://en.wikipedia.org/wiki/Elias_gamma_coding
|
|
* */
|
|
public class EliasGammaCodec implements IntIterator {
|
|
|
|
private final BitReader reader;
|
|
private int last = 0;
|
|
private int next = 0;
|
|
|
|
private EliasGammaCodec(ByteBuffer buffer) {
|
|
reader = new BitReader(buffer);
|
|
}
|
|
|
|
/** Decode a sequence of integers from a ByteBuffer using the Elias Gamma code */
|
|
public static IntIterator decode(ByteBuffer buffer) {
|
|
return new EliasGammaCodec(buffer);
|
|
}
|
|
|
|
/** Encode a sequence of integers into a ByteBuffer using the Elias Gamma code.
|
|
* The sequence must be strictly increasing and may not contain values less than
|
|
* or equal to zero.
|
|
*/
|
|
public static ByteBuffer encode(ByteBuffer workArea, IntList sequence) {
|
|
var writer = new BitWriter(workArea);
|
|
int last = 0;
|
|
|
|
for (var iter = sequence.iterator(); iter.hasNext(); ) {
|
|
int i = iter.nextInt();
|
|
int delta = i - last;
|
|
last = i;
|
|
|
|
// can't encode zeroes
|
|
assert delta > 0 : "Sequence must be strictly increasing and may not contain zeroes or negative values";
|
|
|
|
int bits = Integer.numberOfTrailingZeros(Integer.highestOneBit(delta));
|
|
writer.put(0, bits + 1);
|
|
writer.put(delta, bits + 1);
|
|
}
|
|
|
|
return writer.finish();
|
|
}
|
|
|
|
/** Encode a sequence of integers into a ByteBuffer using the Elias Gamma code.
|
|
* The sequence must be strictly increasing and may not contain values less than
|
|
* or equal to zero.
|
|
*/
|
|
public static ByteBuffer encode(ByteBuffer workArea, int[] sequence) {
|
|
return encode(workArea, IntList.of(sequence));
|
|
}
|
|
|
|
@Override
|
|
public boolean hasNext() {
|
|
if (next > 0)
|
|
return true;
|
|
if (!reader.hasMore())
|
|
return false;
|
|
|
|
int bits = reader.takeWhileZero();
|
|
|
|
if (!reader.hasMore()) {
|
|
return false;
|
|
}
|
|
int delta = reader.get(bits);
|
|
last += delta;
|
|
next = last;
|
|
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public int nextInt() {
|
|
if (hasNext()) {
|
|
int ret = next;
|
|
next = -1;
|
|
return ret;
|
|
}
|
|
throw new ArrayIndexOutOfBoundsException("No more data to read");
|
|
}
|
|
|
|
|
|
}
|