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