BITS BITS EVERYWHERE

This commit is contained in:
cyberpwn
2021-09-23 10:44:56 -04:00
parent 5ed59d0282
commit 1628652264
28 changed files with 899 additions and 159 deletions
@@ -0,0 +1,191 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util.hunk.bits;
import com.volmit.iris.util.data.Varint;
import org.apache.commons.lang3.Validate;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLongArray;
import java.util.function.IntConsumer;
public class DataBits {
private static final int[] MAGIC = new int[]{
-1, -1, 0, Integer.MIN_VALUE, 0, 0, 1431655765, 1431655765, 0, Integer.MIN_VALUE,
0, 1, 858993459, 858993459, 0, 715827882, 715827882, 0, 613566756, 613566756,
0, Integer.MIN_VALUE, 0, 2, 477218588, 477218588, 0, 429496729, 429496729, 0,
390451572, 390451572, 0, 357913941, 357913941, 0, 330382099, 330382099, 0, 306783378,
306783378, 0, 286331153, 286331153, 0, Integer.MIN_VALUE, 0, 3, 252645135, 252645135,
0, 238609294, 238609294, 0, 226050910, 226050910, 0, 214748364, 214748364, 0,
204522252, 204522252, 0, 195225786, 195225786, 0, 186737708, 186737708, 0, 178956970,
178956970, 0, 171798691, 171798691, 0, 165191049, 165191049, 0, 159072862, 159072862,
0, 153391689, 153391689, 0, 148102320, 148102320, 0, 143165576, 143165576, 0,
138547332, 138547332, 0, Integer.MIN_VALUE, 0, 4, 130150524, 130150524, 0, 126322567,
126322567, 0, 122713351, 122713351, 0, 119304647, 119304647, 0, 116080197, 116080197,
0, 113025455, 113025455, 0, 110127366, 110127366, 0, 107374182, 107374182, 0,
104755299, 104755299, 0, 102261126, 102261126, 0, 99882960, 99882960, 0, 97612893,
97612893, 0, 95443717, 95443717, 0, 93368854, 93368854, 0, 91382282, 91382282,
0, 89478485, 89478485, 0, 87652393, 87652393, 0, 85899345, 85899345, 0,
84215045, 84215045, 0, 82595524, 82595524, 0, 81037118, 81037118, 0, 79536431,
79536431, 0, 78090314, 78090314, 0, 76695844, 76695844, 0, 75350303, 75350303,
0, 74051160, 74051160, 0, 72796055, 72796055, 0, 71582788, 71582788, 0,
70409299, 70409299, 0, 69273666, 69273666, 0, 68174084, 68174084, 0, Integer.MIN_VALUE,
0, 5};
private final AtomicLongArray data;
private final int bits;
private final long mask;
private final int size;
private final int valuesPerLong;
private final int divideMul;
private final int divideAdd;
private final int divideShift;
public DataBits(int bits, int length) {
this(bits, length, (AtomicLongArray) null);
}
public DataBits(int bits, int length, DataInputStream din) throws IOException
{
this(bits, length, longs(din, (length + ((char) (64 / bits)) - 1) / ((char) (64 / bits))));
}
public DataBits(int bits, int length, AtomicLongArray data) {
Validate.inclusiveBetween(1L, 32L, bits);
this.size = length;
this.bits = bits;
this.mask = (1L << bits) - 1L;
this.valuesPerLong = (char) (64 / bits);
int var3 = 3 * (valuesPerLong - 1);
this.divideMul = MAGIC[var3];
this.divideAdd = MAGIC[var3 + 1];
this.divideShift = MAGIC[var3 + 2];
int var4 = (length + valuesPerLong - 1) / valuesPerLong;
if (data != null) {
if (data.length() != var4)
{
throw new RuntimeException("NO!");
}
this.data = data;
} else {
this.data = new AtomicLongArray(var4);
}
}
private static AtomicLongArray longs(DataInputStream din, int len) throws IOException{
AtomicLongArray a = new AtomicLongArray(len);
for(int i = 0; i < len; i++)
{
a.set(i, din.readLong());
}
return a;
}
public DataBits setBits(int newBits)
{
if(bits != newBits)
{
DataBits newData = new DataBits(newBits, size);
AtomicInteger c = new AtomicInteger(0);
getAll((i) -> newData.set(c.incrementAndGet(), i));
return newData;
}
return this;
}
private int cellIndex(int var0) {
long var1 = Integer.toUnsignedLong(this.divideMul);
long var3 = Integer.toUnsignedLong(this.divideAdd);
return (int) (var0 * var1 + var3 >> 32L >> this.divideShift);
}
@SuppressWarnings("PointlessBitwiseExpression")
public int getAndSet(int var0, int var1) {
Validate.inclusiveBetween(0L, (this.size - 1), var0);
Validate.inclusiveBetween(0L, this.mask, var1);
int var2 = cellIndex(var0);
long var3 = this.data.get(var2);
int var5 = (var0 - var2 * this.valuesPerLong) * this.bits;
int var6 = (int) (var3 >> var5 & this.mask);
this.data.set(var2, var3 & (this.mask << var5 ^ 0xFFFFFFFFFFFFFFFFL) | (var1 & this.mask) << var5);
return var6;
}
@SuppressWarnings("PointlessBitwiseExpression")
public void set(int var0, int var1) {
Validate.inclusiveBetween(0L, (this.size - 1), var0);
Validate.inclusiveBetween(0L, this.mask, var1);
int var2 = cellIndex(var0);
long var3 = this.data.get(var2);
int var5 = (var0 - var2 * this.valuesPerLong) * this.bits;
this.data.set(var2, var3 & (this.mask << var5 ^ 0xFFFFFFFFFFFFFFFFL) | (var1 & this.mask) << var5);
}
public int get(int var0) {
Validate.inclusiveBetween(0L, (size - 1), var0);
int var1 = cellIndex(var0);
long var2 = this.data.get(var1);
int var4 = (var0 - var1 * valuesPerLong) * this.bits;
return (int) (var2 >> var4 & mask);
}
public AtomicLongArray getRaw() {
return data;
}
public int getSize() {
return size;
}
public int getBits() {
return bits;
}
public void getAll(IntConsumer var0) {
int var1 = 0;
for(int i = 0; i < data.length(); i++)
{
long var5 = data.get(i);
for (int var7 = 0; var7 < valuesPerLong; var7++) {
var0.accept((int) (var5 & mask));
var5 >>= bits;
if (++var1 >= size)
{
return;
}
}
}
}
public void write(DataOutputStream dos) throws IOException {
dos.writeByte(bits);
for(int i = 0; i < data.length(); i++)
{
dos.writeLong(data.get(i));
}
}
}
@@ -0,0 +1,146 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util.hunk.bits;
import com.volmit.iris.util.data.Varint;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
public class DataContainer<T> {
protected static final int INITIAL_BITS = 3;
protected static final int LINEAR_BITS_LIMIT = 5;
protected static final int LINEAR_INITIAL_LENGTH = (int) Math.pow(2, LINEAR_BITS_LIMIT) + 1;
protected static final int[] BIT = computeBitLimits();
private final AtomicReference<Palette<T>> palette;
private final AtomicReference<DataBits> data;
private final AtomicInteger bits;
private final int length;
private final Writable<T> writer;
public DataContainer(Writable<T> writer, int length)
{
this.writer = writer;
this.length = length;
this.palette = new AtomicReference<>(newPalette(INITIAL_BITS));
this.data = new AtomicReference<>(new DataBits(INITIAL_BITS, length));
this.bits = new AtomicInteger(INITIAL_BITS);
}
public byte[] write() throws IOException
{
ByteArrayOutputStream boas = new ByteArrayOutputStream();
write(boas);
return boas.toByteArray();
}
public static <T> DataContainer<T> read(InputStream in, Writable<T> writer) throws IOException {
DataInputStream din = new DataInputStream(in);
return readDin(din, writer);
}
public static <T> DataContainer<T> readDin(DataInputStream in, Writable<T> writer) throws IOException {
DataInputStream din = new DataInputStream(in);
DataContainer<T> container = new DataContainer<>(writer, Varint.readUnsignedVarInt(din));
int paletteSize = Varint.readUnsignedVarInt(din);
container.palette.set(container.newPalette(BIT[paletteSize]).from(paletteSize, writer, din));
container.data.set(new DataBits(container.palette.get().bits(), container.length, din));
return container;
}
public void write(OutputStream out) throws IOException
{
DataOutputStream dos = new DataOutputStream(out);
writeDos(dos);
}
public void writeDos(DataOutputStream out) throws IOException
{
DataOutputStream dos = new DataOutputStream(out);
Varint.writeUnsignedVarInt(length);
Varint.writeUnsignedVarInt(palette.get().size());
palette.get().iterateIO((data, __) -> writer.writeNodeData(dos, data));
data.get().write(dos);
}
private Palette<T> newPalette(int bits)
{
if(bits <= LINEAR_BITS_LIMIT)
{
return new LinearPalette<>(LINEAR_INITIAL_LENGTH);
}
return new HashPalette<>();
}
public void set(int position, T t)
{
int id = palette.get().id(t);
if(id == -1)
{
id = palette.get().add(t);
}
data.get().set(position, id);
}
public T get(int position)
{
int id = data.get().get(position);
if(id <= 0)
{
return null;
}
return palette.get().get(id - 1);
}
public void setBits(int bits)
{
if(this.bits.get() != bits)
{
if(this.bits.get() <= LINEAR_BITS_LIMIT != bits <= LINEAR_BITS_LIMIT)
{
palette.set(newPalette(bits).from(palette.get()));
}
this.bits.set(bits);
data.set(data.get().setBits(bits));
}
}
private static int[] computeBitLimits() {
int[] m = new int[16];
for(int i = 0; i < m.length; i++)
{
m[i] = (int) Math.pow(2, i);
}
return m;
}
}
@@ -0,0 +1,74 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util.hunk.bits;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.function.Consumer2;
import java.util.concurrent.atomic.AtomicInteger;
public class HashPalette<T> implements Palette<T> {
private final KMap<T, Integer> palette;
private final KMap<Integer, T> lookup;
private final AtomicInteger size;
public HashPalette()
{
this.size = new AtomicInteger(0);
this.palette = new KMap<>();
this.lookup = new KMap<>();
}
@Override
public T get(int id) {
if(id < 0 || id >= size.get())
{
return null;
}
return lookup.get(id);
}
@Override
public int add(T t) {
int index = size.getAndIncrement();
palette.put(t, index);
lookup.put(index, t);
return index;
}
@Override
public int id(T t) {
Integer v = palette.get(t);
return v != null ? v : -1;
}
@Override
public int size() {
return size.get();
}
@Override
public void iterate(Consumer2<T, Integer> c) {
for(T i : palette.keySet())
{
c.accept(i, id(i));
}
}
}
@@ -0,0 +1,94 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util.hunk.bits;
import com.volmit.iris.util.function.Consumer2;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceArray;
public class LinearPalette<T> implements Palette<T> {
private final AtomicReference<AtomicReferenceArray<T>> palette;
private final AtomicInteger size;
public LinearPalette(int initialSize)
{
this.size = new AtomicInteger(0);
this.palette = new AtomicReference<>(new AtomicReferenceArray<>(initialSize));
}
@Override
public T get(int id) {
if(id < 0 || id >= size.get())
{
return null;
}
return palette.get().get(id);
}
@Override
public int add(T t) {
int index = size.getAndIncrement();
grow(index + 1);
palette.get().set(index, t);
return index;
}
private void grow(int newLength) {
if(newLength > palette.get().length())
{
AtomicReferenceArray<T> a = new AtomicReferenceArray<>(newLength + size.get());
for(int i = 0; i < palette.get().length(); i++)
{
a.set(i, palette.get().get(i));
}
palette.set(a);
}
}
@Override
public int id(T t) {
for(int i = 0; i < size(); i++)
{
if(t.equals(palette.get().get(i)))
{
return i;
}
}
return -1;
}
@Override
public int size() {
return size.get();
}
@Override
public void iterate(Consumer2<T, Integer> c) {
for(int i = 0; i < size(); i++)
{
c.accept(palette.get().get(i), i);
}
}
}
@@ -0,0 +1,86 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util.hunk.bits;
import com.volmit.iris.util.function.Consumer2;
import com.volmit.iris.util.function.Consumer2IO;
import java.io.DataInputStream;
import java.io.IOException;
public interface Palette<T> {
T get(int id);
int add(T t);
int id(T t);
int size();
default int bits()
{
return bits(DataContainer.INITIAL_BITS);
}
default int bits(int minBits)
{
if(size() <= DataContainer.BIT[minBits])
{
return minBits;
}
for(int i = 0; i < DataContainer.BIT.length; i++)
{
if(DataContainer.BIT[i] >= size())
{
return i;
}
}
return DataContainer.BIT.length - 1;
}
void iterate(Consumer2<T, Integer> c);
default void iterateIO(Consumer2IO<T, Integer> c)
{
iterate((a,b)-> {
try {
c.accept(a,b);
} catch (IOException e) {
e.printStackTrace();
}
});
}
default Palette<T> from(int size, Writable<T> writable, DataInputStream in) throws IOException {
for(int i = 0; i < size; i++)
{
add(writable.readNodeData(in));
}
return this;
}
default Palette<T> from(Palette<T> oldPalette)
{
oldPalette.iterate((k,v) -> add(k));
return this;
}
}
@@ -0,0 +1,29 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util.hunk.bits;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public interface Writable<T> {
T readNodeData(DataInputStream din) throws IOException;
void writeNodeData(DataOutputStream dos, T t) throws IOException;
}
@@ -23,6 +23,8 @@ import com.volmit.iris.util.data.palette.PalettedContainer;
import com.volmit.iris.util.function.Consumer4;
import com.volmit.iris.util.function.Consumer4IO;
import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.hunk.bits.DataContainer;
import com.volmit.iris.util.hunk.bits.Writable;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -33,17 +35,27 @@ import java.util.Map;
@Data
@EqualsAndHashCode(callSuper = false)
public class PaletteHunk<T> extends StorageHunk<T> implements Hunk<T> {
private PalettedContainer<T> data;
private DataContainer<T> data;
public PaletteHunk() {
super(16, 16, 16);
data = new PalettedContainer<>();
public PaletteHunk(int w, int h, int d, Writable<T> writer) {
super(w,h,d);
data = new DataContainer<>(writer, w * h * d);
}
public void setPalette(DataContainer<T> c) {
data = c;
}
public boolean isMapped() {
return false;
}
private int index(int x, int y, int z) {
return (z * getWidth() * getHeight()) + (y * getWidth()) + x;
}
@Override
public synchronized Hunk<T> iterateSync(Consumer4<Integer, Integer, Integer, T> c) {
for(int i = 0; i < getWidth(); i++)
@@ -84,11 +96,11 @@ public class PaletteHunk<T> extends StorageHunk<T> implements Hunk<T> {
@Override
public void setRaw(int x, int y, int z, T t) {
data.set(x,y,z,t);
data.set(index(x, y, z), t);
}
@Override
public T getRaw(int x, int y, int z) {
return data.get(x,y,z);
return data.get(index(x, y, z));
}
}
@@ -22,6 +22,8 @@ import com.volmit.iris.util.data.palette.PalettedContainer;
import com.volmit.iris.util.function.Consumer4;
import com.volmit.iris.util.function.Consumer4IO;
import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.hunk.bits.DataContainer;
import com.volmit.iris.util.hunk.bits.Writable;
import java.io.IOException;
import java.util.Map;
@@ -29,26 +31,26 @@ import java.util.function.Supplier;
public class PaletteOrHunk<T> extends StorageHunk<T> implements Hunk<T> {
private final Hunk<T> hunk;
public PaletteOrHunk(int width, int height, int depth, boolean allow, Supplier<Hunk<T>> factory) {
public PaletteOrHunk(int width, int height, int depth, boolean allow, Writable<T> writable, Supplier<Hunk<T>> factory) {
super(width, height, depth);
hunk = (width == 16 && height == 16 && depth == 16 && allow) ? new PaletteHunk<>() : factory.get();
hunk = (allow) ? new PaletteHunk<>(width, height, depth, writable) : factory.get();
}
public PalettedContainer<T> palette()
public DataContainer<T> palette()
{
return isPalette() ? ((PaletteHunk<T>)hunk).getData() : null;
}
public void palette(PalettedContainer<T> t)
{
if(isPalette()){
((PaletteHunk<T>)hunk).setData(t);
public void setPalette(DataContainer<T> c) {
if(isPalette())
{
((PaletteHunk<T>)hunk).setPalette(c);
}
}
public boolean isPalette()
{
return getWidth() == 16 && getHeight() == 16 && getDepth() == 16;
return hunk instanceof PaletteHunk;
}
@Override
@@ -36,7 +36,7 @@ public class FunctionalHunkView<R, T> implements Hunk<T> {
@Override
public void setRaw(int x, int y, int z, T t) {
if (backConverter == null) {
throw new UnsupportedOperationException("You cannot write to this hunk (Read Only)");
throw new UnsupportedOperationException("You cannot writeNodeData to this hunk (Read Only)");
}
src.setRaw(x, y, z, backConverter.apply(t));