123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726 |
- /*
- * UNPUBLISHED -- Rights reserved under the copyright laws of the
- * United States. Use of a copyright notice is precautionary only and
- * does not imply publication or disclosure.
- *
- * THIS DOCUMENTATION CONTAINS CONFIDENTIAL AND PROPRIETARY INFORMATION
- * OF VICARIOUS VISIONS, INC. ANY DUPLICATION, MODIFICATION,
- * DISTRIBUTION, OR DISCLOSURE IS STRICTLY PROHIBITED WITHOUT THE PRIOR
- * EXPRESS WRITTEN PERMISSION OF VICARIOUS VISIONS, INC.
- */
- /*
- AUTHOR: Dave Calvin
- CREATED: 2002-05-07
- SParse ARray Compressor. Given an array, this class reduces the memory
- needed to store the array by eliminating the most-frequently used element.
- The remaining elements are increased in size by one integer.
- If the compressed data would be larger than the original data, the
- original data is stored as is.
- Compression is O(2N) where N is the number of elements to compress.
- Decompression is O(log M + N) where M is the number of elements after
- compression (CompressedLength()) and N is the number of elements to decompress.
- Decompression is O(1) when the same or smaller amount of data is requested as
- the last decompression.
- The pointer returned by Decompress() is valid until the class is destroyed
- or a new call is made to Compress() or Decompress().
- Elements must define operator==, operator!=, and sizeof.
- */
- #ifndef __SPARC_H
- #define __SPARC_H
- #ifdef _GAMECUBE
- #define SPARC_BIG_ENDIAN
- #endif
- //Bigger than a short, smaller than an int.
- #pragma pack(push, 1)
- struct NotSoShort
- {
- unsigned char bytes[3];
- NotSoShort(void) {}
- NotSoShort(unsigned int source) {
- #ifdef SPARC_BIG_ENDIAN
- bytes[2] = source & 0xFF;
- bytes[1] = (source >> 8) & 0xFF;
- bytes[0] = (source >> 16) & 0xFF;
- #else
- bytes[0] = source & 0xFF;
- bytes[1] = (source >> 8) & 0xFF;
- bytes[2] = (source >> 16) & 0xFF;
- #endif
- }
- inline unsigned int GetValue(void) {
- #ifdef SPARC_BIG_ENDIAN
- return (bytes[0] << 16) | (bytes[1] << 8) | bytes[2];
- #else
- return (bytes[2] << 16) | (bytes[1] << 8) | bytes[0];
- #endif
- }
- inline bool operator==(unsigned int cmp) {
- #ifdef SPARC_BIG_ENDIAN
- return cmp == ((*(unsigned int*)bytes) >> 8);
- #else
- return cmp == ((*(unsigned int*)bytes) & 0x00FFFFFF);
- #endif
- }
- bool operator<(unsigned int cmp) {
- unsigned int tmp = *(unsigned int*)bytes;
- #ifdef SPARC_BIG_ENDIAN
- tmp >>= 8;
- #else
- tmp &= 0x00FFFFFF;
- #endif
- return tmp < cmp;
- }
- bool operator<=(unsigned int cmp) {
- unsigned int tmp = *(unsigned int*)bytes;
- #ifdef SPARC_BIG_ENDIAN
- tmp >>= 8;
- #else
- tmp &= 0x00FFFFFF;
- #endif
- return tmp <= cmp;
- }
- bool operator>(unsigned int cmp) {
- unsigned int tmp = *(unsigned int*)bytes;
- #ifdef SPARC_BIG_ENDIAN
- tmp >>= 8;
- #else
- tmp &= 0x00FFFFFF;
- #endif
- return tmp > cmp;
- }
- };
- //Compressed data is made up of these elements.
- template <class T, class U>
- struct SPARCElement
- {
- T data;
- U offset;
- };
- #pragma pack(pop)
- inline unsigned int SPARC_SWAP32(unsigned int x, bool doSwap) {
- if (doSwap) {
- return ((unsigned int)( ( (x & 0xff000000) >> 24)
- + ( (x & 0x00ff0000) >> 8 )
- + ( (x & 0x0000ff00) << 8 )
- + ( (x & 0x000000ff) << 24 ) ));
- }
- return x;
- }
- inline NotSoShort SPARC_SWAP24(NotSoShort x, bool doSwap) {
- if (doSwap) {
- x.bytes[0] ^= x.bytes[2];
- x.bytes[2] ^= x.bytes[0];
- x.bytes[0] ^= x.bytes[2];
- }
- return x;
- }
- inline unsigned short SPARC_SWAP16(unsigned short x, bool doSwap) {
- if (doSwap) {
- return ((unsigned short)( ( (x & 0xff00) >> 8)
- + ( (x & 0x00ff) << 8 ) ));
- }
- return x;
- }
- //The core of the SPARC system. T is the data type to be compressed.
- //U is the data type needed to store offsets information in the compressed
- //data. Smaller U makes for better compression but bigger data requires
- //larger U.
- template <class T, class U>
- class SPARCCore
- {
- private:
- //Using compression or just storing clear data?
- bool compressionUsed;
- //Compressed data and its length.
- SPARCElement<T, U> *compressedData;
- unsigned int compressedLength;
- //Decompression cache.
- T *decompressedData;
- unsigned int decompressedOffset;
- unsigned int decompressedLength;
- //Element which was removed to compress.
- T removedElement;
-
- //Length of original data before compression.
- unsigned int originalLength;
- //Memory allocators.
- void* (*Allocator)(unsigned int size);
- void (*Deallocator)(void *ptr);
- //Destroy all allocated memory.
- void Cleanup(void) {
- if(compressedData) {
- if(Deallocator) {
- Deallocator(compressedData);
- } else {
- delete [] compressedData;
- }
- compressedData = NULL;
- }
- if(decompressedData) {
- if(Deallocator) {
- Deallocator(decompressedData);
- } else {
- delete [] decompressedData;
- }
- decompressedData = NULL;
- }
- }
- void Init(void) {
- compressionUsed = false;
- compressedData = NULL;
- originalLength = 0;
- compressedLength = 0;
- decompressedData = NULL;
- decompressedOffset = 0;
- decompressedLength = 0;
- }
- //Binary search for the compressed element most closely matching 'offset'.
- SPARCElement<T, U> *FindDecompStart(unsigned int offset)
- {
- unsigned int startPoint = compressedLength / 2;
- unsigned int divisor = 4;
- unsigned int leap;
- while(1) {
- if(compressedData[startPoint].offset <= offset &&
- compressedData[startPoint+1].offset > offset) {
- if(compressedData[startPoint].offset == offset) {
- return &compressedData[startPoint];
- } else {
- return &compressedData[startPoint+1];
- }
- }
- leap = compressedLength / divisor;
- if(leap < 1) {
- leap = 1;
- } else {
- divisor *= 2;
- }
- if(compressedData[startPoint].offset > offset) {
- startPoint -= leap;
- } else {
- startPoint += leap;
- }
- }
- }
- public:
- SPARCCore(void) {
- Init();
- Allocator = NULL;
- Deallocator = NULL;
- }
- ~SPARCCore(void) {
- Cleanup();
- }
- void SetAllocator(void* (*alloc)(unsigned int size),
- void (*dealloc)(void *ptr)) {
- Allocator = alloc;
- Deallocator = dealloc;
- }
- //Just store the array without compression.
- unsigned int Store(const T *array, unsigned int length) {
- //Destroy old data.
- Cleanup();
- Init();
- //Allocate memory and copy array.
- if(Allocator) {
- decompressedData = (T*)Allocator(length * sizeof(T));
- } else {
- decompressedData = new T[length];
- }
- compressedLength = length;
- memcpy(decompressedData, array, sizeof(T) * length);
- //Set length.
- originalLength = length;
- return CompressedSize();
- }
- //Load compressed data directly.
- unsigned int Load(const char *array, unsigned int length) {
- //Destroy old data.
- Cleanup();
- Init();
- //Restore some attributes.
- compressionUsed = (bool)*array++;
- assert(sizeof(T) == 1); //For now only support characters.
- removedElement = *(T*)array;
- array += sizeof(T);
- originalLength = *(unsigned int*)array;
- array += sizeof(unsigned int);
- compressedLength = *(unsigned int*)array;
- array += sizeof(unsigned int);
- //Allocate memory and copy array.
- if (compressionUsed) {
- if(Allocator) {
- compressedData = (SPARCElement<T, U>*)
- Allocator(compressedLength * sizeof(SPARCElement<T, U>));
- } else {
- compressedData = new SPARCElement<T, U>[compressedLength];
- }
- memcpy(compressedData, array,
- compressedLength * sizeof(SPARCElement<T, U>));
- }
- else {
- if(Allocator) {
- decompressedData = (T*)Allocator(
- compressedLength * sizeof(T));
- } else {
- decompressedData = new T[compressedLength];
- }
- memcpy(decompressedData, array, compressedLength * sizeof(T));
- }
- return CompressedSize();
- }
- //Save state for later restoration.
- unsigned int Save(char *array, unsigned int length, bool doSwap) {
- //Figure out how much space is needed.
- unsigned int size = sizeof(char) + sizeof(T) +
- sizeof(unsigned int) + sizeof(unsigned int);
- if (compressionUsed) {
- size += compressedLength * sizeof(SPARCElement<T, U>);
- }
- else {
- size += compressedLength * sizeof(T);
- }
-
- assert(length >= size);
-
- //Save some attributes.
- *array++ = (char)compressionUsed;
- assert(sizeof(T) == 1); //For now only support characters.
- *(T*)array = removedElement;
- array += sizeof(T);
- *(unsigned int*)array = SPARC_SWAP32(originalLength, doSwap);
- array += sizeof(unsigned int);
- *(unsigned int*)array = SPARC_SWAP32(compressedLength, doSwap);
- array += sizeof(unsigned int);
- //Store compressed data (or uncompressed data if none exists)
- if (compressionUsed) {
- for (unsigned int i = 0; i < compressedLength; ++i) {
- //Copy the data element. For now only support characters.
- ((SPARCElement<T, U> *)array)[i].data = compressedData[i].data;
-
- //Copy the offset to the next unique element.
- if (sizeof(U) == 1) {
- ((SPARCElement<T, U> *)array)[i].offset =
- compressedData[i].offset;
- }
- else if (sizeof(U) == 2) {
- ((SPARCElement<T, unsigned short> *)array)[i].offset =
- SPARC_SWAP16(*(unsigned short*)&compressedData[i].offset,
- doSwap);
- }
- else if (sizeof(U) == 3) {
- ((SPARCElement<T, NotSoShort> *)array)[i].offset =
- SPARC_SWAP24(*(NotSoShort*)&compressedData[i].offset,
- doSwap);
- }
- else if (sizeof(U) == 4) {
- ((SPARCElement<T, unsigned int> *)array)[i].offset =
- SPARC_SWAP32(*(unsigned int*)&compressedData[i].offset,
- doSwap);
- }
- }
- }
- else {
- memcpy(array, decompressedData, compressedLength * sizeof(T));
- }
- return size;
- }
- //Compresses this array, returns the compressed size. Compresses
- //by eliminating the given element.
- unsigned int Compress(const T *array, unsigned int length, T removal) {
- unsigned int i;
- unsigned int numRemove = 0;
- SPARCElement<T, U> *compress;
- //Destroy old data.
- Cleanup();
- Init();
- //Count number of elements to remove. Can't remove first or
- //last element (prevents boundary conditions).
- for(i=1; i<length-1; i++) {
- if(array[i] == removal) {
- numRemove++;
- }
- }
- compressedLength = length - numRemove;
- originalLength = length;
- //If we're going to allocate more memory than was originally used,
- //just store the data.
- if(sizeof(SPARCElement<T, U>) * compressedLength >=
- sizeof(T) * length) {
- Store(array, length);
- return CompressedSize();
- }
- //Allocate memory for compressed elements.
- if(Allocator) {
- compressedData = (SPARCElement<T, U>*)
- Allocator(compressedLength * sizeof(SPARCElement<T, U>));
- } else {
- compressedData = new SPARCElement<T, U>[compressedLength];
- }
- compressionUsed = true;
- //Fill compressed array. First and last elements go in no matter
- //what.
- compressedData[0].data = array[0];
- compressedData[0].offset = 0;
- compress = &compressedData[1];
- for(i=1; i<length-1; i++) {
- if(array[i] != removal) {
- compress->data = array[i];
- compress->offset = i;
- compress++;
- }
- }
- compress->data = array[i];
- compress->offset = i;
- //Store removal value for decompression purposes.
- removedElement = removal;
- //Store original length for bounds checking.
- originalLength = length;
- //Return the compressed size.
- return CompressedSize();
- }
- //Get the compressed data size in bytes, or 0 if nothing stored.
- unsigned int CompressedSize(void) {
- return compressedLength * sizeof(SPARCElement<T, U>);
- }
- //Get the decompressed data starting at offset and ending at
- //offset + length. Returns NULL on error.
- const T *Decompress(unsigned int offset, unsigned int length) {
- SPARCElement<T, U> *decomp = NULL;
- unsigned int i;
- //If data isn't compressed, just return a pointers.
- if(!compressionUsed) {
- return decompressedData + offset;
- }
-
- //If last decompression falls within offset and length, just return
- //a pointer.
- if(decompressedData && decompressedOffset <= offset &&
- decompressedOffset + decompressedLength >= offset + length) {
- return decompressedData + offset - decompressedOffset;
- }
- //Allocate new space for decompression if length has changed.
- if(length != decompressedLength) {
- //Destroy old data first.
- if(decompressedData) {
- if(Deallocator) {
- Deallocator(decompressedData);
- } else {
- delete [] decompressedData;
- }
- }
- if(Allocator) {
- decompressedData = (T*)Allocator(length * sizeof(T));
- } else {
- decompressedData = new T[length];
- }
- }
- decompressedOffset = offset;
- decompressedLength = length;
- //Find position to start decompressing from.
- decomp = FindDecompStart(offset);
- if(!decomp) { //should never happen
- assert(0);
- return NULL;
- }
- //Decompress the data.
- for(i=0; i < length; i++) {
- if(decomp->offset == i + offset) {
- decompressedData[i] = decomp->data;
- decomp++;
- } else {
- decompressedData[i] = removedElement;
- }
- }
- return decompressedData;
- }
- };
- //The user-interface to SPARC. Automatically selects the best core based
- //on data size.
- template <class T>
- class SPARC
- {
- private:
- void *core;
- unsigned char offsetBytes;
- //Memory allocators.
- void* (*Allocator)(unsigned int size);
- void (*Deallocator)(void *ptr);
- public:
- SPARC(void) {
- core = NULL;
- offsetBytes = 0;
- Allocator = NULL;
- Deallocator = NULL;
- }
- ~SPARC(void) {
- Release();
- };
- void SetAllocator(void* (*alloc)(unsigned int size),
- void (*dealloc)(void *ptr)) {
- Allocator = alloc;
- Deallocator = dealloc;
- }
- //Select a core, cast it to the right type and return the size.
- unsigned int CompressedSize(void) {
- if(!core) {
- return 0;
- }
- switch(offsetBytes) {
- case 1:
- return ((SPARCCore<T, unsigned char>*)core)->CompressedSize();
- case 2:
- return ((SPARCCore<T, unsigned short>*)core)->CompressedSize();
- case 3:
- return ((SPARCCore<T, NotSoShort>*)core)->CompressedSize();
- case 4:
- return ((SPARCCore<T, unsigned int>*)core)->CompressedSize();
- }
- return 0;
- }
- //Always use the same core type since we won't be compressing.
- unsigned int Store(const T *array, unsigned int length)
- {
- Release();
- offsetBytes = 1;
- core = new SPARCCore<T, unsigned char>;
- ((SPARCCore<T, unsigned char>*)core)->
- SetAllocator(Allocator, Deallocator);
- return ((SPARCCore<T, unsigned char>*)core)-> Store(array, length);
- }
- //Load compressed data directly.
- unsigned int Load(const char *array, unsigned int length) {
- Release();
- offsetBytes = *array++;
- switch (offsetBytes) {
- case 1:
- core = new SPARCCore<T, unsigned char>;
- ((SPARCCore<T, unsigned char>*)core)->
- SetAllocator(Allocator, Deallocator);
- return ((SPARCCore<T, unsigned char>*)core)->
- Load(array, length-1);
- case 2:
- core = new SPARCCore<T, unsigned short>;
- ((SPARCCore<T, unsigned short>*)core)->
- SetAllocator(Allocator, Deallocator);
- return ((SPARCCore<T, unsigned short>*)core)->
- Load(array, length-1);
- case 3:
- core = new SPARCCore<T, NotSoShort>;
- ((SPARCCore<T, NotSoShort>*)core)->
- SetAllocator(Allocator, Deallocator);
- return ((SPARCCore<T, NotSoShort>*)core)->
- Load(array, length-1);
- case 4:
- core = new SPARCCore<T, unsigned int>;
- ((SPARCCore<T, unsigned int>*)core)->
- SetAllocator(Allocator, Deallocator);
- return ((SPARCCore<T, unsigned int>*)core)->
- Load(array, length-1);
- default:
- assert(false);
- return 0;
- }
- }
- //Save compressed data into array.
- unsigned int Save(char *array, unsigned int length, bool doSwap) {
- *array++ = offsetBytes;
- switch (offsetBytes) {
- case 1:
- return ((SPARCCore<T, unsigned char>*)core)->
- Save(array, length-1, doSwap);
- case 2:
- return ((SPARCCore<T, unsigned short>*)core)->
- Save(array, length-1, doSwap);
- case 3:
- return ((SPARCCore<T, NotSoShort>*)core)->
- Save(array, length-1, doSwap);
- case 4:
- return ((SPARCCore<T, unsigned int>*)core)->
- Save(array, length-1, doSwap);
- default:
- assert(false);
- return 0;
- }
- }
- //Create the smallest core possible for the given data.
- unsigned int Compress(const T *array, unsigned int length, T removal) {
- Release();
- if(length < 256) {
- offsetBytes = 1;
- core = new SPARCCore<T, unsigned char>;
- ((SPARCCore<T, unsigned char>*)core)->
- SetAllocator(Allocator, Deallocator);
- return ((SPARCCore<T, unsigned char>*)core)->
- Compress(array, length, removal);
- } else if(length < 65536) {
- offsetBytes = 2;
- core = new SPARCCore<T, unsigned short>;
- ((SPARCCore<T, unsigned short>*)core)->
- SetAllocator(Allocator, Deallocator);
- return ((SPARCCore<T, unsigned short>*)core)->
- Compress(array, length, removal);
- } else if(length < 16777216) {
- offsetBytes = 3;
- core = new SPARCCore<T, NotSoShort>;
- ((SPARCCore<T, NotSoShort>*)core)->
- SetAllocator(Allocator, Deallocator);
- return ((SPARCCore<T, NotSoShort>*)core)->
- Compress(array, length, removal);
- } else {
- offsetBytes = 4;
- core = new SPARCCore<T, unsigned int>;
- ((SPARCCore<T, unsigned int>*)core)->
- SetAllocator(Allocator, Deallocator);
- return ((SPARCCore<T, unsigned int>*)core)->
- Compress(array, length, removal);
- }
- }
- //Cast to the correct core type and decompress.
- const T *Decompress(unsigned int offset, unsigned int length) {
- if(!core) {
- return NULL;
- }
- switch(offsetBytes) {
- case 1:
- return ((SPARCCore<T, unsigned char>*)core)->
- Decompress(offset, length);
- case 2:
- return ((SPARCCore<T, unsigned short>*)core)->
- Decompress(offset, length);
- case 3:
- return ((SPARCCore<T, NotSoShort>*)core)->
- Decompress(offset, length);
- case 4:
- return ((SPARCCore<T, unsigned int>*)core)->
- Decompress(offset, length);
- }
- return NULL;
- }
- //Destroy all compressed data and the current decompressed buffer.
- void Release(void) {
- if(core) {
- switch(offsetBytes) {
- case 1:
- delete (SPARCCore<T, unsigned char>*)core;
- break;
- case 2:
- delete (SPARCCore<T, unsigned short>*)core;
- break;
- case 3:
- delete (SPARCCore<T, NotSoShort>*)core;
- break;
- case 4:
- delete (SPARCCore<T, unsigned int>*)core;
- break;
- }
- core = NULL;
- }
- }
- };
- #endif
|