2012-06-07 17:19:29 +00:00
//
// KRDataBlock.cpp
// KREngine
//
2012-09-11 03:06:35 +00:00
// Copyright 2012 Kearwood Gilbert. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The views and conclusions contained in the software and documentation are those of the
// authors and should not be interpreted as representing official policies, either expressed
// or implied, of Kearwood Gilbert.
2012-06-07 17:19:29 +00:00
//
# include "KRDataBlock.h"
KRDataBlock : : KRDataBlock ( ) {
m_data = NULL ;
m_data_size = 0 ;
2012-09-11 03:06:35 +00:00
m_fdPackFile = 0 ;
m_bMalloced = false ;
2012-06-07 17:19:29 +00:00
}
KRDataBlock : : ~ KRDataBlock ( ) {
2012-09-11 03:06:35 +00:00
unload ( ) ;
}
// Unload a file, releasing any mmap'ed file handles or malloc'ed ram that was in use
void KRDataBlock : : unload ( )
{
if ( m_fdPackFile ) {
// Memory mapped file
2012-09-11 06:45:02 +00:00
munmap ( m_data , m_data_size ) ;
2012-09-11 03:06:35 +00:00
close ( m_fdPackFile ) ;
} else if ( m_data ! = NULL & & m_bMalloced ) {
// Malloc'ed data
2012-06-07 17:19:29 +00:00
free ( m_data ) ;
}
2012-09-11 03:06:35 +00:00
m_bMalloced = false ;
m_data = NULL ;
m_data_size = 0 ;
m_fdPackFile = 0 ;
}
// Encapsulate a pointer. Note - The pointer will not be free'ed
bool KRDataBlock : : load ( void * data , size_t size )
{
unload ( ) ;
m_data = data ;
m_data_size = size ;
return true ;
2012-06-07 17:19:29 +00:00
}
2012-09-11 03:06:35 +00:00
// Load a file into memory using mmap. The data pointer will be protected as read-only until append() or expand() is called
bool KRDataBlock : : load ( const std : : string & path )
{
bool success = false ;
unload ( ) ;
struct stat statbuf ;
m_fdPackFile = open ( path . c_str ( ) , O_RDONLY ) ;
if ( m_fdPackFile > = 0 ) {
if ( fstat ( m_fdPackFile , & statbuf ) > = 0 ) {
if ( ( m_data = mmap ( 0 , statbuf . st_size , PROT_READ , MAP_SHARED , m_fdPackFile , 0 ) ) = = ( caddr_t ) - 1 ) {
} else {
m_data_size = statbuf . st_size ;
success = true ;
}
}
}
if ( ! success ) {
// If anything failed, don't leave the object in an invalid state
unload ( ) ;
}
return success ;
}
// Return a pointer to the start of the data block
void * KRDataBlock : : getStart ( ) {
2012-06-07 17:19:29 +00:00
return m_data ;
}
2012-09-11 03:06:35 +00:00
// Return a pointer to the byte after the end of the data block
void * KRDataBlock : : getEnd ( ) {
return ( unsigned char * ) m_data + m_data_size ;
}
// Return the size of the data block. Use append() or expand() to make the data block larger
size_t KRDataBlock : : getSize ( ) const {
2012-06-07 17:19:29 +00:00
return m_data_size ;
}
2012-09-11 03:06:35 +00:00
// Expand the data block, and switch it to read-write mode. Note - this may result in a mmap'ed file being copied to malloc'ed ram and then closed
void KRDataBlock : : expand ( size_t size )
{
2012-06-07 17:19:29 +00:00
if ( m_data = = NULL ) {
2012-09-11 03:06:35 +00:00
// Starting with an empty data block; allocate memory on the heap
2012-06-07 17:19:29 +00:00
m_data = malloc ( size ) ;
2012-09-20 09:32:20 +00:00
assert ( m_data ! = NULL ) ;
2012-06-07 17:19:29 +00:00
m_data_size = size ;
2012-09-11 03:06:35 +00:00
m_bMalloced = true ;
} else if ( m_bMalloced ) {
// Starting with a malloc'ed data block; realloc it expand
2012-06-07 17:19:29 +00:00
m_data = realloc ( m_data , m_data_size + size ) ;
m_data_size + = size ;
2012-09-11 03:06:35 +00:00
} else {
// Starting with a mmap'ed data block; copy it to ram before expanding to avoid updating the original file until save() is called
// ... Or starting with a pointer reference, we must make our own copy and must not free the pointer
void * pNewData = malloc ( m_data_size + size ) ;
2012-09-20 09:32:20 +00:00
assert ( pNewData ! = NULL ) ;
2012-09-11 03:06:35 +00:00
memcpy ( ( unsigned char * ) pNewData , m_data , m_data_size ) ; // Copy exising data
// Unload existing data allocation, which is now redundant
size_t new_size = m_data_size + size ; // We need to store this before unload() as unload() will reset it
unload ( ) ;
m_bMalloced = true ;
m_data = pNewData ;
m_data_size = new_size ;
2012-06-07 17:19:29 +00:00
}
2012-09-11 03:06:35 +00:00
}
// Append data to the end of the block, increasing the size of the block and making it read-write.
void KRDataBlock : : append ( void * data , size_t size ) {
// Expand the data block
expand ( size ) ;
// Fill the new space with the data to append
memcpy ( ( unsigned char * ) m_data + m_data_size - size , data , size ) ;
}
2012-12-20 01:23:57 +00:00
// Append data to the end of the block, increasing the size of the block and making it read-write.
void KRDataBlock : : append ( KRDataBlock & data ) {
append ( data . getStart ( ) , data . getSize ( ) ) ;
}
// Append string to the end of the block, increasing the size of the block and making it read-write. The null terminating character is included
void KRDataBlock : : append ( const std : : string & s )
{
const char * szText = s . c_str ( ) ;
append ( ( void * ) szText , strlen ( szText ) + 1 ) ;
}
2012-09-11 03:06:35 +00:00
// Save the data to a file, and switch to read-only mode. The data pointer will be replaced with a mmap'ed address of the file; the malloc'ed data will be freed
bool KRDataBlock : : save ( const std : : string & path ) {
int fdNewFile = open ( path . c_str ( ) , O_RDWR | O_CREAT | O_TRUNC , ( mode_t ) 0600 ) ;
if ( fdNewFile = = - 1 ) {
return false ;
} else {
// Seek to end of file and write a byte to enlarge it
lseek ( fdNewFile , m_data_size - 1 , SEEK_SET ) ;
write ( fdNewFile , " " , 1 ) ;
// Now map it...
void * pNewData = mmap ( 0 , m_data_size , PROT_READ | PROT_WRITE , MAP_SHARED , fdNewFile , 0 ) ;
if ( pNewData = = ( caddr_t ) - 1 ) {
close ( fdNewFile ) ;
return false ;
} else if ( m_data ! = NULL ) {
// Copy data to new file
memcpy ( pNewData , m_data , m_data_size ) ;
// Unload existing data allocation, which is now redundant
size_t new_size = m_data_size ; // We need to store this, as unload() will reset it
unload ( ) ;
// Protect new mmap'ed memory
mprotect ( pNewData , m_data_size , PROT_READ ) ;
// Switch pointer to use new mmap'ed memory
m_data_size = new_size ;
m_fdPackFile = fdNewFile ;
m_data = pNewData ;
}
return true ;
}
}
2013-01-04 20:31:39 +00:00
// Get contents as a string
std : : string KRDataBlock : : getString ( )
{
KRDataBlock b ;
b . append ( * this ) ;
b . append ( ( void * ) " \0 " , 1 ) ; // Ensure data is null terminated, to read as a string safely
return std : : string ( ( char * ) b . getStart ( ) ) ;
}