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