Inserting data in the middle of a file

Posted By: EvilSOB

Inserting data in the middle of a file - 02/14/09 15:44

ANNOUNCEMENT - CODE HAS BEEN RE-IMAGINED
Due to new developments/discoveries, this code has been seriously overhauled.

Newer FASTER code is now in place, almost all functions usage/parameters have
remained the same and two new functions have been added.
Performance/response times have now also been provided.


Of late, there had been a discussion smouldering in the Future forum about our ability to insert text into the middle
of a file. Some people are after this functionality to be added to the engine, but JCL makes it seem unlikely due to
complexity and lack of nececessity and/or interest.

FEAR NO MORE !
I have spent the last week coding the following include to give everyone this missing functionality.

Have a quick scroll through and you'll see theres plenty of documentation in there. It just looks a bit mis-aligned
through the forum display, but just cut and paste into SED and everything will line up neatly.

Please feel free to let me know of any bugs or problems, and suggestions are welcome.
Thank you all.

Code:
#ifndef File_Open_RW_h
#define File_Open_RW_h
//
/////////////////////////////////////////////////////////////////////////////////////////
//
//		FILE_OPEN_RW.H
//
//		Library of 3DGS-like functiona filling some gaps in updating data MID-file.
//
/////////////////////////////////////////////////////////////////////////////////////////
//
//		Largely equivalent to standard file_open_write() and file_open_append() 
//		functionality but adds the EXTRA ability to insert STRING, VAR, ASC or buffer data 
//		at mid-file positions, and to read buffer data blocks from mid-file, and to delete
//		specific blocks of data from mid-file.
//
/////////////////////////////////////////////////////////////////////////////////////////
//
//		Author	:	EvilSOB
//		Updated	: 	18-02-2009
//
//		Requirements:	Acknex.h
//
//
//		Notes:
//
/////////////////////////////////////////////////////////////////////////////////////////
//
//		FUNCTIONS
//
//=======================================================================================
//		file_open_RW(STRING* name);
//---------------------------------------------------------------------------------------
//		Opens a file for reading and writing additional content at any point. 
//		If the file does not exist, it is created.
//		The function returns a file handle - that is a unique number to identify the file. 
//		The file handle is used by other functions to access that file. 
//---------------------------------------------------------------------------------------
//		Parameters:
//			name	- file name with or without path, STRING* or char*. 
//---------------------------------------------------------------------------------------
//		Returns:
//			File handle(type VAR), or NULL if the file could not be opened. 
//
//
//
//
//=======================================================================================
//		file_close_RW(var handle);
//---------------------------------------------------------------------------------------
//		Closes the file with the given handle. A file must be closed before other programs
//			can access it, or before it can be opened again in a different mode. As soon as
//			the file is closed, the handle becomes invalid.
//---------------------------------------------------------------------------------------
//		Parameters:
//			handle	- File handle of the closing file.
//---------------------------------------------------------------------------------------
//		NOTE: 
//			Using lite-c file_close() on a file_open_RW() file works fine but wastes a very
//				small amount of memory. (up to 500 bytes, no more)
//
//
//
//
//=======================================================================================
//		file_void_insert(var handle, void* buffer, var size, var offset, var mode)
//---------------------------------------------------------------------------------------
//		Writes the given buffer into the file with the given handle. The file must be have
//			been opened with file_open_RW(). 
//		After data-insertion, the read pointer is placed at the end of the inserted data.
//---------------------------------------------------------------------------------------
//		Parameters:
//			handle	- File handle 
//			buffer	- Void Pointer to a preallocated buffer.
//			size 		- The length of the block to be written.
//			offset	- Write position in bytes from the position given by the mode parameter
//			mode		- 0 = from beginning of the file
//						  1 = from the current read position
//						  2 = from the end of the file. 
//---------------------------------------------------------------------------------------
//		Returns:
//			True	- file update was sucessful.
//			False	- file update failed. Due to invalid handle.
//			False	- file update failed. Due to file not opened with file_open_RW.
//			False	- file update failed. Due to calculated offset outside file bounds.
//			False	- file update failed. Due to invalid buffer pointer.
//			False	- file update failed. Due to invalid buffer length.
//
//
//
//
//=======================================================================================
//		file_str_insert(var handle, STRING* text, var offset, var mode)
//---------------------------------------------------------------------------------------
//		Writes the given string into the file with the given handle. The file must be have
//			been opened with file_open_RW(). The string is unmodified, i.e. special 
//			sequences like '\n' are not converted. 
//		After data-insertion, the read pointer is placed at the end of the inserted data.
//---------------------------------------------------------------------------------------
//		Parameters:
//			handle	- File handle 
//			text		- Character string to be written into the file 
//			offset	- Write position in bytes from the position given by the mode parameter
//			mode		- 0 = from beginning of the file
//						  1 = from the current read position
//						  2 = from the end of the file. 
//---------------------------------------------------------------------------------------
//		Returns:
//			True	- file update was sucessful.
//			False	- file update failed. Due to invalid handle.
//			False	- file update failed. Due to file not opened with file_open_RW.
//			False	- file update failed. Due to calculated offset outside file bounds.
//
//
//
//
//=======================================================================================
//		file_var_insert(var handle, var number, var offset, var mode)
//---------------------------------------------------------------------------------------
//		Writes the given number or variable into the file with the given handle. The file
//			must have been opened with file_open_RW(). If the number has a decimal between
//			.001 and .999, the number is then written with 3 decimals, ending by a blank.
//			Otherwise the integer is written with no decimals, again ending by a blank.
//		After data-insertion, the read pointer is placed at the end of the inserted data.
//---------------------------------------------------------------------------------------
//		Parameters:
//			handle	- File handle 
//			number	- Value to be written into the file as a string 
//			offset	- Write position in bytes from the position given by the mode parameter
//			mode		- 0 = from beginning of the file
//						  1 = from the current read position
//						  2 = from the end of the file. 
//---------------------------------------------------------------------------------------
//		Returns:
//			True	- file update was sucessful.
//			False	- file update failed. Due to invalid handle.
//			False	- file update failed. Due to file not opened with file_open_RW.
//			False	- file update failed. Due to calculated offset outside file bounds.
//
//
//
//
//=======================================================================================
//		file_asc_insert(var handle, var number, var offset, var mode)
//---------------------------------------------------------------------------------------
//		Writes a single byte given by the number or variable (0..255) into the file with 
//		the given handle. The file must have been opened with file_open_RW(). 
//		After data-insertion, the read pointer is placed at the end of the inserted data.
//---------------------------------------------------------------------------------------
//		Parameters:
//			handle	- File handle 
//			number	- Number between 0 and 255.
//			offset	- Write position in bytes from the position given by the mode parameter
//			mode		- 0 = from beginning of the file
//						  1 = from the current read position
//						  2 = from the end of the file. 
//---------------------------------------------------------------------------------------
//		Returns:
//			True	- file update was sucessful.
//			False	- file update failed. Due to invalid handle.
//			False	- file update failed. Due to file not opened with file_open_RW.
//			False	- file update failed. Due to calculated offset outside file bounds.
//
//
//
//
//=======================================================================================
//		file_bytes_remove(var handle, var cut_qty, var offset, var mode)
//---------------------------------------------------------------------------------------
//		Removes cut_qty number of bytes from the the file with the given handle. The file 
//			must have been opened with file_open_RW(). 
//		After data-removal, the read pointer is placed at the position of the removed data.
//---------------------------------------------------------------------------------------
//		Parameters:
//			handle	- File handle 
//			cut_qty	- Quantity of bytes to be "cut" from the middle of the file.
//			offset	- Cut position in bytes from the position given by the mode parameter
//			mode		- 0 = from beginning of the file
//						  1 = from the current read position
//						  2 = from the end of the file. 
//---------------------------------------------------------------------------------------
//		Returns:
//			True	- file update was sucessful.
//			False	- file update failed. Due to invalid handle.
//			False	- file update failed. Due to file not opened with file_open_RW.
//			False	- file update failed. Due to calculated offset outside file bounds.
//
//
//
//
//=======================================================================================
//		void* file_void_read(var handle, var read_qty, var offset, var mode)
//---------------------------------------------------------------------------------------
//		Reads read_qty number of bytes from the the file with the given handle. The file 
//			must have been opened with file_open_RW(). 
//		After data-retrieval, the read pointer is placed at the end of the read data.
//---------------------------------------------------------------------------------------
//		Parameters:
//			handle	- File handle 
//			read_qty	- Quantity of bytes to be read from the middle of the file.
//			offset	- Cut position in bytes from the position given by the mode parameter
//			mode		- 0 = from beginning of the file
//						  1 = from the current read position
//						  2 = from the end of the file. 
//---------------------------------------------------------------------------------------
//		Returns:
//			VOID*	- Void pointer to the retrieved data. Should be released using free(...);
//			NULL	- file update failed. Due to invalid handle.
//			NULL	- file update failed. Due to file not opened with file_open_RW.
//			NULL	- file update failed. Due to calculated offset outside file bounds.
//
//
//
//
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
//
//
#define Max_RW_files	25		//MAXIMUM file_insert files allowed open AT ANY ONE TIME
STRING*	RW_filename[Max_RW_files];
var 		RW_file[Max_RW_files];
//
function file_open_RW_startup()		//initialise empty File tracking table
{	var f; for(f=0; f<Max_RW_files; f++)	RW_filename[f] = RW_file[f] = NULL;	}
//
//
/////////////////////////////////////////////////////////////////////////////////////////
//
//
function file_open_RW(STRING* filename)		//open file for read/write capability
{	var f; for(f=0; f<Max_RW_files; f++)			//look for available slot
	{	if(RW_file[f]==NULL)									//in tracking table
		{	RW_filename[f] = str_create(_chr(filename));			//create entry 
			RW_file[f] 	 = file_open_read(filename);				//in vacant
			break;			}		}												//slot
	if(f==Max_RW_files)	return(NULL);	//no space in table to open new file
	return(RW_file[f]);			//return pointer to new insert-capable file
}
//
/////////////////////////////////////////////////////////////////////////////////////////
//
function file_close_RW(var *file)			//close insert-capable entry in tracking table
{	if(*file==NULL)	return(false);	//not a valid file pointer
	var f; for(f=0; f<Max_RW_files; f++)	//look for table entry 
	{	if(RW_file[f]==*file)	break;		}		//that matches pointer
	if(f<Max_RW_files)			//clear table entry
	{	RW_file[f] = NULL;				//for re-use 
		str_remove(RW_filename[f]);	//and release memory 
		RW_filename[f] = NULL;			}	//used by string
	file_close(*file);		//close file whether it was in the table or not.
	return(true);
}
//
/////////////////////////////////////////////////////////////////////////////////////////
//
function file_void_insert(var *file, void* block, var block_len, var offset, var mode)
{	if(*file==NULL)	return(false);	//not a valid file pointer
	if(block==NULL)		return(false);	//not a valid block pointer
	if(block_len<00)	return(false);	//not a valid block length
	var f; for(f=0; f<Max_RW_files; f++)		//look for table entry
	{	if(RW_file[f]==*file)	break;		}			//that matches pointer
	if(f==Max_RW_files)	return(false);	//not an RW-capable file
	if(mode == 3)
		offset += file_length(RW_file[f]);				//offset relates to end of file
	else if(mode == 1)
		offset += file_seek(RW_file[f], NULL, 4);		//offset relates to current position
	else	//mode==0
		offset += 0;											//offset relates to beginning of file
	int bytepos = offset;	//define position that changes take place at
	if(bytepos<0)									return(false);		//invalid start position
	if(bytepos>file_length(RW_file[f]))		return(false);		//invalid start position
	file_close(RW_file[f]);		//close original file
	long fsize = 0;	long block_size = block_len;	//define file and block length storage
	long buffer0 = (void*)file_load(_chr(RW_filename[f]), NULL, &fsize);	//read whole file
	long buffer1 = (void*)malloc(fsize + block_size);	//create new file workspace
	memcpy( buffer1 , buffer0, bytepos);	//copy across pre-insert data
	memcpy( buffer1+bytepos , block, block_size);	//copy across insert data
	memcpy( buffer1+bytepos+block_size , buffer0+bytepos, fsize-bytepos); //copy remaining
   file_save(RW_filename[f], (void*)buffer1, fsize + block_len);	//overwite old file
	file_load(NULL, (void*)buffer0, NULL);		free(buffer1);		//free file buffers
	RW_file[f] = file_open_read(RW_filename[f]);	//re-open it as RW-capable
	file_seek(RW_file[f], bytepos, 0);		//re-position read position to after insert
	*file = RW_file[f];		//reset pointer to file to calling function
	return(true);	//return successful status	
}
//
/////////////////////////////////////////////////////////////////////////////////////////
//
function file_str_insert(var *file, STRING* text, var offset, var mode)
{	if(*file==NULL)	return(false);	//not a valid file pointer
	if(text==NULL)		return(false);	//not a valid STRING pointer
	var Results = file_void_insert(*file, text.chars, str_len(text)+1, offset, mode);
	return(Results);		//inform caller of sucess/failure
}
//
/////////////////////////////////////////////////////////////////////////////////////////
//
function file_asc_insert(var *file, var num, var offset, var mode)
{	if(*file==NULL)	return(false);	//not a valid file pointer
	STRING* tmpStr = " ";		//temp space for to-be-written data
	str_for_asc(tmpStr, num);		//convert ASC value into CHAR character for writing
	var Results = file_void_insert(*file, tmpStr.chars, 1, offset, mode);	//write char
	str_remove(tmpStr);			//remove/free temp spaces from has-be-written data
	return(Results);		//inform caller of sucess/failure
}
//
/////////////////////////////////////////////////////////////////////////////////////////
//
function file_var_insert(var *file, var num, var offset, var mode)
{	if(*file==NULL)	return(false);	//not a valid file pointer
	STRING* tmpStr = ".............";		//temp space for to-be-written data
	str_for_num(tmpStr, num);		//convert number value into STRING for writing
	var Results = file_void_insert(*file, tmpStr.chars, tmpStr.length, offset, mode);
	str_remove(tmpStr);			//remove/free temp spaces from has-be-written data
	return(Results);		//inform caller of sucess/failure
}
//
/////////////////////////////////////////////////////////////////////////////////////////
//
function file_bytes_remove(var *file, var cut_qty, var offset, var mode)
{	if(*file==NULL)	return(false);	//not a valid file pointer
	var f; for(f=0; f<Max_RW_files; f++)		//look for table entry
	{	if(RW_file[f]==*file)	break;		}			//that matches pointer
	if(f==Max_RW_files)	return(false);	//not an RW-capable file
	if(mode == 3)
		offset += file_length(RW_file[f]);				//offset relates to end of file
	else if(mode == 1)
		offset += file_seek(RW_file[f], NULL, 4);		//offset relates to current position
	else	//mode==0
		offset += 0;											//offset relates to beginning of file
	long bytepos = offset;	//define position that changes take place at
	if(bytepos<0)									return(false);		//invalid start position
	if(bytepos>=file_length(RW_file[f]))	return(false);		//invalid start position
	if((bytepos+cut_qty)>file_length(RW_file[f]))		//reduce overshooting
		cut_qty = file_length(RW_file[f]) - bytepos;		//of cut_qty
	file_close(RW_file[f]);		//close original file
	long fsize = 0;	long cut_size = cut_qty;	//define file size and cut size variables
	long buffer0 = (void*)file_load(_chr(RW_filename[f]), NULL, &fsize);	//read whole file
	memcpy( buffer0+bytepos , buffer0+bytepos+cut_size, fsize-bytepos);  //copy remaining
   file_save(RW_filename[f], (void*)buffer0, fsize - cut_size);	//overwite old file
	file_load(NULL, (void*)buffer0, NULL);		//free file buffer
	RW_file[f] = file_open_read(RW_filename[f]);	//re-open it as RW-capable
	file_seek(RW_file[f], bytepos, 0);		//re-position read position to after deletion
	*file = RW_file[f];		//reset pointer to file to calling function
	return(true);	//return successful status	
}
//
/////////////////////////////////////////////////////////////////////////////////////////
//
void* file_void_read(var *file, var read_qty, var offset, var mode)
{	if(*file==NULL)	return((void*)NULL);	//not a valid file pointer
	if(read_qty<=0)	return((void*)NULL);	//not a valid block length
	var f; for(f=0; f<Max_RW_files; f++)		//look for table entry
	{	if(RW_file[f]==*file)	break;		}			//that matches pointer
	if(f==Max_RW_files)	return((void*)NULL);	//not an RW-capable file
	if(mode == 3)
		offset += file_length(RW_file[f]);				//offset relates to end of file
	else if(mode == 1)
		offset += file_seek(RW_file[f], NULL, 4);		//offset relates to current position
	else	//mode==0
		offset += 0;											//offset relates to beginning of file
	long bytepos = offset;	//define position that read takes place at
	if(bytepos<0)									return((void*)NULL);   //invalid start position
	if(bytepos>=file_length(RW_file[f]))	return((void*)NULL);   //invalid start position
	long fsize = 0;	long read_size = read_qty;	//define file size and read sizes
	long buffer0 = (void*)file_load(_chr(RW_filename[f]), NULL, &fsize);	//read whole file
	long buffer1 = (void*)malloc(read_size);	//create out-block workspace
	memcpy( buffer1, buffer0+bytepos, read_size);  //copy required data
	file_load(NULL, (void*)buffer0, NULL);		//free file-read buffer
	return((void*)(buffer1));	//return pointer to retrieved block	
}
//
//
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
//
#endif	// File_Open_RW_h


