nuclear@14: //----------------------------------------------------------------------------- nuclear@14: // Product: OpenCTM nuclear@14: // File: stream.c nuclear@14: // Description: Stream I/O functions. nuclear@14: //----------------------------------------------------------------------------- nuclear@14: // Copyright (c) 2009-2010 Marcus Geelnard nuclear@14: // nuclear@14: // This software is provided 'as-is', without any express or implied nuclear@14: // warranty. In no event will the authors be held liable for any damages nuclear@14: // arising from the use of this software. nuclear@14: // nuclear@14: // Permission is granted to anyone to use this software for any purpose, nuclear@14: // including commercial applications, and to alter it and redistribute it nuclear@14: // freely, subject to the following restrictions: nuclear@14: // nuclear@14: // 1. The origin of this software must not be misrepresented; you must not nuclear@14: // claim that you wrote the original software. If you use this software nuclear@14: // in a product, an acknowledgment in the product documentation would be nuclear@14: // appreciated but is not required. nuclear@14: // nuclear@14: // 2. Altered source versions must be plainly marked as such, and must not nuclear@14: // be misrepresented as being the original software. nuclear@14: // nuclear@14: // 3. This notice may not be removed or altered from any source nuclear@14: // distribution. nuclear@14: //----------------------------------------------------------------------------- nuclear@14: nuclear@14: #include nuclear@14: #include nuclear@14: #include nuclear@14: #include "openctm.h" nuclear@14: #include "internal.h" nuclear@14: nuclear@14: #ifdef __DEBUG_ nuclear@14: #include nuclear@14: #endif nuclear@14: nuclear@14: //----------------------------------------------------------------------------- nuclear@14: // _ctmStreamRead() - Read data from a stream. nuclear@14: //----------------------------------------------------------------------------- nuclear@14: CTMuint _ctmStreamRead(_CTMcontext * self, void * aBuf, CTMuint aCount) nuclear@14: { nuclear@14: if(!self->mUserData || !self->mReadFn) nuclear@14: return 0; nuclear@14: nuclear@14: return self->mReadFn(aBuf, aCount, self->mUserData); nuclear@14: } nuclear@14: nuclear@14: //----------------------------------------------------------------------------- nuclear@14: // _ctmStreamWrite() - Write data to a stream. nuclear@14: //----------------------------------------------------------------------------- nuclear@14: CTMuint _ctmStreamWrite(_CTMcontext * self, void * aBuf, CTMuint aCount) nuclear@14: { nuclear@14: if(!self->mUserData || !self->mWriteFn) nuclear@14: return 0; nuclear@14: nuclear@14: return self->mWriteFn(aBuf, aCount, self->mUserData); nuclear@14: } nuclear@14: nuclear@14: //----------------------------------------------------------------------------- nuclear@14: // _ctmStreamReadUINT() - Read an unsigned integer from a stream in a machine nuclear@14: // endian independent manner (for portability). nuclear@14: //----------------------------------------------------------------------------- nuclear@14: CTMuint _ctmStreamReadUINT(_CTMcontext * self) nuclear@14: { nuclear@14: unsigned char buf[4]; nuclear@14: _ctmStreamRead(self, (void *) buf, 4); nuclear@14: return ((CTMuint) buf[0]) | nuclear@14: (((CTMuint) buf[1]) << 8) | nuclear@14: (((CTMuint) buf[2]) << 16) | nuclear@14: (((CTMuint) buf[3]) << 24); nuclear@14: } nuclear@14: nuclear@14: //----------------------------------------------------------------------------- nuclear@14: // _ctmStreamWriteUINT() - Write an unsigned integer to a stream in a machine nuclear@14: // endian independent manner (for portability). nuclear@14: //----------------------------------------------------------------------------- nuclear@14: void _ctmStreamWriteUINT(_CTMcontext * self, CTMuint aValue) nuclear@14: { nuclear@14: unsigned char buf[4]; nuclear@14: buf[0] = aValue & 0x000000ff; nuclear@14: buf[1] = (aValue >> 8) & 0x000000ff; nuclear@14: buf[2] = (aValue >> 16) & 0x000000ff; nuclear@14: buf[3] = (aValue >> 24) & 0x000000ff; nuclear@14: _ctmStreamWrite(self, (void *) buf, 4); nuclear@14: } nuclear@14: nuclear@14: //----------------------------------------------------------------------------- nuclear@14: // _ctmStreamReadFLOAT() - Read a floating point value from a stream in a nuclear@14: // machine endian independent manner (for portability). nuclear@14: //----------------------------------------------------------------------------- nuclear@14: CTMfloat _ctmStreamReadFLOAT(_CTMcontext * self) nuclear@14: { nuclear@14: union { nuclear@14: CTMfloat f; nuclear@14: CTMuint i; nuclear@14: } u; nuclear@14: u.i = _ctmStreamReadUINT(self); nuclear@14: return u.f; nuclear@14: } nuclear@14: nuclear@14: //----------------------------------------------------------------------------- nuclear@14: // _ctmStreamWriteFLOAT() - Write a floating point value to a stream in a nuclear@14: // machine endian independent manner (for portability). nuclear@14: //----------------------------------------------------------------------------- nuclear@14: void _ctmStreamWriteFLOAT(_CTMcontext * self, CTMfloat aValue) nuclear@14: { nuclear@14: union { nuclear@14: CTMfloat f; nuclear@14: CTMuint i; nuclear@14: } u; nuclear@14: u.f = aValue; nuclear@14: _ctmStreamWriteUINT(self, u.i); nuclear@14: } nuclear@14: nuclear@14: //----------------------------------------------------------------------------- nuclear@14: // _ctmStreamReadSTRING() - Read a string value from a stream. The format of nuclear@14: // the string in the stream is: an unsigned integer (string length) followed by nuclear@14: // the string (without null termination). nuclear@14: //----------------------------------------------------------------------------- nuclear@14: void _ctmStreamReadSTRING(_CTMcontext * self, char ** aValue) nuclear@14: { nuclear@14: CTMuint len; nuclear@14: nuclear@14: // Clear the old string nuclear@14: if(*aValue) nuclear@14: { nuclear@14: free(*aValue); nuclear@14: *aValue = (char *) 0; nuclear@14: } nuclear@14: nuclear@14: // Get string length nuclear@14: len = _ctmStreamReadUINT(self); nuclear@14: nuclear@14: // Read string nuclear@14: if(len > 0) nuclear@14: { nuclear@14: *aValue = (char *) malloc(len + 1); nuclear@14: if(*aValue) nuclear@14: { nuclear@14: _ctmStreamRead(self, (void *) *aValue, len); nuclear@14: (*aValue)[len] = 0; nuclear@14: } nuclear@14: } nuclear@14: } nuclear@14: nuclear@14: //----------------------------------------------------------------------------- nuclear@14: // _ctmStreamWriteSTRING() - Write a string value to a stream. The format of nuclear@14: // the string in the stream is: an unsigned integer (string length) followed by nuclear@14: // the string (without null termination). nuclear@14: //----------------------------------------------------------------------------- nuclear@14: void _ctmStreamWriteSTRING(_CTMcontext * self, const char * aValue) nuclear@14: { nuclear@14: CTMuint len; nuclear@14: nuclear@14: // Get string length nuclear@14: if(aValue) nuclear@14: len = strlen(aValue); nuclear@14: else nuclear@14: len = 0; nuclear@14: nuclear@14: // Write string length nuclear@14: _ctmStreamWriteUINT(self, len); nuclear@14: nuclear@14: // Write string nuclear@14: if(len > 0) nuclear@14: _ctmStreamWrite(self, (void *) aValue, len); nuclear@14: } nuclear@14: nuclear@14: //----------------------------------------------------------------------------- nuclear@14: // _ctmStreamReadPackedInts() - Read an compressed binary integer data array nuclear@14: // from a stream, and uncompress it. nuclear@14: //----------------------------------------------------------------------------- nuclear@14: int _ctmStreamReadPackedInts(_CTMcontext * self, CTMint * aData, nuclear@14: CTMuint aCount, CTMuint aSize, CTMint aSignedInts) nuclear@14: { nuclear@14: size_t packedSize, unpackedSize; nuclear@14: CTMuint i, k, x; nuclear@14: CTMint value; nuclear@14: unsigned char * packed, * tmp; nuclear@14: unsigned char props[5]; nuclear@14: int lzmaRes; nuclear@14: nuclear@14: // Read packed data size from the stream nuclear@14: packedSize = (size_t) _ctmStreamReadUINT(self); nuclear@14: nuclear@14: // Read LZMA compression props from the stream nuclear@14: _ctmStreamRead(self, (void *) props, 5); nuclear@14: nuclear@14: // Allocate memory and read the packed data from the stream nuclear@14: packed = (unsigned char *) malloc(packedSize); nuclear@14: if(!packed) nuclear@14: { nuclear@14: self->mError = CTM_OUT_OF_MEMORY; nuclear@14: return CTM_FALSE; nuclear@14: } nuclear@14: _ctmStreamRead(self, (void *) packed, packedSize); nuclear@14: nuclear@14: // Allocate memory for interleaved array nuclear@14: tmp = (unsigned char *) malloc(aCount * aSize * 4); nuclear@14: if(!tmp) nuclear@14: { nuclear@14: free(packed); nuclear@14: self->mError = CTM_OUT_OF_MEMORY; nuclear@14: return CTM_FALSE; nuclear@14: } nuclear@14: nuclear@14: // Uncompress nuclear@14: unpackedSize = aCount * aSize * 4; nuclear@14: lzmaRes = LzmaUncompress(tmp, &unpackedSize, packed, nuclear@14: &packedSize, props, 5); nuclear@14: nuclear@14: // Free the packed array nuclear@14: free(packed); nuclear@14: nuclear@14: // Error? nuclear@14: if((lzmaRes != SZ_OK) || (unpackedSize != aCount * aSize * 4)) nuclear@14: { nuclear@14: self->mError = CTM_LZMA_ERROR; nuclear@14: free(tmp); nuclear@14: return CTM_FALSE; nuclear@14: } nuclear@14: nuclear@14: // Convert interleaved array to integers nuclear@14: for(i = 0; i < aCount; ++ i) nuclear@14: { nuclear@14: for(k = 0; k < aSize; ++ k) nuclear@14: { nuclear@14: value = (CTMint) tmp[i + k * aCount + 3 * aCount * aSize] | nuclear@14: (((CTMint) tmp[i + k * aCount + 2 * aCount * aSize]) << 8) | nuclear@14: (((CTMint) tmp[i + k * aCount + aCount * aSize]) << 16) | nuclear@14: (((CTMint) tmp[i + k * aCount]) << 24); nuclear@14: // Convert signed magnitude to two's complement? nuclear@14: if(aSignedInts) nuclear@14: { nuclear@14: x = (CTMuint) value; nuclear@14: value = (x & 1) ? -(CTMint)((x + 1) >> 1) : (CTMint)(x >> 1); nuclear@14: } nuclear@14: aData[i * aSize + k] = value; nuclear@14: } nuclear@14: } nuclear@14: nuclear@14: // Free the interleaved array nuclear@14: free(tmp); nuclear@14: nuclear@14: return CTM_TRUE; nuclear@14: } nuclear@14: nuclear@14: //----------------------------------------------------------------------------- nuclear@14: // _ctmStreamWritePackedInts() - Compress a binary integer data array, and nuclear@14: // write it to a stream. nuclear@14: //----------------------------------------------------------------------------- nuclear@14: int _ctmStreamWritePackedInts(_CTMcontext * self, CTMint * aData, nuclear@14: CTMuint aCount, CTMuint aSize, CTMint aSignedInts) nuclear@14: { nuclear@14: int lzmaRes, lzmaAlgo; nuclear@14: CTMuint i, k; nuclear@14: CTMint value; nuclear@14: size_t bufSize, outPropsSize; nuclear@14: unsigned char * packed, outProps[5], *tmp; nuclear@14: #ifdef __DEBUG_ nuclear@14: CTMuint negCount = 0; nuclear@14: #endif nuclear@14: nuclear@14: // Allocate memory for interleaved array nuclear@14: tmp = (unsigned char *) malloc(aCount * aSize * 4); nuclear@14: if(!tmp) nuclear@14: { nuclear@14: self->mError = CTM_OUT_OF_MEMORY; nuclear@14: return CTM_FALSE; nuclear@14: } nuclear@14: nuclear@14: // Convert integers to an interleaved array nuclear@14: for(i = 0; i < aCount; ++ i) nuclear@14: { nuclear@14: for(k = 0; k < aSize; ++ k) nuclear@14: { nuclear@14: value = aData[i * aSize + k]; nuclear@14: // Convert two's complement to signed magnitude? nuclear@14: if(aSignedInts) nuclear@14: value = value < 0 ? -1 - (value << 1) : value << 1; nuclear@14: #ifdef __DEBUG_ nuclear@14: else if(value < 0) nuclear@14: ++ negCount; nuclear@14: #endif nuclear@14: tmp[i + k * aCount + 3 * aCount * aSize] = value & 0x000000ff; nuclear@14: tmp[i + k * aCount + 2 * aCount * aSize] = (value >> 8) & 0x000000ff; nuclear@14: tmp[i + k * aCount + aCount * aSize] = (value >> 16) & 0x000000ff; nuclear@14: tmp[i + k * aCount] = (value >> 24) & 0x000000ff; nuclear@14: } nuclear@14: } nuclear@14: nuclear@14: // Allocate memory for the packed data nuclear@14: bufSize = 1000 + aCount * aSize * 4; nuclear@14: packed = (unsigned char *) malloc(bufSize); nuclear@14: if(!packed) nuclear@14: { nuclear@14: free(tmp); nuclear@14: self->mError = CTM_OUT_OF_MEMORY; nuclear@14: return CTM_FALSE; nuclear@14: } nuclear@14: nuclear@14: // Call LZMA to compress nuclear@14: outPropsSize = 5; nuclear@14: lzmaAlgo = (self->mCompressionLevel < 1 ? 0 : 1); nuclear@14: lzmaRes = LzmaCompress(packed, nuclear@14: &bufSize, nuclear@14: (const unsigned char *) tmp, nuclear@14: aCount * aSize * 4, nuclear@14: outProps, nuclear@14: &outPropsSize, nuclear@14: self->mCompressionLevel, // Level (0-9) nuclear@14: 0, -1, -1, -1, -1, -1, // Default values (set by level) nuclear@14: lzmaAlgo // Algorithm (0 = fast, 1 = normal) nuclear@14: ); nuclear@14: nuclear@14: // Free temporary array nuclear@14: free(tmp); nuclear@14: nuclear@14: // Error? nuclear@14: if(lzmaRes != SZ_OK) nuclear@14: { nuclear@14: self->mError = CTM_LZMA_ERROR; nuclear@14: free(packed); nuclear@14: return CTM_FALSE; nuclear@14: } nuclear@14: nuclear@14: #ifdef __DEBUG_ nuclear@14: printf("%d->%d bytes (%d negative words)\n", aCount * aSize * 4, (int) bufSize, negCount); nuclear@14: #endif nuclear@14: nuclear@14: // Write packed data size to the stream nuclear@14: _ctmStreamWriteUINT(self, (CTMuint) bufSize); nuclear@14: nuclear@14: // Write LZMA compression props to the stream nuclear@14: _ctmStreamWrite(self, (void *) outProps, 5); nuclear@14: nuclear@14: // Write the packed data to the stream nuclear@14: _ctmStreamWrite(self, (void *) packed, (CTMuint) bufSize); nuclear@14: nuclear@14: // Free the packed data nuclear@14: free(packed); nuclear@14: nuclear@14: return CTM_TRUE; nuclear@14: } nuclear@14: nuclear@14: //----------------------------------------------------------------------------- nuclear@14: // _ctmStreamReadPackedFloats() - Read an compressed binary float data array nuclear@14: // from a stream, and uncompress it. nuclear@14: //----------------------------------------------------------------------------- nuclear@14: int _ctmStreamReadPackedFloats(_CTMcontext * self, CTMfloat * aData, nuclear@14: CTMuint aCount, CTMuint aSize) nuclear@14: { nuclear@14: CTMuint i, k; nuclear@14: size_t packedSize, unpackedSize; nuclear@14: union { nuclear@14: CTMfloat f; nuclear@14: CTMint i; nuclear@14: } value; nuclear@14: unsigned char * packed, * tmp; nuclear@14: unsigned char props[5]; nuclear@14: int lzmaRes; nuclear@14: nuclear@14: // Read packed data size from the stream nuclear@14: packedSize = (size_t) _ctmStreamReadUINT(self); nuclear@14: nuclear@14: // Read LZMA compression props from the stream nuclear@14: _ctmStreamRead(self, (void *) props, 5); nuclear@14: nuclear@14: // Allocate memory and read the packed data from the stream nuclear@14: packed = (unsigned char *) malloc(packedSize); nuclear@14: if(!packed) nuclear@14: { nuclear@14: self->mError = CTM_OUT_OF_MEMORY; nuclear@14: return CTM_FALSE; nuclear@14: } nuclear@14: _ctmStreamRead(self, (void *) packed, packedSize); nuclear@14: nuclear@14: // Allocate memory for interleaved array nuclear@14: tmp = (unsigned char *) malloc(aCount * aSize * 4); nuclear@14: if(!tmp) nuclear@14: { nuclear@14: free(packed); nuclear@14: self->mError = CTM_OUT_OF_MEMORY; nuclear@14: return CTM_FALSE; nuclear@14: } nuclear@14: nuclear@14: // Uncompress nuclear@14: unpackedSize = aCount * aSize * 4; nuclear@14: lzmaRes = LzmaUncompress(tmp, &unpackedSize, packed, nuclear@14: &packedSize, props, 5); nuclear@14: nuclear@14: // Free the packed array nuclear@14: free(packed); nuclear@14: nuclear@14: // Error? nuclear@14: if((lzmaRes != SZ_OK) || (unpackedSize != aCount * aSize * 4)) nuclear@14: { nuclear@14: self->mError = CTM_LZMA_ERROR; nuclear@14: free(tmp); nuclear@14: return CTM_FALSE; nuclear@14: } nuclear@14: nuclear@14: // Convert interleaved array to floats nuclear@14: for(i = 0; i < aCount; ++ i) nuclear@14: { nuclear@14: for(k = 0; k < aSize; ++ k) nuclear@14: { nuclear@14: value.i = (CTMint) tmp[i + k * aCount + 3 * aCount * aSize] | nuclear@14: (((CTMint) tmp[i + k * aCount + 2 * aCount * aSize]) << 8) | nuclear@14: (((CTMint) tmp[i + k * aCount + aCount * aSize]) << 16) | nuclear@14: (((CTMint) tmp[i + k * aCount]) << 24); nuclear@14: aData[i * aSize + k] = value.f; nuclear@14: } nuclear@14: } nuclear@14: nuclear@14: // Free the interleaved array nuclear@14: free(tmp); nuclear@14: nuclear@14: return CTM_TRUE; nuclear@14: } nuclear@14: nuclear@14: //----------------------------------------------------------------------------- nuclear@14: // _ctmStreamWritePackedFloats() - Compress a binary float data array, and nuclear@14: // write it to a stream. nuclear@14: //----------------------------------------------------------------------------- nuclear@14: int _ctmStreamWritePackedFloats(_CTMcontext * self, CTMfloat * aData, nuclear@14: CTMuint aCount, CTMuint aSize) nuclear@14: { nuclear@14: int lzmaRes, lzmaAlgo; nuclear@14: CTMuint i, k; nuclear@14: union { nuclear@14: CTMfloat f; nuclear@14: CTMint i; nuclear@14: } value; nuclear@14: size_t bufSize, outPropsSize; nuclear@14: unsigned char * packed, outProps[5], *tmp; nuclear@14: nuclear@14: // Allocate memory for interleaved array nuclear@14: tmp = (unsigned char *) malloc(aCount * aSize * 4); nuclear@14: if(!tmp) nuclear@14: { nuclear@14: self->mError = CTM_OUT_OF_MEMORY; nuclear@14: return CTM_FALSE; nuclear@14: } nuclear@14: nuclear@14: // Convert floats to an interleaved array nuclear@14: for(i = 0; i < aCount; ++ i) nuclear@14: { nuclear@14: for(k = 0; k < aSize; ++ k) nuclear@14: { nuclear@14: value.f = aData[i * aSize + k]; nuclear@14: tmp[i + k * aCount + 3 * aCount * aSize] = value.i & 0x000000ff; nuclear@14: tmp[i + k * aCount + 2 * aCount * aSize] = (value.i >> 8) & 0x000000ff; nuclear@14: tmp[i + k * aCount + aCount * aSize] = (value.i >> 16) & 0x000000ff; nuclear@14: tmp[i + k * aCount] = (value.i >> 24) & 0x000000ff; nuclear@14: } nuclear@14: } nuclear@14: nuclear@14: // Allocate memory for the packed data nuclear@14: bufSize = 1000 + aCount * aSize * 4; nuclear@14: packed = (unsigned char *) malloc(bufSize); nuclear@14: if(!packed) nuclear@14: { nuclear@14: free(tmp); nuclear@14: self->mError = CTM_OUT_OF_MEMORY; nuclear@14: return CTM_FALSE; nuclear@14: } nuclear@14: nuclear@14: // Call LZMA to compress nuclear@14: outPropsSize = 5; nuclear@14: lzmaAlgo = (self->mCompressionLevel < 1 ? 0 : 1); nuclear@14: lzmaRes = LzmaCompress(packed, nuclear@14: &bufSize, nuclear@14: (const unsigned char *) tmp, nuclear@14: aCount * aSize * 4, nuclear@14: outProps, nuclear@14: &outPropsSize, nuclear@14: self->mCompressionLevel, // Level (0-9) nuclear@14: 0, -1, -1, -1, -1, -1, // Default values (set by level) nuclear@14: lzmaAlgo // Algorithm (0 = fast, 1 = normal) nuclear@14: ); nuclear@14: nuclear@14: // Free temporary array nuclear@14: free(tmp); nuclear@14: nuclear@14: // Error? nuclear@14: if(lzmaRes != SZ_OK) nuclear@14: { nuclear@14: self->mError = CTM_LZMA_ERROR; nuclear@14: free(packed); nuclear@14: return CTM_FALSE; nuclear@14: } nuclear@14: nuclear@14: #ifdef __DEBUG_ nuclear@14: printf("%d->%d bytes\n", aCount * aSize * 4, (int) bufSize); nuclear@14: #endif nuclear@14: nuclear@14: // Write packed data size to the stream nuclear@14: _ctmStreamWriteUINT(self, (CTMuint) bufSize); nuclear@14: nuclear@14: // Write LZMA compression props to the stream nuclear@14: _ctmStreamWrite(self, (void *) outProps, 5); nuclear@14: nuclear@14: // Write the packed data to the stream nuclear@14: _ctmStreamWrite(self, (void *) packed, (CTMuint) bufSize); nuclear@14: nuclear@14: // Free the packed data nuclear@14: free(packed); nuclear@14: nuclear@14: return CTM_TRUE; nuclear@14: }