1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
|
#include "stdafx.h"
#include "SparseDataStorage.h"
// Note: See header for an overview of this class
int SparseDataStorage::deleteQueueIndex;
XLockFreeStack <unsigned char> SparseDataStorage::deleteQueue[3];
void SparseDataStorage::staticCtor()
{
for( int i = 0; i < 3; i++ )
{
deleteQueue[i].Initialize();
}
}
// Initialise data storage, with very limited compression - the very first plane is stored as either compressed to be "all 0", and the rest of the planes aren't compressed at all.
// The reason behind this is to keep the total allocation as a round number of 4K (small) pages, ie 16K.
// By doing this, and doing this "special" allocation as a XPhysicalAlloc rather than a malloc, we can help ensure that this full allocation gets cleaned up properly when the first
// proper compression is done on this storage. If it were just allocated with malloc, then the memory management system would have a large number of 16512 allocations to free, and
// it seems from experimentation that these basically don't make it back to the system as free pages.
// Note - the other approach here would be to allocate *no* actual storage for the data at the ctor stage. However, as chunks are created then this creates an awful lot of intermediate
// stages as each line of data is added, so it is actually much cleaner to just allocate almost fully here & then attempt to do a single compression pass over the data later on.
SparseDataStorage::SparseDataStorage()
{
// Allocate using physical alloc. As this will (by default) return memory from the pool of 4KB pages, the address will in the range of MM_PHYSICAL_4KB_BASE upwards. We can use
// this fact to identify the allocation later, and so free it with the corresponding call to XPhysicalFree.
#ifdef _XBOX
unsigned char *planeIndices = (unsigned char *)XPhysicalAlloc(128 * 128, MAXULONG_PTR, 4096, PAGE_READWRITE);
#else
unsigned char *planeIndices = static_cast<unsigned char *>(malloc(128 * 128));
#endif
unsigned char *data = planeIndices + 128;
planeIndices[0] = ALL_0_INDEX;
for( int i = 1; i < 128; i++ )
{
planeIndices[i] = i - 1;
}
XMemSet(data, 0, 128 * 127);
// Data and count packs together the pointer to our data and the count of planes allocated - 127 planes allocated in this case
#pragma warning ( disable : 4826 )
dataAndCount = 0x007F000000000000L | (( (int64_t) planeIndices ) & 0x0000ffffffffffffL);
#pragma warning ( default : 4826 )
#ifdef DATA_COMPRESSION_STATS
count = 128;
#endif
}
SparseDataStorage::SparseDataStorage(bool isUpper)
{
// Allocate using physical alloc. As this will (by default) return memory from the pool of 4KB pages, the address will in the range of MM_PHYSICAL_4KB_BASE upwards. We can use
// this fact to identify the allocation later, and so free it with the corresponding call to XPhysicalFree.
unsigned char *planeIndices = static_cast<unsigned char *>(malloc(128));
for( int i = 0; i < 128; i++ )
{
planeIndices[i] = ALL_0_INDEX;
}
// Data and count packs together the pointer to our data and the count of planes allocated - 127 planes allocated in this case
#pragma warning ( disable : 4826 )
dataAndCount = 0x0000000000000000L | (( (int64_t) planeIndices ) & 0x0000ffffffffffffL);
#pragma warning ( default : 4826 )
#ifdef DATA_COMPRESSION_STATS
count = 128;
#endif
}
SparseDataStorage::~SparseDataStorage()
{
unsigned char *indicesAndData = (unsigned char *)(dataAndCount & 0x0000ffffffffffff);
// Determine correct means to free this data - could have been allocated either with XPhysicalAlloc or malloc
#ifdef _XBOX
if( (unsigned int)indicesAndData >= MM_PHYSICAL_4KB_BASE )
{
XPhysicalFree(indicesAndData);
}
else
#endif
{
free(indicesAndData);
}
// printf("Free (in dtor) 0x%x\n", indicesAndData);
}
SparseDataStorage::SparseDataStorage(SparseDataStorage *copyFrom)
{
// Extra details of source storage
const int64_t sourceDataAndCount = copyFrom->dataAndCount;
const unsigned char *sourceIndicesAndData = (unsigned char *)(sourceDataAndCount & 0x0000ffffffffffff);
int sourceCount = (sourceDataAndCount >> 48 ) & 0xffff;
// Allocate & copy indices ( 128 bytes ) and any allocated planes (128 * count)
unsigned char *destIndicesAndData = static_cast<unsigned char *>(malloc(sourceCount * 128 + 128));
// AP - I've moved this to be before the memcpy because of a very strange bug on vita. Sometimes dataAndCount wasn't valid in time when ::get was called.
// This should never happen and this isn't a proper solution but fixes it for now.
#pragma warning ( disable : 4826 )
dataAndCount = ( sourceDataAndCount & 0xffff000000000000L ) | ( ((int64_t) destIndicesAndData ) & 0x0000ffffffffffffL );
#pragma warning ( default : 4826 )
XMemCpy( destIndicesAndData, sourceIndicesAndData, sourceCount * 128 + 128 );
#ifdef DATA_COMPRESSION_STATS
count = sourceCount;
#endif
}
// Set all data values from a data array of length 16384 (128 x 16 x 16 x 0.5). Source data must have same order as original java game
void SparseDataStorage::setData(byteArray dataIn, unsigned int inOffset)
{
// Original order is defined as:
// pos = (x << 11 | z << 7 | y);
// slot = pos >> 1;
// part = pos & 1;
// if ( part == 0 ) value = data[slot] & 0xf
// else value = (data[slot] >> 4) & 0xf
// Two passed through the data. First pass sets up plane indices, and counts number of planes that we actually need to allocate
int allocatedPlaneCount = 0;
unsigned char _planeIndices[128];
//unsigned char *lastDataPointer = (unsigned char *)(dataAndCount & 0x0000ffffffffffff);
for( int y = 0; y < 128; y++ )
{
bool all0 = true;
for( int xz = 0; xz < 256; xz++ ) // 256 in loop as 16 x 16 separate bytes need checked
{
const int pos = ( xz << 7 ) | y;
const int slot = pos >> 1;
const int part = pos & 1;
const unsigned char value = ( dataIn[slot + inOffset] >> (part * 4) ) & 15;
if( value != 0 ) all0 = false;
}
if( all0 )
{
_planeIndices[y] = ALL_0_INDEX;
}
else
{
_planeIndices[y] = allocatedPlaneCount++;
}
}
// Allocate required storage
unsigned char *planeIndices = static_cast<unsigned char *>(malloc(128 * allocatedPlaneCount + 128));
unsigned char *data = planeIndices + 128;
XMemCpy(planeIndices, _planeIndices, 128);
// Second pass through to actually copy the data in to the storage allocated for the required planes
unsigned char *pucOut = data;
for( int y = 0; y < 128 ; y++ )
{
// Index will be < 128 if we allocated storage for it and it has a valid index. No need to actually check the index as
// we know they were sequentially allocated above.
if( planeIndices[y] < 128 )
{
const int part = y & 1;
//int shift = 4 * part;
const unsigned char *pucIn = &dataIn[ (y >> 1) + inOffset];
for( int xz = 0; xz < 128; xz++ ) // 128 ( 16 x 16 x 0.5 ) in loop as packing 2 values into each destination byte
{
*pucOut = ( ( *pucIn ) >> ( part * 4 ) ) & 15;
pucIn += 64;
*pucOut |= ( ( ( *pucIn ) >> ( part * 4 ) ) & 15 ) << 4;
pucIn += 64;
pucOut++;
}
}
}
// Get new data and count packed info
#pragma warning ( disable : 4826 )
int64_t newDataAndCount = reinterpret_cast<int64_t>(planeIndices) & 0x0000ffffffffffffL;
#pragma warning ( default : 4826 )
newDataAndCount |= static_cast<int64_t>(allocatedPlaneCount) << 48;
updateDataAndCount( newDataAndCount );
}
// Gets all data values into an array of length 16384. Destination data will have same order as original java game.
void SparseDataStorage::getData(byteArray retArray, unsigned int retOffset)
{
XMemSet(retArray.data + + retOffset, 0, 16384);
unsigned char *planeIndices, *data;
getPlaneIndicesAndData(&planeIndices, &data);
// Original order is defined as:
// pos = (x << 11 | z << 7 | y);
// slot = pos >> 1;
// part = pos & 1;
// if ( part == 0 ) value = data[slot] & 0xf
// else value = (data[slot] >> 4) & 0xf
for( int y = 0; y < 128; y++ )
{
if( planeIndices[y] == ALL_0_INDEX )
{
// No need to do anything in this case as retArray is initialised to zero
}
else
{
const int part = y & 1;
const int shift = 4 * part;
unsigned char *pucOut = &retArray.data[ (y >> 1) + + retOffset];
const unsigned char *pucIn = &data[ planeIndices[ y ] * 128 ];
for( int xz = 0; xz < 128; xz++ ) // 128 in loop (16 x 16 x 0.5) as input data is being treated in pairs of nybbles that are packed in the same byte
{
unsigned char value = (*pucIn) & 15;
*pucOut |= ( value << shift );
pucOut += 64;
value = ((*pucIn) >> 4 ) & 15;
*pucOut |= ( value << shift );
pucOut += 64;
pucIn++;
}
}
}
}
// Get an individual data value
int SparseDataStorage::get(int x, int y, int z)
{
unsigned char *planeIndices, *data;
getPlaneIndicesAndData(&planeIndices, &data);
if( planeIndices[y] == ALL_0_INDEX )
{
return 0;
}
else
{
const int planeIndex = x * 16 + z; // Index within this xz plane
const int byteIndex = planeIndex / 2; // Byte index within the plane (2 tiles stored per byte)
const int shift = ( planeIndex & 1 ) * 4; // Bit shift within the byte
const int retval = ( data[ planeIndices[y] * 128 + byteIndex ] >> shift ) & 15;
return retval;
}
}
// Set an individual data value
void SparseDataStorage::set(int x, int y, int z, int val)
{
unsigned char *planeIndices, *data;
getPlaneIndicesAndData(&planeIndices, &data);
// If this plane isn't yet allocated, then we might have some extra work to do
if( planeIndices[y] >= ALL_0_INDEX )
{
// No data allocated. Early out though if we are storing what is already represented by our special index.
if( ( val == 0 ) && ( planeIndices[y] == ALL_0_INDEX ) )
{
return;
}
// Reallocate the storage for planes to accomodate one extra
addNewPlane(y);
// Get pointers again as these may have moved
getPlaneIndicesAndData(&planeIndices, &data);
}
// Either data was already allocated, or we've just done that. Now store our value into the right place.
const int planeIndex = x * 16 + z; // Index within this xz plane
const int byteIndex = planeIndex / 2; // Byte index within the plane (2 tiles stored per byte)
const int shift = ( planeIndex & 1 ) * 4; // Bit shift within the byte
const int mask = 0xf0 >> shift;
const int idx = planeIndices[y] * 128 + byteIndex;
data[idx] = ( data[idx] & mask ) | ( val << shift );
}
// Sets a region of data values with the data at offset position in the array dataIn - external ordering compatible with java DataLayer
// Note - when data was extracted from the original data layers by LevelChunk::getBlocksAndData, y0 had to have even alignment and y1 - y0 also
// needed to be even as data was packed in nyblles in this dimension, and the code didn't make any attempt to unpack it. This behaviour is copied
// here for compatibility even though our source data isn't packed this way.
// Returns size of data copied.
int SparseDataStorage::setDataRegion(byteArray dataIn, int x0, int y0, int z0, int x1, int y1, int z1, int offset, tileUpdatedCallback callback, void *param, int yparam)
{
// Actual setting of data happens when calling set method so no need to lock here
const unsigned char *pucIn = &dataIn.data[offset];
if( callback )
{
for( int x = x0; x < x1; x++ )
{
for( int z = z0; z < z1; z++ )
{
// Emulate how data was extracted from DataLayer... see comment above
const int yy0 = y0 & 0xfffffffe;
const int len = ( y1 - y0 ) / 2;
for( int i = 0; i < len; i++ )
{
const int y = yy0 + ( i * 2 );
int toSet = (*pucIn) & 15;
if( get(x, y, z) != toSet )
{
set(x, y, z, toSet );
callback(x, y, z, param, yparam);
}
toSet = ((*pucIn) >> 4 ) & 15;
if( get(x, y + 1, z) != toSet )
{
set(x, y + 1, z, toSet );
callback(x, y + 1, z, param, yparam);
}
pucIn++;
}
}
}
}
else
{
for( int x = x0; x < x1; x++ )
{
for( int z = z0; z < z1; z++ )
{
// Emulate how data was extracted from DataLayer... see comment above
const int yy0 = y0 & 0xfffffffe;
const int len = ( y1 - y0 ) / 2;
for( int i = 0; i < len; i++ )
{
const int y = yy0 + ( i * 2 );
set(x, y, z, (*pucIn) & 15 );
set(x, y + 1, z, ((*pucIn) >> 4 ) & 15 );
pucIn++;
}
}
}
}
const ptrdiff_t count = pucIn - &dataIn.data[offset];
return static_cast<int>(count);
}
// Updates the data at offset position dataInOut with a region of data information - external ordering compatible with java DataLayer
// Note - when data was placed in the original data layers by LevelChunk::setBlocksAndData, y0 had to have even alignment and y1 - y0 also
// needed to be even as data was packed in nyblles in this dimension, and the code didn't make any attempt to unpack it. This behaviour is copied
// here for compatibility even though our source data isn't packed this way
// Returns size of data copied.
int SparseDataStorage::getDataRegion(byteArray dataInOut, int x0, int y0, int z0, int x1, int y1, int z1, int offset)
{
unsigned char *pucOut = &dataInOut.data[offset];
for( int x = x0; x < x1; x++ )
{
for( int z = z0; z < z1; z++ )
{
// Emulate how data was extracted from DataLayer... see comment above
const int yy0 = y0 & 0xfffffffe;
const int len = ( y1 - y0 ) / 2;
for( int i = 0; i < len; i++ )
{
const int y = yy0 + ( i * 2 );
*pucOut = get( x, y, z);
*pucOut |= get( x, y + 1, z) << 4;
pucOut++;
}
}
}
const ptrdiff_t count = pucOut - &dataInOut.data[offset];
return static_cast<int>(count);
}
void SparseDataStorage::addNewPlane(int y)
{
bool success = false;
do
{
// Get last packed data pointer & count
const int64_t lastDataAndCount = dataAndCount;
// Unpack count & data pointer
const int lastLinesUsed = static_cast<int>((lastDataAndCount >> 48) & 0xffff);
unsigned char *lastDataPointer = (unsigned char *)(lastDataAndCount & 0x0000ffffffffffff);
// Find out what to prefill the newly allocated line with
const unsigned char planeIndex = lastDataPointer[y];
if( planeIndex < ALL_0_INDEX ) return; // Something has already allocated this line - we're done
int linesUsed = lastLinesUsed + 1;
// Allocate new memory storage, copy over anything from old storage, and initialise remainder
auto dataPointer = static_cast<unsigned char *>(malloc(linesUsed * 128 + 128));
XMemCpy( dataPointer, lastDataPointer, 128 * lastLinesUsed + 128);
XMemSet( dataPointer + ( 128 * lastLinesUsed ) + 128, 0, 128 );
dataPointer[y] = lastLinesUsed;
// Get new data and count packed info
#pragma warning ( disable : 4826 )
int64_t newDataAndCount = reinterpret_cast<int64_t>(dataPointer) & 0x0000ffffffffffffL;
#pragma warning ( default : 4826 )
newDataAndCount |= static_cast<int64_t>(linesUsed) << 48;
// Attempt to update the data & count atomically. This command will Only succeed if the data stored at
// dataAndCount is equal to lastDataAndCount, and will return the value present just before the write took place
const int64_t lastDataAndCount2 = InterlockedCompareExchangeRelease64( &dataAndCount, newDataAndCount, lastDataAndCount );
if( lastDataAndCount2 == lastDataAndCount )
{
success = true;
// Queue old data to be deleted
queueForDelete( lastDataPointer );
// printf("Marking for delete 0x%x\n", lastDataPointer);
#ifdef DATA_COMPRESSION_STATS
count = linesUsed;
#endif
}
else
{
// If we didn't succeed, queue data that we made to be deleted, and try again
queueForDelete( dataPointer );
// printf("Marking for delete (fail) 0x%x\n", dataPointer);
}
} while( !success );
}
void SparseDataStorage::getPlaneIndicesAndData(unsigned char **planeIndices, unsigned char **data)
{
unsigned char *indicesAndData = reinterpret_cast<unsigned char *>(dataAndCount & 0x0000ffffffffffff);
*planeIndices = indicesAndData;
*data = indicesAndData + 128;
}
void SparseDataStorage::queueForDelete(unsigned char *data)
{
// Add this into a queue for deleting. This shouldn't be actually deleted until tick has been called twice from when
// the data went into the queue.
deleteQueue[deleteQueueIndex].Push( data );
}
void SparseDataStorage::tick()
{
// We have 3 queues for deleting. Always delete from the next one after where we are writing to, so it should take 2 ticks
// before we ever delete something, from when the request to delete it came in
const int freeIndex = ( deleteQueueIndex + 1 ) % 3;
// printf("Free queue: %d, %d\n",deleteQueue[freeIndex].GetEntryCount(),deleteQueue[freeIndex].GetAllocated());
unsigned char *toFree = nullptr;
do
{
toFree = deleteQueue[freeIndex].Pop();
// if( toFree ) printf("Deleting 0x%x\n", toFree);
// Determine correct means to free this data - could have been allocated either with XPhysicalAlloc or malloc
#ifdef _XBOX
if( (unsigned int)toFree >= MM_PHYSICAL_4KB_BASE )
{
XPhysicalFree(toFree);
}
else
#endif
{
free(toFree);
}
} while( toFree );
deleteQueueIndex = ( deleteQueueIndex + 1 ) % 3;
}
// Update storage with a new values for dataAndCount, repeating as necessary if other simultaneous writes happen.
void SparseDataStorage::updateDataAndCount(int64_t newDataAndCount)
{
// Now actually assign this data to the storage. Just repeat until successful, there isn't any useful really that we can merge the results of this
// with any other simultaneous writes that might be happening.
bool success = false;
do
{
const int64_t lastDataAndCount = dataAndCount;
unsigned char *lastDataPointer = reinterpret_cast<unsigned char *>(lastDataAndCount & 0x0000ffffffffffff);
// Attempt to update the data & count atomically. This command will Only succeed if the data stored at
// dataAndCount is equal to lastDataAndCount, and will return the value present just before the write took place
const int64_t lastDataAndCount2 = InterlockedCompareExchangeRelease64( &dataAndCount, newDataAndCount, lastDataAndCount );
if( lastDataAndCount2 == lastDataAndCount )
{
success = true;
// Queue old data to be deleted
// printf("Marking for delete 0x%x (full replace)\n", lastDataPointer);
queueForDelete( lastDataPointer );
}
} while( !success);
#ifdef DATA_COMPRESSION_STATS
count = ( newDataAndCount >> 48 ) & 0xffff;
#endif
}
// Attempt to compress the stored data. This method makes no guarantee of success - if it fails due to something else writing to the storage whilst this is running, then it won't actually do anything.
int SparseDataStorage::compress()
{
unsigned char _planeIndices[128];
bool needsCompressed = false;
const int64_t lastDataAndCount = dataAndCount;
unsigned char *planeIndices = (unsigned char *)(lastDataAndCount & 0x0000ffffffffffff);
unsigned char *data = planeIndices + 128;
int planesToAlloc = 0;
for( int i = 0; i < 128; i++ )
{
if( planeIndices[i] == ALL_0_INDEX )
{
_planeIndices[i] = ALL_0_INDEX;
}
else
{
const unsigned char *pucData = &data[ 128 * planeIndices[i] ];
bool all0 = true;
for( int j = 0; j < 128; j++ ) // 16 x 16 x 4-bits
{
if( *pucData != 0 ) all0 = false;
pucData++;
}
if( all0 )
{
_planeIndices[i] = ALL_0_INDEX;
needsCompressed = true;
}
else
{
_planeIndices[i] = planesToAlloc++;
}
}
}
if( needsCompressed )
{
unsigned char *newIndicesAndData = static_cast<unsigned char *>(malloc(128 + 128 * planesToAlloc));
unsigned char *pucData = newIndicesAndData + 128;
XMemCpy( newIndicesAndData, _planeIndices, 128 );
for( int i = 0; i < 128; i++ )
{
if( newIndicesAndData[i] < ALL_0_INDEX )
{
XMemCpy( pucData, &data[ 128 * planeIndices[i] ], 128 );
pucData += 128;
}
}
// Get new data and count packed info
#pragma warning ( disable : 4826 )
int64_t newDataAndCount = ((int64_t) newIndicesAndData) & 0x0000ffffffffffffL;
#pragma warning ( default : 4826 )
newDataAndCount |= static_cast<int64_t>(planesToAlloc) << 48;
// Attempt to update the data & count atomically. This command will Only succeed if the data stored at
// dataAndCount is equal to lastDataAndCount, and will return the value present just before the write took place
const int64_t lastDataAndCount2 = InterlockedCompareExchangeRelease64( &dataAndCount, newDataAndCount, lastDataAndCount );
if( lastDataAndCount2 != lastDataAndCount )
{
// Failed to write. Don't bother trying again... being very conservative here.
// printf("Marking for delete 0x%x (compress fail)\n", newIndicesAndData);
queueForDelete( newIndicesAndData );
}
else
{
// Success
queueForDelete( planeIndices );
// printf("Successfully compressed to %d planes, to delete 0x%x\n", planesToAlloc, planeIndices);
#ifdef DATA_COMPRESSION_STATS
count = planesToAlloc;
#endif
}
return planesToAlloc;
}
else
{
return static_cast<int>((lastDataAndCount >> 48) & 0xffff);
}
}
bool SparseDataStorage::isCompressed()
{
const int count = ( dataAndCount >> 48 ) & 0xffff;
return (count < 127);
}
void SparseDataStorage::write(DataOutputStream *dos)
{
const int count = ( dataAndCount >> 48 ) & 0xffff;
dos->writeInt(count);
unsigned char *dataPointer = (unsigned char *)(dataAndCount & 0x0000ffffffffffff);
const byteArray wrapper(dataPointer, count * 128 + 128);
dos->write(wrapper);
}
void SparseDataStorage::read(DataInputStream *dis)
{
const int count = dis->readInt();
unsigned char *dataPointer = static_cast<unsigned char *>(malloc(count * 128 + 128));
const byteArray wrapper(dataPointer, count * 128 + 128);
dis->readFully(wrapper);
#pragma warning ( disable : 4826 )
int64_t newDataAndCount = reinterpret_cast<int64_t>(dataPointer) & 0x0000ffffffffffffL;
#pragma warning ( default : 4826 )
newDataAndCount |= static_cast<int64_t>(count) << 48;
updateDataAndCount(newDataAndCount);
}
|