Here is the performance info, Ive listed the speed my old loop-based code ran at, and the speed of this code.
It show the response times on an Athalon 64x2 6000, 2gig Ram machine, with a range of file sizes.
There is only two tests per file per code, as those two listed functions do all the work.
Code:
FileSize   function tested      NEW code time        Old code time
=====================================================================================
1K       file_bytes_remove()   (new)=0.004984 secs   (old)=0.001148 secs
         file_void_insert()    (new)=0.004852 secs   (old)=0.000660 secs
-------------------------------------------------------------------------------------
5K       file_bytes_remove()   (new)=0.005016 secs   (old)=0.002321 secs
         file_void_insert()    (new)=0.004893 secs   (old)=0.001619 secs
-------------------------------------------------------------------------------------
10K      file_bytes_remove()   (new)=0.005029 secs   (old)=0.003211 secs
         file_void_insert()    (new)=0.004913 secs   (old)=0.002710 secs
-------------------------------------------------------------------------------------
50K      file_bytes_remove()   (new)=0.005135 secs   (old)=0.011177 secs
         file_void_insert()    (new)=0.005003 secs   (old)=0.011398 secs
-------------------------------------------------------------------------------------
100K     file_bytes_remove()   (new)=0.005274 secs   (old)=0.021067 secs
         file_void_insert()    (new)=0.005089 secs   (old)=0.022597 secs
