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.
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.
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.
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..