1 /**
2 Copyright: Copyright (c) 2013-2018 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 module voxelman.world.storage.chunk.layer;
7 
8 import voxelman.log;
9 import std.array : uninitializedArray;
10 import std..string : format;
11 import std.typecons : Nullable;
12 
13 import cbor;
14 import voxelman.math;
15 
16 import voxelman.core.config;
17 import voxelman.world.block;
18 import voxelman.algorithm.arraycopy3d;
19 import voxelman.world.storage.coordinates;
20 import voxelman.world.storage.utils;
21 import voxelman.platform.compression;
22 
23 enum BLOCK_LAYER = 0;
24 enum ENTITY_LAYER = 1;
25 enum METADATA_LAYER = 2;
26 enum NUM_CHUNK_LAYERS = 3;
27 
28 enum BLOCKS_DATA_LENGTH = CHUNK_SIZE_CUBE * BlockId.sizeof;
29 enum BLOCKID_UNIFORM_FILL_BITS = bitsToUniformLength!BlockId;
30 enum BLOCK_METADATA_UNIFORM_FILL_BITS = bitsToUniformLength!BlockMetadata;
31 
32 ubyte bitsToUniformLength(T)()
33 {
34 	return bitsToUniformLength(T.sizeof * 8);
35 }
36 
37 ubyte bitsToUniformLength(ubyte bits) {
38 	if (bits == 1)
39 		return 1;
40 	else if (bits == 2)
41 		return 2;
42 	else if (bits == 4)
43 		return 3;
44 	else if (bits == 8)
45 		return 4;
46 	else if (bits == 16)
47 		return 5;
48 	else if (bits == 32)
49 		return 6;
50 	else if (bits == 64)
51 		return 7;
52 	else
53 		assert(false);
54 }
55 
56 /// is used as array size when non-uniform.
57 /// Used to allocate full array when uniform.
58 /// Assumes array length as CHUNK_SIZE_CUBE, but element size is dataLength/CHUNK_SIZE_CUBE if CHUNK_SIZE_CUBE > dataLength
59 /// Or CHUNK_SIZE_CUBE/dataLength otherwise. Element size is used to fill created array.
60 /// If dataLength < CHUNK_SIZE_CUBE then element size is less than byte.
61 /// Element size must be power of two, either of bytes or of bits.
62 alias LayerDataLenType = uint;
63 
64 enum StorageType : ubyte
65 {
66 	uniform,
67 	//linearMap,
68 	//hashMap,
69 	compressedArray,
70 	fullArray,
71 }
72 
73 import std.experimental.allocator;
74 import std.experimental.allocator.mallocator;
75 
76 ubyte[] allocLayerArray(ubyte[] dataToCopy) {
77 	auto res = makeArray!ubyte(Mallocator.instance, dataToCopy);
78 	//tracef("allocLayerArray1 %s", res.ptr);
79 	return res;
80 }
81 
82 ubyte[] reallocLayerArray(Layer)(ref Layer layer, size_t newLength) {
83 	assert(layer.type != StorageType.uniform);
84 	void[] data = layer.getArray!ubyte;
85 	Mallocator.instance.reallocate(data, newLength);
86 	layer.dataPtr = data.ptr;
87 	layer.dataLength = cast(LayerDataLenType)data.length;
88 	//tracef("reallocLayerArray %s", data.ptr);
89 	return cast(ubyte[])data;
90 }
91 
92 ubyte[] allocLayerArray(size_t length) {
93 	auto res = makeArray!ubyte(Mallocator.instance, length);
94 	//tracef("allocLayerArray2 %s", res.ptr);
95 	return res;
96 }
97 
98 void freeLayerArray(Layer)(ref Layer layer) {
99 	//tracef("freeLayerArray %s %s", layer.dataPtr, layer);
100 	if(layer.type != StorageType.uniform)
101 	{
102 		ubyte[] data = layer.getArray!ubyte;
103 		dispose(Mallocator.instance, data);
104 		layer.dataPtr = null;
105 		layer.dataLength = 0;
106 	}
107 }
108 
109 struct ChunkHeaderItem {
110 	ChunkWorldPos cwp;
111 	uint numLayers;
112 	uint metadata; // for task purposes
113 }
114 static assert(ChunkHeaderItem.sizeof == 16);
115 
116 struct ChunkLayerTimestampItem {
117 	uint timestamp;
118 	ubyte layerId;
119 }
120 static assert(ChunkLayerTimestampItem.sizeof == 8);
121 
122 /// Stores layer of chunk data. Used for transferring and saving chunk layers.
123 /// Stores layerId. Stores no user count.
124 align(1)
125 struct ChunkLayerItem
126 {
127 	StorageType type;
128 	ubyte layerId;
129 	ushort metadata;
130 	uint timestamp;
131 	union {
132 		ulong uniformData;
133 		void* dataPtr; /// Stores ptr to the first byte of data. The length of data is in dataLength.
134 	}
135 	LayerDataLenType dataLength;
136 
137 	this(ubyte _layerId, LayerDataLenType _dataLength, uint _timestamp, ulong _uniformData, ushort _metadata = 0) {
138 		type = StorageType.uniform; layerId = _layerId; dataLength = _dataLength; timestamp = _timestamp; uniformData = _uniformData; metadata = _metadata;
139 	}
140 	this(StorageType _type, ubyte _layerId, LayerDataLenType _dataLength, uint _timestamp, ubyte* _dataPtr, ushort _metadata = 0) {
141 		type = _type; layerId = _layerId; dataLength = _dataLength; timestamp = _timestamp; dataPtr = _dataPtr; metadata = _metadata;
142 	}
143 	this(T)(StorageType _type, ubyte _layerId, uint _timestamp, T[] _array, ushort _metadata = 0) {
144 		ubyte[] data = cast(ubyte[])_array;
145 		type = _type; layerId = _layerId; dataLength = cast(LayerDataLenType)data.length; timestamp = _timestamp; dataPtr = cast(void*)data.ptr; metadata = _metadata;
146 	}
147 	this(ChunkLayerSnap l, ubyte _layerId) {
148 		type = l.type;
149 		layerId = _layerId;
150 		dataLength = l.dataLength;
151 		timestamp = l.timestamp;
152 		uniformData = l.uniformData;
153 		metadata = l.metadata;
154 	}
155 
156 	void toString()(scope void delegate(const(char)[]) sink) const
157 	{
158 		import std.format : formattedWrite;
159 		sink.formattedWrite("ChunkLayerItem(%s, %s, %s, %s, {%s, %s}, %s)",
160 			type, layerId, dataLength, timestamp, uniformData, dataPtr, metadata);
161 	}
162 }
163 static assert(ChunkLayerItem.sizeof == 20);
164 
165 struct WriteBuffer
166 {
167 	ChunkLayerItem layer;
168 	// If false, no changes will be performed on commit.
169 	bool isModified = true;
170 	// If true, after commit current snapshot is null.
171 	bool removeSnapshot = false;
172 
173 	void makeUniform(ulong uniformData, LayerDataLenType dataLength, ushort metadata = 0) {
174 		freeLayerArray(layer);
175 		layer.uniformData = uniformData;
176 		layer.dataLength = dataLength;
177 		layer.metadata = metadata;
178 	}
179 
180 	void makeUniform(T)(ulong uniformData, ushort metadata = 0) {
181 		freeLayerArray(layer);
182 		layer.uniformData = uniformData;
183 		layer.dataLength = bitsToUniformLength!T;
184 		layer.metadata = metadata;
185 	}
186 
187 	bool isUniform() {
188 		return layer.type == StorageType.uniform;
189 	}
190 
191 	T getUniform(T)() {
192 		return layer.getUniform!T;
193 	}
194 
195 	T[] getArray(T)() {
196 		return layer.getArray!T;
197 	}
198 }
199 
200 void expandUniformLayer(Layer)(ref Layer layer)
201 	if (isSomeLayer!Layer)
202 {
203 	assert(layer.type == StorageType.uniform);
204 
205 	// zero is used to denote that uniform cannot be expanded and should produce empty array.
206 	assert(layer.dataLength >= 0 && layer.dataLength <= 7,
207 		format("dataLength == %s", layer.dataLength));
208 	if (layer.dataLength > 0)
209 	{
210 		size_t itemBitsPowerOfTwo = layer.dataLength - 1; // [1; 7] => [0; 6]
211 		size_t itemBits = 1 << itemBitsPowerOfTwo; // [1..64]
212 		size_t arraySize = (CHUNK_SIZE_CUBE * itemBits) / 8;
213 
214 		ubyte[] buffer = ensureLayerArrayLength(layer, arraySize);
215 		expandUniform(buffer, cast(ubyte)itemBits, layer.uniformData);
216 		layer.dataPtr = buffer.ptr;
217 		layer.dataLength = cast(LayerDataLenType)buffer.length;
218 	}
219 	else
220 	{
221 		// empty array
222 	}
223 	layer.type = StorageType.fullArray;
224 }
225 
226 // copies layer array without umcompressing
227 void copyLayer(Layer1, Layer2)(const Layer1 source, ref Layer2 dest)
228 	if (isSomeLayer!Layer1 && isSomeLayer!Layer2)
229 {
230 	assert(dest.isUniform);
231 	dest = source;
232 	if (!dest.isUniform)
233 	{
234 		auto data = dest.getArray!ubyte;
235 		auto copy = allocLayerArray(data.length);
236 		copy[] = data;
237 		dest.dataPtr = copy.ptr;
238 		dest.dataLength = cast(LayerDataLenType)copy.length;
239 	}
240 }
241 
242 // copies layer's data into write buffer filling fullArray
243 void applyLayer(Layer1, Layer2)(const Layer1 layer, ref Layer2 writeBuffer)
244 	if (isSomeLayer!Layer1 && isSomeLayer!Layer2)
245 {
246 	ubyte[] buffer;
247 	if (layer.type == StorageType.uniform)
248 	{
249 		// zero is used to denote that uniform cannot be expanded and should produce empty array.
250 		assert(layer.dataLength >= 0 && layer.dataLength <= 7,
251 			format("dataLength == %s", layer.dataLength));
252 		if (layer.dataLength > 0)
253 		{
254 			size_t itemBitsPowerOfTwo = layer.dataLength - 1; // [1; 7] => [0; 6]
255 			size_t itemBits = 1 << itemBitsPowerOfTwo; // [1..64]
256 			size_t arraySize = (CHUNK_SIZE_CUBE * itemBits) / 8;
257 
258 			buffer = ensureLayerArrayLength(writeBuffer, arraySize);
259 			expandUniform(buffer, cast(ubyte)itemBits, layer.uniformData);
260 		}
261 		else
262 		{
263 			// empty array
264 		}
265 	}
266 	else if (layer.type == StorageType.fullArray)
267 	{
268 		buffer = ensureLayerArrayLength(writeBuffer, layer.dataLength);
269 		buffer[] = layer.getArray!ubyte;
270 	}
271 	else if (layer.type == StorageType.compressedArray)
272 	{
273 		ubyte[] compressedData = layer.getArray!ubyte;
274 		size_t uncompressedLength = uncompressedDataLength(compressedData);
275 		buffer = ensureLayerArrayLength(writeBuffer, uncompressedLength);
276 		ubyte[] decompressedData = decompress(compressedData, buffer);
277 		assert(decompressedData.length == buffer.length);
278 	}
279 	writeBuffer.type = StorageType.fullArray;
280 	writeBuffer.metadata = layer.metadata;
281 	writeBuffer.dataPtr = buffer.ptr;
282 	writeBuffer.dataLength = cast(LayerDataLenType)buffer.length;
283 }
284 
285 ubyte[] ensureLayerArrayLength(Layer)(ref Layer layer, size_t length)
286 	if (isSomeLayer!Layer)
287 {
288 	ubyte[] buffer;
289 	if (layer.isUniform)
290 	{
291 		buffer = allocLayerArray(length);
292 	}
293 	else
294 	{
295 		if (layer.dataLength == length)
296 		{
297 			buffer = layer.getArray!ubyte;
298 		}
299 		else
300 		{
301 			//buffer = reallocLayerArray(layer, length);
302 			freeLayerArray(layer); // TODO realloc
303 			buffer = allocLayerArray(length);
304 		}
305 	}
306 	return buffer;
307 }
308 
309 // tables of repeated bit patterns for 1, 2 and 4 bit items.
310 private static immutable ubyte[2] bitsTable1 = [0b0000_0000, 0b1111_1111];
311 private static immutable ubyte[4] bitsTable2 = [0b00_00_00_00, 0b01_01_01_01, 0b10_10_10_10, 0b11_11_11_11];
312 private static immutable ubyte[16] bitsTable4 = [
313 0b0000_0000, 0b0001_0001, 0b0010_0010, 0b0011_0011, 0b0100_0100, 0b0101_0101, 0b0110_0110, 0b0111_0111,
314 0b1000_1000, 0b1001_1001, 0b1010_1010, 0b1011_1011, 0b1100_1100, 0b1101_1101, 0b1110_1110, 0b1111_1111];
315 
316 void expandUniform(ubyte[] buffer, ubyte itemBits, ulong uniformData)
317 {
318 	switch(itemBits) {
319 		case 1:
320 			ubyte byteFiller = bitsTable1[uniformData & 0b0001];
321 			buffer[] = byteFiller;
322 			break;
323 		case 2:
324 			ubyte byteFiller = bitsTable2[uniformData & 0b0011];
325 			buffer[] = byteFiller;
326 			break;
327 		case 4:
328 			ubyte byteFiller = bitsTable4[uniformData & 0b1111];
329 			buffer[] = byteFiller;
330 			break;
331 		case 8:
332 			ubyte byteFiller = uniformData & ubyte.max;
333 			buffer[] = byteFiller;
334 			break;
335 		case 16:
336 			ushort ushortFiller = uniformData & ushort.max;
337 			(cast(ushort[])buffer)[] = ushortFiller;
338 			break;
339 		case 32:
340 			uint uintFiller = uniformData & uint.max;
341 			(cast(uint[])buffer)[] = uintFiller;
342 			break;
343 		case 64:
344 			ulong ulongFiller = uniformData & ulong.max;
345 			(cast(ulong[])buffer)[] = ulongFiller;
346 			break;
347 		default:
348 			assert(false, "Invalid itemBits");
349 	}
350 }
351 
352 /// Container for chunk updates
353 /// If blockChanges is null uses newBlockData
354 struct ChunkChange
355 {
356 	uvec3 a, b; // box
357 	BlockId blockId;
358 	BlockMetadata blockMeta;
359 }
360 
361 // container of single block change.
362 // position is chunk local [0; CHUNK_SIZE-1];
363 struct BlockChange
364 {
365 	ushort index;
366 	BlockId blockId;
367 	BlockMetadata blockMeta;
368 }
369 
370 ushort[2] areaOfImpact(BlockChange[] changes)
371 {
372 	ushort start;
373 	ushort end;
374 
375 	foreach(change; changes)
376 	{
377 		if (change.index < start)
378 			start = change.index;
379 		if (change.index > end)
380 			end = change.index;
381 	}
382 
383 	return cast(ushort[2])[start, end+1];
384 }
385 
386 // stores all used snapshots of the chunk.
387 struct BlockDataSnapshot
388 {
389 	ChunkLayerData blockData;
390 	TimestampType timestamp;
391 	uint numUsers;
392 }
393 
394 /// Stores layer of chunk data. Blocks are stored as array of blocks or uniform.
395 struct ChunkLayerSnap
396 {
397 	union {
398 		ulong uniformData;
399 		void* dataPtr; /// Stores ptr to the first byte of data. The length of data is in dataLength.
400 	}
401 	LayerDataLenType dataLength; // unused when uniform
402 	uint timestamp;
403 	ushort numUsers;
404 	ushort metadata;
405 	StorageType type;
406 	this(LayerDataLenType _dataLength, uint _timestamp, ulong _uniformData, ushort _metadata = 0) {
407 		type = StorageType.uniform; dataLength = _dataLength; timestamp = _timestamp; uniformData = _uniformData; metadata = _metadata;
408 	}
409 	this(StorageType _type, LayerDataLenType _dataLength, uint _timestamp, void* _dataPtr, ushort _metadata = 0) {
410 		type = _type; dataLength = _dataLength; timestamp = _timestamp; dataPtr = _dataPtr; metadata = _metadata;
411 	}
412 	this(T)(StorageType _type, uint _timestamp, T[] _array, ushort _metadata = 0) {
413 		ubyte[] data = cast(ubyte[])_array;
414 		type = _type; dataLength = cast(LayerDataLenType)data.length; timestamp = _timestamp; dataPtr = data.ptr; metadata = _metadata;
415 	}
416 	this(ChunkLayerItem l) {
417 		numUsers = 0;
418 		timestamp = l.timestamp;
419 		type = l.type;
420 		dataLength = l.dataLength;
421 		uniformData = l.uniformData;
422 		metadata = l.metadata;
423 	}
424 }
425 
426 enum isSomeLayer(Layer) = is(Layer == ChunkLayerSnap) || is(Layer == ChunkLayerItem) || is(Layer == Nullable!ChunkLayerSnap);
427 
428 T[] getArray(T, Layer)(const ref Layer layer)
429 	if (isSomeLayer!Layer)
430 {
431 	assert(layer.type != StorageType.uniform);
432 	return cast(T[])(layer.dataPtr[0..layer.dataLength]);
433 }
434 T getUniform(T, Layer)(const ref Layer layer)
435 	if (isSomeLayer!Layer)
436 {
437 	assert(layer.type == StorageType.uniform);
438 	return cast(T)layer.uniformData;
439 }
440 
441 BlockId getBlockId(Layer)(const ref Layer layer, BlockChunkIndex index)
442 	if (isSomeLayer!Layer)
443 {
444 	if (layer.type == StorageType.fullArray) return (cast(BlockId*)layer.dataPtr)[index];
445 	if (layer.type == StorageType.uniform) return cast(BlockId)layer.uniformData;
446 
447 	BlockId[CHUNK_SIZE_CUBE] buffer;
448 	decompressLayerData(layer, cast(ubyte[])buffer[]);
449 	return buffer[index];
450 }
451 
452 BlockId getBlockId(Layer)(const ref Layer layer, int x, int y, int z)
453 	if (isSomeLayer!Layer)
454 {
455 	return getBlockId(layer, BlockChunkIndex(x, y, z));
456 }
457 
458 T getLayerItemNoncompressed(T, Layer)(const ref Layer layer, BlockChunkIndex index)
459 	if (isSomeLayer!Layer)
460 {
461 	if (layer.type == StorageType.fullArray) return (cast(T*)layer.dataPtr)[index];
462 	if (layer.type == StorageType.uniform) return cast(T)layer.uniformData;
463 	assert(false);
464 }
465 
466 bool isUniform(Layer)(const ref Layer layer) @property
467 	if (isSomeLayer!Layer)
468 {
469 	return layer.type == StorageType.uniform;
470 }
471 
472 ChunkLayerData toBlockData(Layer)(const ref Layer layer, ubyte layerId)
473 	if (isSomeLayer!Layer)
474 {
475 	ChunkLayerData res;
476 	res.uniform = layer.type == StorageType.uniform;
477 	res.metadata = layer.metadata;
478 	res.layerId = layerId;
479 	if (!res.uniform) {
480 		res.blocks = layer.getArray!ubyte();
481 	} else {
482 		res.uniformType = layer.uniformData;
483 		res.dataLength = layer.dataLength;
484 	}
485 	return res;
486 }
487 
488 ChunkLayerItem fromBlockData(const ref ChunkLayerData bd)
489 {
490 	if (bd.uniform)
491 		return ChunkLayerItem(bd.layerId, bd.dataLength, 0, bd.uniformType, bd.metadata);
492 	else
493 		return ChunkLayerItem(StorageType.compressedArray, bd.layerId, 0, bd.blocks, bd.metadata);
494 }
495 
496 void copyToBuffer(Layer)(Layer layer, BlockId[] outBuffer)
497 	if (isSomeLayer!Layer)
498 {
499 	assert(outBuffer.length == CHUNK_SIZE_CUBE);
500 	if (layer.type == StorageType.uniform)
501 		outBuffer[] = cast(BlockId)layer.uniformData;
502 	else if (layer.type == StorageType.fullArray)
503 		outBuffer[] = layer.getArray!BlockId;
504 	else if (layer.type == StorageType.compressedArray)
505 		decompressLayerData(layer, outBuffer);
506 }
507 
508 size_t getLayerDataBytes(Layer)(const ref Layer layer)
509 	if (isSomeLayer!Layer)
510 {
511 	if (layer.type == StorageType.uniform)
512 		return 0;
513 	else
514 		return layer.dataLength;
515 }
516 
517 void applyChanges(WriteBuffer* writeBuffer, BlockChange[] changes)
518 {
519 	assert(!writeBuffer.isUniform);
520 	assert(writeBuffer.layer.dataLength == BLOCKS_DATA_LENGTH);
521 	BlockId[] blocks = writeBuffer.layer.getArray!BlockId;
522 	foreach(change; changes)
523 	{
524 		blocks[change.index] = change.blockId;
525 	}
526 }
527 
528 void applyChanges(WriteBuffer* writeBuffer, ChunkChange[] changes)
529 {
530 	assert(!writeBuffer.isUniform);
531 	assert(writeBuffer.layer.dataLength == BLOCKS_DATA_LENGTH);
532 	BlockId[] blocks = writeBuffer.layer.getArray!BlockId;
533 	foreach(change; changes)
534 	{
535 		setSubArray3d(blocks, CHUNK_SIZE_VECTOR, Box(ivec3(change.a), ivec3(change.b)), change.blockId);
536 	}
537 }
538 
539 ubyte[] compressLayerData(ubyte[] data, ubyte[] buffer)
540 {
541 	size_t size = encodeCbor(buffer[], data.length);
542 	size += compress(data, buffer[size..$]).length;
543 	return buffer[0..size];
544 }
545 
546 ubyte[] decompressLayerData(Layer)(const Layer layer, ubyte[] outBuffer) if (isSomeLayer!Layer)
547 {
548 	assert(layer.type == StorageType.compressedArray);
549 	return decompressLayerData(layer.getArray!ubyte, outBuffer);
550 }
551 
552 ubyte[] decompressLayerData(const ubyte[] _compressedData)
553 {
554 	ubyte[] compressedData = cast(ubyte[])_compressedData;
555 	auto dataSize = uncompressedDataLength(compressedData);
556 	ubyte[] buffer = allocLayerArray(dataSize);
557 	ubyte[] decompressedData = decompress(compressedData, buffer);
558 	//assert(buffer.length == decompressedData.length, "data size and length dont match");
559 	return decompressedData;
560 }
561 
562 // pops and returns size of uncompressed data. Modifies provided array.
563 size_t uncompressedDataLength()(auto ref ubyte[] compressedData)
564 {
565 	return decodeCborSingle!size_t(compressedData);
566 }
567 
568 ubyte[] decompressLayerData(const ubyte[] _compressedData, ubyte[] outBuffer)
569 {
570 	ubyte[] compressedData = cast(ubyte[])_compressedData;
571 	auto dataSize = decodeCborSingle!size_t(compressedData);
572 	//assert(outBuffer.length == dataSize, format("%s != %s", outBuffer.length, dataSize));
573 	ubyte[] decompressedData = decompress(compressedData, outBuffer);
574 	//assert(outBuffer.length == decompressedData.length, "data size and length dont match");
575 	return decompressedData;
576 }
577 
578 // Stores blocks of the chunk.
579 // Still used because ChunkLayerSnap needs custom (de)serizalizer.
580 struct ChunkLayerData
581 {
582 	void validate()
583 	{
584 		if (layerId == 0 && !uniform && blocks.length != BLOCKS_DATA_LENGTH) {
585 			fatalf("Size of uniform chunk != CHUNK_SIZE_CUBE, == %s", blocks.length);
586 		}
587 	}
588 
589 	/// null if uniform is true, or contains chunk data otherwise
590 	ubyte[] blocks;
591 
592 	/// type of common block
593 	ulong uniformType = 0; // Unknown block
594 
595 	/// is chunk filled with block of the same type
596 	bool uniform = true;
597 	uint dataLength;
598 
599 	ushort metadata;
600 	ubyte layerId;
601 }