-------------------------------------------------------------------------------------
500K     file_bytes_remove()   (new)=0.008007 secs   (old)=0.098530 secs
         file_void_insert()    (new)=0.008223 secs   (old)=0.108704 secs
-------------------------------------------------------------------------------------
1000K    file_bytes_remove()   (new)=0.019177 secs   (old)=0.208722 secs
(~1M)    file_void_insert()    (new)=0.020519 secs   (old)=0.236202 secs
-------------------------------------------------------------------------------------
5000K    file_bytes_remove()   (new)=0.013800 secs   (old)=0.179805 secs
(~5M)    file_void_insert()    (new)=0.012629 secs   (old)=0.197460 secs
-------------------------------------------------------------------------------------
10000K   file_bytes_remove()   (new)=0.025863 secs   (old)=0.356722 secs
(~10M)   file_void_insert()    (new)=0.023418 secs   (old)=0.393732 secs
=====================================================================================


And here is my older code, for historical reasons, in case you're curious.
If you are new, here is the older version Im comparing speeds with.
Its much slower, and less elegant, but easier to understand.
But beware, the bigger the file your working with, the slower it gets.
Click to reveal..
Code:
#ifndef File_Open_RW_h
#define File_Open_RW_h
//
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
//
//		File_Open_RW.h 
//		Library of 3DGS-like function filling some gaps in updating data MID-file
//		Equivalent to standard file_open_write() and file_open_append() functionality
//		but adds the EXTRA ability to insert STRING, VAR, or ASC data at mid-file positions
//
//		Author	:	EvilSOB
//		Written	: 	16-02-2009
//
//		Requirements:	None.
//
//		FUNCTIONS
//=======================================================================================
//		file_open_RW(STRING* name);
//---------------------------------------------------------------------------------------
//		Opens a file for reading and writing additional content at any point. 
//		If the file does not exist, it is created.
//		The function returns a file handle - that is a unique number to identify the file. 
//		The file handle is used by other functions to access that file. 
//---------------------------------------------------------------------------------------
//		Parameters:
//			name	- file name with or without path, STRING* or char*. 
//---------------------------------------------------------------------------------------
//		Returns:
//			File handle(type VAR), or 0 if the file could not be opened. 
//
//
//
//
//=======================================================================================
//		file_close_RW(var handle);
//---------------------------------------------------------------------------------------
//		Closes the file with the given handle. A file must be closed before other programs
//			can access it, or before it can be opened again in a different mode. As soon as
//			the file is closed, the handle becomes invalid.
//---------------------------------------------------------------------------------------
//		Parameters:
//			handle	- File handle of the closing file.
//---------------------------------------------------------------------------------------
//		NOTE: 
//			Using lite-c file_close() on a file_open_RW() file works fine but wastes memory.
//
//
//
//
//=======================================================================================
//		file_str_insert(var handle, STRING* data, var offset, var mode)
//---------------------------------------------------------------------------------------
//		Writes the given string into the file with the given handle. The file must be have
//			been opened with file_open_RW(). The string is unmodified, i.e. special 
//			sequences like '\n' are not converted. 
//		After data-insertion, the read pointer is placed at the end of the inserted data.
//---------------------------------------------------------------------------------------
//		Parameters:
//			handle	- File handle 
//			data		- Character string to be written into the file 
//			offset	- Write position in bytes from the position given by the mode parameter
//			mode		- 0 = from beginning of the file
//						  1 = from the current read position
//						  2 = from the end of the file. 
//---------------------------------------------------------------------------------------
//		Returns:
//			True	- file update was sucessful.
//			False	- file update failed. Due to invalid handle.
//			False	- file update failed. Due to file not opened with file_open_RW.
//
//
//
//
//=======================================================================================
//		file_var_insert(var handle, var number, var offset, var mode)
//---------------------------------------------------------------------------------------
//		Writes the given number or variable into the file with the given handle. The file
//			must have been opened with file_open_RW(). If the number has a decimal between
//			.001 and .999, the number is then written with 3 decimals, ending by a blank.
//			Otherwise the integer is written with no decimals, again ending by a blank.
//		After data-insertion, the read pointer is placed at the end of the inserted data.
//---------------------------------------------------------------------------------------
//		Parameters:
//			handle	- File handle 
//			number	- Value to be written into the file as a string 
//			offset	- Write position in bytes from the position given by the mode parameter
//			mode		- 0 = from beginning of the file
//						  1 = from the current read position
//						  2 = from the end of the file. 
//---------------------------------------------------------------------------------------
//		Returns:
//			True	- file update was sucessful.
//			False	- file update failed. Due to invalid handle.
//			False	- file update failed. Due to file not opened with file_open_RW.
//
//
//
//
//=======================================================================================
//		file_asc_insert(var handle, var number, var offset, var mode)
//---------------------------------------------------------------------------------------
//		Writes a single byte given by the number or variable (0..255) into the file with 
//		the given handle. The file must have been opened with file_open_RW(). 
//		After data-insertion, the read pointer is placed at the end of the inserted data.
//---------------------------------------------------------------------------------------
//		Parameters:
//			handle	- File handle 
//			number	- Number between 0 and 255.
//			offset	- Write position in bytes from the position given by the mode parameter
//			mode		- 0 = from beginning of the file
//						  1 = from the current read position
//						  2 = from the end of the file. 
//---------------------------------------------------------------------------------------
//		Returns:
//			True	- file update was sucessful.
//			False	- file update failed. Due to invalid handle.
//			False	- file update failed. Due to file not opened with file_open_RW.
//
//
//
//
//=======================================================================================
//		file_num_cut(var handle, var cut_qty, var offset, var mode)
//---------------------------------------------------------------------------------------
//		Removes cut_qty number of bytes from the the file with the given handle. The file 
//			must have been opened with file_open_RW(). 
//		After data-removal, the read pointer is placed at the position of the removed data.
//---------------------------------------------------------------------------------------
//		Parameters:
//			handle	- File handle 
//			cut_qtyr	- Quantity of bytes to be "cut" from the middle of the file.
//			offset	- Cut position in bytes from the position given by the mode parameter
//			mode		- 0 = from beginning of the file
//						  1 = from the current read position
//						  2 = from the end of the file. 
//---------------------------------------------------------------------------------------
//		Returns:
//			True	- file update was sucessful.
//			False	- file update failed. Due to invalid handle.
//			False	- file update failed. Due to file not opened with file_open_RW.
//
//
//
//
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
//
//
#define Max_RW_files	10		//MAXIMUM file_insert files allowed open AT ANY ONE TIME
STRING*	RW_filename[Max_RW_files];
var 		RW_file[Max_RW_files];
//
function file_open_RW_startup()		//initialise empty File tracking table
{	var f; for(f=0; f<Max_RW_files; f++)	RW_filename[f] = RW_file[f] = NULL;	}
//
//
/////////////////////////////////////////////////////////////////////////////////////////
//
//
function file_open_RW(STRING* filename)		//open file for read/write capability
{	var f; for(f=0; f<Max_RW_files; f++)			//look for available slot
	{	if(RW_file[f]==NULL)									//in tracking table
		{	RW_filename[f] = str_create(_chr(filename));			//create entry 
			RW_file[f] 	 = file_open_read(filename);				//in vacant
			break;			}		}												//slot
	if(f==Max_RW_files)	return(NULL);	//no space in table to open new file
	return(RW_file[f]);			//return pointer to new insert-capable file
}
//
/////////////////////////////////////////////////////////////////////////////////////////
//
function file_close_RW(var *file)			//close insert-capable entry in tracking table
{	if(*file==NULL)	return(false);	//not a valid file pointer
	var f; for(f=0; f<Max_RW_files; f++)	//look for table entry 
	{	if(RW_file[f]==*file)	break;		}		//that matches pointer
	if(f<Max_RW_files)			//clear table entry
	{	RW_file[f] = NULL;				//for re-use 
		str_remove(RW_filename[f]);	//and release memory 
		RW_filename[f] = NULL;			}	//used by string
	file_close(*file);		//close file whether it was in the table or not.
	return(true);
}
//
/////////////////////////////////////////////////////////////////////////////////////////
//
function file_str_insert(var *file, char* text, var offset, var mode)
{	if(*file==NULL)	return(false);	//not a valid file pointer
	var f; for(f=0; f<Max_RW_files; f++)		//look for table entry
	{	if(RW_file[f]==*file)	break;		}			//that matches pointer
	if(f==Max_RW_files)	return(false);	//not an insert-capable file
	if(mode == 3)
		offset += file_length(RW_file[f]);				//offset relates to end of file
	else if(mode == 1)
		offset += file_seek(RW_file[f], NULL, 4);		//offset relates to current position
	else	//mode==0
		offset += 0;											//offset relates to beginning of file
	int bytepos = offset;	//define position that changes take place at
	int remain  = file_length(RW_file[f]) - bytepos;	//and bytes-remaining in file
	int byt;	file_seek(RW_file[f],0,0);		//return read_position to start of file
	//Copy first half
	var*	PartA = (var*)malloc((int)sizeof(var)*bytepos);	//create read buffer PartA
	for(byt=0; byt<bytepos; byt++)	PartA[byt] = file_asc_read(RW_file[f]);	//and fill
	//Copy last half
	var*	PartB = (var*)malloc((int)sizeof(var)*remain);		//create read buffer PartA
	for(byt=0; byt<remain; byt++)		PartB[byt] = file_asc_read(RW_file[f]);	//and fill
	file_close(RW_file[f]);		//close the old file	
	file_delete(RW_filename[f]); 	//and delete it, so we can create a new one
	//Create replacement file
	RW_file[f] = file_open_write(RW_filename[f]);	//open new empty file
	for(byt=0; byt<bytepos; byt++)	file_asc_write(RW_file[f], PartA[byt]);	//start fill
	file_str_write(RW_file[f], text);		//write in the bit we've added
	bytepos = file_seek(RW_file[f], NULL, 4);	//calculate the read position to after data
	for(byt=0; byt<remain; byt++)	file_asc_write(RW_file[f], PartB[byt]);	//continue fill
	file_close(RW_file[f]);	//close newly created file
	RW_file[f] = file_open_read(RW_filename[f]);	//re-open it as insert-capable
	file_seek(RW_file[f], bytepos, 0);		//re-position read position to after added data
	*file = RW_file[f];		//reset pointer to file to calling function
	free(PartA);	free(PartB);	//free read buffer memory for first and second 'halves' of file
	return(true);	//return successful status	
}
//
/////////////////////////////////////////////////////////////////////////////////////////
//
function file_asc_insert(var *file, var num, var offset, var mode)
{	if(*file==NULL)	return(false);	//not a valid file pointer
	STRING* tmpStr = " ";		//temp space for to-be-written data
	str_for_asc(tmpStr, num);		//convert ASC value into CHAR character for writing
	var Result = file_str_insert(*file, tmpStr, offset, mode);	//write character 'string'
	str_remove(tmpStr);			//remove/free temp spaces from has-be-written data
	return(Result);		//inform caller of sucess/failure
}
//
/////////////////////////////////////////////////////////////////////////////////////////
//
function file_var_insert(var *file, var num, var offset, var mode)
{	if(*file==NULL)	return(false);	//not a valid file pointer
	STRING* tmpStr = ".............";		//temp space for to-be-written data
	str_for_num(tmpStr, num);		//convert number value into STRING for writing
	var Result = file_str_insert(*file, tmpStr, offset, mode);	//write character 'string'
	str_remove(tmpStr);			//remove/free temp spaces from has-be-written data
	return(Result);		//inform caller of sucess/failure
}
//
/////////////////////////////////////////////////////////////////////////////////////////
//
function file_num_cut(var *file, var cut_qty, var offset, var mode)
{	if(*file==NULL)	return(false);	//not a valid file pointer
	if(cut_qty==0)		return(true);	//NO characters needing extracting, assume OK
	var f; for(f=0; f<Max_RW_files; f++)		//look for table entry
	{	if(RW_file[f]==*file)	break;		}			//that matches pointer
	if(f==Max_RW_files)	return(false);	//not an insert-capable file
	if(mode == 3)
		offset += file_length(RW_file[f]);				//offset relates to end of file
	else if(mode == 1)
		offset += file_seek(RW_file[f], NULL, 4);		//offset relates to current position
	else	//mode==0
		offset += 0;											//offset relates to beginning of file
	int bytepos = offset;	//define position that changes take place at
	if(cut_qty<0)	bytepos -= abs(cut_qty);	//re-position read position to START of cut
	int remain  = file_length(RW_file[f]) - bytepos - cut_qty;	//calc bytes-remaining in file
	file_close(RW_file[f]);	//close source file
	file_rename(RW_filename[f], "temp.tmp");		//rename (to be deleted) source file
	var source = file_open_read("temp.tmp");	//open temp source file
	RW_file[f] = file_open_write(RW_filename[f]);	//open new empty file
	var byt; for(byt=0; byt<bytepos; byt++)	file_asc_write(RW_file[f], file_asc_read(source));
	for(byt=0; byt<abs(cut_qty); byt++)	file_asc_read(source);	//throw away junked data
	bytepos = file_seek(RW_file[f], NULL, 4);	//calculate the read position to after cut
	for(byt=0; byt<remain; byt++)	file_asc_write(RW_file[f], file_asc_read(source));
	file_close(source);	//close old source file
	file_delete("temp.tmp");	//delete old source file
	file_close(RW_file[f]);	//close newly created file
	RW_file[f] = file_open_read(RW_filename[f]);	//re-open it as insert-capable
	file_seek(RW_file[f], bytepos, 0);		//re-position read position to after added data
	*file = RW_file[f];		//reset pointer to file to calling function
	return(true);	//return successfule status	
}
//
//
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
//
#endif	// File_Open_RW_h

