/*
* Copyright 2013 MovingBlocks
*
* Licensed 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.terasology.world.chunks.blockdata;
import com.google.common.base.Preconditions;
import java.nio.ByteBuffer;
/**
* TeraSparseArrayByte is the base class used to implement sparse arrays with elements of size 4 bit or 8 bit.
*
*/
public abstract class TeraSparseArrayByte extends TeraSparseArray {
protected byte[][] inflated;
protected byte[] deflated;
protected byte fill;
protected TeraSparseArrayByte() {
super();
}
protected TeraSparseArrayByte(int sizeX, int sizeY, int sizeZ) {
super(sizeX, sizeY, sizeZ, false);
}
protected TeraSparseArrayByte(int sizeX, int sizeY, int sizeZ, byte[][] inflated, byte[] deflated) {
super(sizeX, sizeY, sizeZ, false);
this.inflated = Preconditions.checkNotNull(inflated);
this.deflated = Preconditions.checkNotNull(deflated);
Preconditions.checkArgument(inflated.length == sizeY, "The length of parameter 'inflated' has to be " + sizeY + " but is " + inflated.length);
Preconditions.checkArgument(deflated.length == sizeY, "The length of parameter 'deflated' has to be " + sizeY + " but is " + deflated.length);
}
protected TeraSparseArrayByte(int sizeX, int sizeY, int sizeZ, byte fill) {
super(sizeX, sizeY, sizeZ, false);
this.fill = fill;
}
protected abstract TeraArray createSparse(byte defaultFill);
protected abstract TeraArray createSparse(byte[][] inflatedData, byte[] deflatedData);
protected abstract int rowSize();
@Override
protected void initialize() {
}
@Override
public final int getEstimatedMemoryConsumptionInBytes() {
if (inflated == null) {
return 9;
}
int result = 9 + getSizeY() + (getSizeY() * 4);
for (int i = 0; i < getSizeY(); i++) {
if (inflated[i] != null) {
result += 12 + rowSize();
}
}
return result;
}
@Override
public final TeraArray copy() {
if (inflated == null) {
return createSparse(fill);
}
byte[][] inf = new byte[getSizeY()][];
byte[] def = new byte[getSizeY()];
for (int y = 0; y < getSizeY(); y++) {
if (inflated[y] != null) {
inf[y] = new byte[rowSize()];
System.arraycopy(inflated[y], 0, inf[y], 0, rowSize());
}
}
System.arraycopy(deflated, 0, def, 0, getSizeY());
return createSparse(inf, def);
}
protected abstract static class SerializationHandler<T extends TeraSparseArrayByte> extends TeraArray.BasicSerializationHandler<T> {
protected abstract T createArray(int sizeX, int sizeY, int sizeZ);
@Override
protected int internalComputeMinimumBufferSize(T array) {
final byte[][] inf = array.inflated;
if (inf == null) {
return 2;
} else {
int sizeY = array.getSizeY();
int rowSize = array.rowSize();
int result = 1;
for (int y = 0; y < sizeY; y++) {
if (inf[y] == null) {
result += 2;
} else {
result += 1 + rowSize;
}
}
return result;
}
}
@Override
protected void internalSerialize(T array, ByteBuffer buffer) {
final byte[][] inf = array.inflated;
if (inf == null) {
buffer.put((byte) 0);
buffer.put(array.fill);
} else {
buffer.put((byte) 1);
int sizeY = array.getSizeY();
int rowSize = array.rowSize();
final byte[] def = array.deflated;
for (int y = 0; y < sizeY; y++) {
final byte[] row = inf[y];
if (row == null) {
buffer.put((byte) 0);
buffer.put(def[y]);
} else {
buffer.put((byte) 1);
buffer.put(row, 0, rowSize);
}
}
}
}
@Override
protected T internalDeserialize(int sizeX, int sizeY, int sizeZ, ByteBuffer buffer) {
final byte hasData = buffer.get();
final T array = createArray(sizeX, sizeY, sizeZ);
if (hasData == 0) {
array.fill = buffer.get();
return array;
}
int rowSize = array.rowSize();
array.inflated = new byte[sizeY][];
array.deflated = new byte[sizeY];
for (int y = 0; y < sizeY; y++) {
final byte hasRow = buffer.get();
if (hasRow == 0) {
array.deflated[y] = buffer.get();
} else {
array.inflated[y] = new byte[rowSize];
buffer.get(array.inflated[y], 0, rowSize);
}
}
return array;
}
}
}