Posted By: Cowabanga

Re: Inserting data in the middle of a file - 02/14/09 16:13

Wow! Thanks EvilSOB!
Posted By: Joey

Re: Inserting data in the middle of a file - 02/14/09 16:51

can you say something about performance compared to normal appending?
Posted By: EvilSOB

Re: Inserting data in the middle of a file - 02/14/09 18:23

Errrr,....Ummmm,...., not really.
While testing this I found an intermittant bug that causes a application crash.
Only on files over 500k, but it may be cause Im using garbage data, but it still
needs fixing.
Work in progress with this, then I'll write a function to allow the removal of
X number of bytes from the middle of a file.

BUT, I can say for speed, mines bad cause its in code, but the append is handled
by the operating system. Mine takes about 3500 microseconds on a 10k file, and
append takes LESS than 1 microsecond !!!
So we cant really do ANY comparison between mine and appending.

Anyway, back to the de-bugging

[EDIT] De-bugging complete (till we find something else!?!). I found I was using
a var in my malloc calculations that fell over if one of the file 'halves' was larger than 260k.
The file_str_insert function is all that needs to be replaced, Ive edited my head post
with the corrected code.

Posted By: EvilSOB

Re: Inserting data in the middle of a file - 02/15/09 10:44

Minor re-write has meant me replacing the entire top code-post.

Mostly re-naming of functions/variable to improve naming conventions.

Minor bug-fix for bug that fumbled things if more then one ReadWrite file was opened at any one time.

ADDED file_num_cut(HANDLE, NUM) function. Cuts out NUM number of characters from current read position.
If a negative NUM is supplied, characters are cut BACKWARDS fom the current read position instead.

Any suggestions for variants or new functions anyone?
Posted By: MMike

Re: Inserting data in the middle of a file - 02/17/09 16:14

Ok so when is this going to the wiki...?
And many thanks! This is very handy.

Posted By: TechMuc

Re: Inserting data in the middle of a file - 02/17/09 23:51

thank you ofc, but just as a small side-note: The code will be as slow as hell with big files (as you use asc read with just one byte..).

Just test your code (i havn't - but had a short look at your code), with any big (+1MB) file.

You have to read all bytes until the write position at once.
Posted By: EvilSOB

Re: Inserting data in the middle of a file - 02/18/09 01:23

I know, it was sluggish BIG TIME, but Im in the middle of testing a major overhaul that makes use
of the bulk reading capability of file_load().
I should be posting new code (but still with same function names and usage) in a couple of hours.
Posted By: EvilSOB

Re: Inserting data in the middle of a file - 02/18/09 12:43

The NEW and IMPROVED version is now available in the top post,
along with performance specs.

Enjoy averyone...
Posted By: Joey

Re: Inserting data in the middle of a file - 02/19/09 14:06

you could use the older code for small files, since it's like five times faster than the new one. just a suggestion.
Posted By: EvilSOB

Re: Inserting data in the middle of a file - 02/19/09 20:40

yes you can, thats why I left the old code available in the spoiler at the bottom.
But I think the new code is fast enough that it will probably do fine anyway.
Posted By: djfeeler

Re: Inserting data in the middle of a file - 07/18/10 20:01

hello,

I tested your code I get an error E1513 when copying the file in the function file_void_insert at the line memcpy( buffer1+bytepos , block, block_size); //copy across insert data

thankss you for your code !
Posted By: EvilSOB

Re: Inserting data in the middle of a file - 07/18/10 22:47

I cant see a way for it to fail there.

Can you post the code (and a descritpion of the file & data) that killed it?
Posted By: djfeeler

Re: Inserting data in the middle of a file - 07/19/10 05:00

hello,

here my code :

thanks in advance to say what's wrong !



Description: my code
Attached File
test2.rar  (41 downloads)
Posted By: EvilSOB

Re: Inserting data in the middle of a file - 07/19/10 07:41

The crash is because you arent giving "file_str_insert" a string.
When you trying to call it like this
file_str_insert(RW_file,"A",1,1);
then you are only giving it a single byte of type "CHAR" when it needs a "STRING" type.

To achieve this simply, callthe function like so
file_str_insert(RW_file, _str("A") ,1,1);
will give the function a temporary string, which is good enough...
© 2024 lite-C Forums