Given an encoded buffer in C++, what would be the steps using oggvorbis structs to decode the already in-memory data?
OggVorbis_File cannot be used, because assets are within compressed archives.
I'm trying to research the necessary structs and methods, but I'm fairly new to audio encoding and decoding.
Any resources that can help further my reading are appreciated as well!
I should clarify, I intend to use the decoded data to stream into OpenAL.
Thanks.
Answering my own question.
This can be done by providing custom callbacks to vorbis.
struct ogg_file
{
char* curPtr;
char* filePtr;
size_t fileSize;
};
size_t AR_readOgg(void* dst, size_t size1, size_t size2, void* fh)
{
ogg_file* of = reinterpret_cast<ogg_file*>(fh);
size_t len = size1 * size2;
if ( of->curPtr + len > of->filePtr + of->fileSize )
{
len = of->filePtr + of->fileSize - of->curPtr;
}
memcpy( dst, of->curPtr, len );
of->curPtr += len;
return len;
}
int AR_seekOgg( void *fh, ogg_int64_t to, int type ) {
ogg_file* of = reinterpret_cast<ogg_file*>(fh);
switch( type ) {
case SEEK_CUR:
of->curPtr += to;
break;
case SEEK_END:
of->curPtr = of->filePtr + of->fileSize - to;
break;
case SEEK_SET:
of->curPtr = of->filePtr + to;
break;
default:
return -1;
}
if ( of->curPtr < of->filePtr ) {
of->curPtr = of->filePtr;
return -1;
}
if ( of->curPtr > of->filePtr + of->fileSize ) {
of->curPtr = of->filePtr + of->fileSize;
return -1;
}
return 0;
}
int AR_closeOgg(void* fh)
{
return 0;
}
long AR_tellOgg( void *fh )
{
ogg_file* of = reinterpret_cast<ogg_file*>(fh);
return (of->curPtr - of->filePtr);
}
Usage
ov_callbacks callbacks;
ogg_file t;
t.curPtr = t.filePtr = compressedData;
t.fileSize = compressedDataSize;
OggVorbis_File* ov = new OggVorbis_File;
mOggFile = ov;
memset( ov, 0, sizeof( OggVorbis_File ) );
callbacks.read_func = AR_readOgg;
callbacks.seek_func = AR_seekOgg;
callbacks.close_func = AR_closeOgg;
callbacks.tell_func = AR_tellOgg;
int ret = ov_open_callbacks((void *)&t, ov, NULL, -1, callbacks);
vorbis_info* vi = ov_info(ov, -1);
assert(vi);
/* compressed data is available to use, to uncompress look into ov_read */
A Special thanks to the Doom3 GPL source for most of the help with this, it can
be viewed at : here
You also can don't reinvent the wheel and use fmemopen like this:
FILE* memfile = fmemopen(data, len, "r");
Where data is pointer to memory beginning and len is length of your data. Then pass memfile to ov_open like regular FILE object.
However, there is downside: this function seems linux-specific (but it can be found in arduino, so I'm a bit confused about its status), so you don't have it on other systems. But there is some implementations for them (check libconfuse for window or for apple OSes).
Related
I'm currently in process of migrating my hobby project from std::fstream to SDL_RWops (because SDL_RWops is my only simple choice for loading assets on Android).
Reading from a file works perfectly, but writing to a file is incredibly slow.
Consider following testcases:
C standard IO - 0.217193 secs
std::FILE *io = std::fopen("o.txt", "w");
for (int i = 0; i < 1024*1024*4; i++)
std::putc('0', io);
std::fclose(io);
C++ streams - 0.278278 secs
std::ofstream io("o.txt");
for (int i = 0; i < 1024*1024*4; i++)
io << '0';
io.close();
SDL_RWops: - 17.9893 secs
SDL_RWops *io = SDL_RWFromFile("o.txt", "w");
for (int i = 0; i < 1024*1024*4; i++)
io->write(io, "0", 1, 1);
io->close(io);
All testcases were compiled with g++ 5.3.0 (mingw-w64) x86 with -O3. I've used SDL 2.0.4.
I've also tried -O0 with similar results (0.02 to 0.25 seconds slower).
After looking at these results I have an obvious questions:
Why SDL_RWops writing performance is so poor?
What can I do to make it perform better?
Edit: Here is the code of windows_file_write() (from SDL), which is what io->write should point to. It should do buffered output, but I'm not sure how it works.
static size_t SDLCALL
windows_file_write(SDL_RWops * context, const void *ptr, size_t size, size_t num)
{
size_t total_bytes;
DWORD byte_written;
size_t nwritten;
total_bytes = size * num;
if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE || total_bytes <= 0 || !size)
return 0;
if (context->hidden.windowsio.buffer.left) {
SetFilePointer(context->hidden.windowsio.h,
-(LONG)context->hidden.windowsio.buffer.left, NULL,
FILE_CURRENT);
context->hidden.windowsio.buffer.left = 0;
}
/* if in append mode, we must go to the EOF before write */
if (context->hidden.windowsio.append) {
if (SetFilePointer(context->hidden.windowsio.h, 0L, NULL, FILE_END) ==
INVALID_SET_FILE_POINTER) {
SDL_Error(SDL_EFWRITE);
return 0;
}
}
if (!WriteFile
(context->hidden.windowsio.h, ptr, (DWORD)total_bytes, &byte_written, NULL)) {
SDL_Error(SDL_EFWRITE);
return 0;
}
nwritten = byte_written / size;
return nwritten;
}
In short: I've managed to improve it. Now I'm getting 0.316382 secs, which is only a bit slower than other solutions.
But it's one of the dirtiest hacks I've ever done in my life. I'd appreciate any better solutions.
How it was done: I've rolled custom replacement for SDL_RWFromFile(): I've copy-pasted the implementation from SDL_rwops.c and removed all preprocessor branches as if only HAVE_STDIO_H was defined. The function contained a call to SDL_RWFromFP(), thus I've copy-pasted SDL_RWFromFP() too and applied same modifications to it. In turn, SDL_RWFromFP() relied on stdio_size(),stdio_read(),stdio_write(),stdio_seek() and stdio_close() (these are a part of SDL_rwops.c too), thus I've copy-pasted them too. In turn, these relied (again!) on some fields of "hidden" union inside of struct SDL_RWops, which are disabled on windows using preprocessor. Instead of changing the header, I've changed the copy-pasted code to use different members of "hidden" union, which do exist on windows. (It's safe, because nothing except my own and copy-pasted code touches the struct.) Some other tweaks were made to make the code work as C++ instead of C.
This is what I got:
#if OnWindows
#define hidden_stdio_fp ((FILE * &)context->hidden.windowsio.h)
#define hidden_stdio_autoclose ((SDL_bool &)context->hidden.windowsio.append)
// ** Begin copied code **
static auto stdio_size = [](SDL_RWops * context) -> int64_t
{
int64_t pos, size;
pos = SDL_RWseek(context, 0, RW_SEEK_CUR);
if (pos < 0) {
return -1;
}
size = SDL_RWseek(context, 0, RW_SEEK_END);
SDL_RWseek(context, pos, RW_SEEK_SET);
return size;
};
static auto stdio_seek = [](SDL_RWops * context, int64_t offset, int whence) -> int64_t
{
#ifdef HAVE_FSEEKO64
if (std::fseeko64(hidden_stdio_fp, (off64_t)offset, whence) == 0) {
return std::ftello64(hidden_stdio_fp);
}
#elif defined(HAVE_FSEEKO)
if (std::fseeko(hidden_stdio_fp, (off_t)offset, whence) == 0) {
return std::ftello(hidden_stdio_fp);
}
#elif defined(HAVE__FSEEKI64)
if (std::_fseeki64(hidden_stdio_fp, offset, whence) == 0) {
return std::_ftelli64(hidden_stdio_fp);
}
#else
if (std::fseek(hidden_stdio_fp, offset, whence) == 0) {
return std::ftell(hidden_stdio_fp);
}
#endif
return SDL_Error(SDL_EFSEEK);
};
static auto stdio_read = [](SDL_RWops * context, void *ptr, std::size_t size, std::size_t maxnum) -> std::size_t
{
std::size_t nread;
nread = std::fread(ptr, size, maxnum, hidden_stdio_fp);
if (nread == 0 && std::ferror(hidden_stdio_fp)) {
SDL_Error(SDL_EFREAD);
}
return nread;
};
static auto stdio_write = [](SDL_RWops * context, const void *ptr, std::size_t size, std::size_t num) -> std::size_t
{
std::size_t nwrote;
nwrote = std::fwrite(ptr, size, num, hidden_stdio_fp);
if (nwrote == 0 && std::ferror(hidden_stdio_fp)) {
SDL_Error(SDL_EFWRITE);
}
return nwrote;
};
static auto stdio_close = [](SDL_RWops * context) -> int
{
int status = 0;
if (context) {
if (hidden_stdio_autoclose) {
/* WARNING: Check the return value here! */
if (std::fclose(hidden_stdio_fp) != 0) {
status = SDL_Error(SDL_EFWRITE);
}
}
SDL_FreeRW(context);
}
return status;
};
static auto RWFromFP = [](FILE * fp, SDL_bool autoclose) -> SDL_RWops *
{
SDL_RWops *context = 0;
context = SDL_AllocRW();
if (context != 0) {
context->size = stdio_size;
context->seek = stdio_seek;
context->read = stdio_read;
context->write = stdio_write;
context->close = stdio_close;
hidden_stdio_fp = fp;
hidden_stdio_autoclose = autoclose;
context->type = SDL_RWOPS_STDFILE;
}
return context;
};
static auto SDL_RWFromFile = [](const char *file, const char *mode) -> SDL_RWops *
{
SDL_RWops *context = 0;
if (!file || !*file || !mode || !*mode) {
SDL_SetError("SDL_RWFromFile(): No file or no mode specified");
return 0;
}
FILE *fp = std::fopen(file, mode);
if (fp == 0) {
SDL_SetError("Couldn't open %s", file);
} else {
context = RWFromFP(fp, (SDL_bool)1);
}
return context;
};
// ** End copied code **
#undef hidden_stdio_fp
#undef hidden_stdio_autoclose
#endif
Hi please some one help me
I've two process say some X and Y.
X and Y both have the following information
typedef enum {
HEALTHY=1,
FAULTY=2,
CHANGE=3,
ERROR=4
} MsgTypeT;
typedef struct {
char role[16];
char state[16];
char info[256];
} InfoT;
typedef struct {
MsgTypeT type;
int size;
InfoT *data;
} MsgT;
Here the condition is that if process Y sends an information process X will read it
So i used fifo between x and y
Y has a function write buffer which writes to fifo and code is as following
int write_buffer(HA_DEVMON_MsgT const* send)
{
char* dest = buffer;
memcpy( dest, &send->type, sizeof( MsgTypeT ));
dest += sizeof(MsgTypeT);
memcpy( dest, &send->size, sizeof( int ));
dest += sizeof(int);
memcpy( dest, send->data, sizeof( InfoT ));
dest += sizeof(InfoT);
int byteCount = write( this->fifo_fd, buffer, dest - buffer );
if ( byteCount != dest - buffer ) {
cout<<"Error in writing ";
}
return byteCount == dest - buffer ? 0 : -1;
}
I think it's writing perfectly because cout statements are working fine also when tried to output nbytes it gave 512bytes have been written
Now when X tries to read it's giving null values for role and state also size its giving 6441568
Its only giving MsgTypeT correct other values are null :(
The code is as follows--- I'm doing something wrong please correct it
int readMsg(MsgT *msg)
{
int rCode=0, nbytes=0;
char buffer[512]={0};
nbytes = read(this->get_handle(), buffer, sizeof(buffer));
if (nbytes < 0) {
cout<<"error in read";
rCode=-1;
}
if (rCode == 0) {
char *p_src = (char *)buffer;
mempcpy(&msg->type, p_src, sizeof(MsgTypeT));
p_src+=sizeof(MsgTypeT);
mempcpy(&msg->size, p_src, sizeof(int));
p_src+=sizeof(int);
msg->data = new InfoT(); //allocating memory (needed or not???)
mempcpy(msg->data, p_src, sizeof(InfoT));
p_src+=sizeof(InfoT);
}
return rCode;
}
In readMsg, your last mempcpy writes to msg, not to the
InfotT you just allocated.
Also, but I suppose you know this: this is only guaranteed to
work if both processes were compiled with the same compiler,
using the same options. (In practice, it's likely to work if
the underlying system defines its API in terms of C, which is
the case for Windows and Unix.)
EDIT:
Further: you have the same problem when writing. You write
sizeof(InfoT) (288) bytes, but you write the pointer (and then
a lot of garbage), not the data it's pointing to.
And you increment the pointer into the MsgT object. This is
likely not to work, if there is any padding. What you really
have to do is:
int
write_buffer( MsgT const* data )
{
char buffer[512] = {}; // Or better yet, std::vector<char>
char* dest = buffer;
memcpy( dest, &data->type, sizeof( MsgTypeT ) );
dest += sizeof( MsgTypeT );
memcpy( dest, &data->size, sizeof( int ) );
dest += sizeof( int );
memcpy( dest, &data->data, sizeof( InfoT ) );
dest += sizeof( InfoT );
int byteCount = write( fifo_fd, buffer, dest - buffer );
if ( byteCount != dest - buffer ) {
std::cerr << "Error in write" << std::endl;
}
return byteCount == dest - buffer ? 0 : -1;
}
and the opposite when reading.
And once again, this will only really work for two processes on
the same machine, compiled with the same compiler using the same
options. A better solution would probably be to define
a protocol, with a defined representation of integers, strings,
etc., format your output to that representation, and parse it
for your input. That way, it will still work even if one of the
processes is 64 bits, and the other 32.
I am writing a simple app that outputs all files in some directory to console. To achieve this I dynamically allocate memory in function PathCreator() and return a pointer to this memory. I don't know how to correctly free this memory segment in GetAllFiles(). When I use the code below I get a stack overflow exception. How can I fix this? Please don't offer me to use something that doesn't need dynamically allocated memory, I just want to fix my code.
#include "stdafx.h"
#include <windows.h>
#include <iostream>
wchar_t *PathCreator(wchar_t *dir, wchar_t *fileName);
int is_directory(wchar_t *p)
{
wchar_t *t = PathCreator(p,L"\\");
WIN32_FIND_DATA file;
HANDLE search_hendle = FindFirstFile(t, &file);
long error = GetLastError();
if(error == 267)
{
return 0;
}
else
{
return 1;
}
}
wchar_t *PathCreator(wchar_t *dir, wchar_t *fileName)
{
wchar_t* path = 0;
int size = 0;
wchar_t *d = dir;
wchar_t *f = fileName;
while(*d != '\0')
{
d++;
size++;
}
while(*f != '\0')
{
f++;
size++;
}
path = new wchar_t[(size+=3) * sizeof(wchar_t)];
int j = 0;
while(j < size)
{
path[j] = '\0';
j++;
}
int i;
i = 0;
while(*dir != '\0')
{
path[i] = *dir;
i++;
dir++;
}
path[i++] = '\\';
wchar_t *t = fileName;
while(*t != '\0')
{
path[i] = *t;
i++;
t++;
}
path[i] = '\0';
return path;
}
void GetAllFiles(wchar_t* dir)
{
wchar_t *p = 0;
int i = 0;
WIN32_FIND_DATA file;
wchar_t *t = PathCreator(dir, L"*");
HANDLE search_hendle = FindFirstFile(t, &file);
if(search_hendle)
{
do
{
p = PathCreator(dir,file.cFileName);
if(!is_directory(p))
{
std::wcout << p << std::endl;
}
else
{
GetAllFiles(p);
}
delete [] p;
}
while(FindNextFile(search_hendle, &file));
}
delete [] t;
FindClose(search_hendle);
}
int _tmain(int argc, _TCHAR* argv[])
{
GetAllFiles(L"C:\\Users");
}
So, you have "." and ".." in your directory search.
The first entry is ".", so:
p = PathCreator(dir, file.cFilename)
yields:
"C:\Users\."
Then the next line:
if (!is_directory(p))
Is ALWAYS false, so it just keeps recursing into:
GetAllFiles(p)
forever ... or until your stack blows up, whichever comes first ;-)
I would recommend explicitly checking for "." and ".." and skipping those entries (also MFC and Qt, etc. have nice directory handling classes, but I think you want to do it this way).
My modification:
do
{
// I added this - guess I can't embolden code text
if (wcscmp(file.cFileName,L".") == 0 || wcscmp(file.cFileName,L"..")==0)
continue;
p = PathCreator(dir,file.cFileName);
if(!is_directory(p))
{
std::wcout << p << std::endl;
}
else
{
GetAllFiles(p);
}
delete [] p;
}
while(FindNextFile(search_hendle, &file));
Again you try to use C in place of C++ and you still using wcout?! no problem you are a programmer and I'm sure you have a reason for this! but memory management in C is much much harder than C++ and you should have some skills to use it. Here is a fully working code but as you see it is really harder to manage, use and understand than its C++ version using standard containers and string, so if you are allowed to use C++(as you use wcout) then use its C++ version for ease:
#include <Windows.h>
/*! \brief Merge \a folder and \a filename into a newly allocate memory and
* return it to the caller. Use free to free returned memory!
*/
wchar_t* PathCreator( wchar_t const* folder, wchar_t const* filename )
{
wchar_t* res;
size_t i, len, folderLen = wcslen( folder ), filenameLen = wcslen( filename );
len = folderLen + filenameLen;
if( folder[folderLen - 1] != '\\' ) ++len;
++len; // for \0
res = (wchar_t*) malloc( sizeof(wchar_t) * len );
if( !res ) return NULL;
wcscpy_s( res, len, folder );
/* Remove possible wide card at end of folder */
for( i = folderLen; i--; ) {
if( res[i] == '*' || res[i] == '?' ) {
res[i] = 0;
--folderLen;
} else {
break;
}
}
if( res[folderLen - 1] != '\\' ) wcscat_s( res, len, L"\\" );
wcscat_s( res, len, filename );
return res;
}
/*! \brief Free memory that returned by \ref GetAllFiles
*/
void FreeAllFilesMemory( wchar_t** p )
{
wchar_t** tmp = p;
if( !p ) return ;
while( *tmp ) free( *tmp++ );
free( p );
}
wchar_t** AddToArray( wchar_t** p, size_t* pAllocated, size_t* pUsed, wchar_t* s )
{
if( *pUsed >= *pAllocated ) {
size_t newAlloc = *pAllocated * 3 / 2; // Grow by 1.5
if( newAlloc < 16 ) newAlloc = 16;
p = (wchar_t**) realloc( p, newAlloc * sizeof(wchar_t*) );
if( !p ) return NULL;
*pAllocated = newAlloc;
}
p[*pUsed] = s;
++*pUsed;
return p;
}
wchar_t** GetAllFilesImpl( wchar_t const* folder, wchar_t** res, size_t* pAllocated, size_t* pUsed )
{
HANDLE hSearch;
WIN32_FIND_DATAW fileinfo;
size_t allocatedMemory = 0;
hSearch = FindFirstFileW( folder, &fileinfo );
if( hSearch != INVALID_HANDLE_VALUE ) {
do {
wchar_t* sFileName, ** tmp, sTmp[ 1024 ];
/* ignore ., .. */
if( !wcscmp(fileinfo.cFileName, L".") ||
!wcscmp(fileinfo.cFileName, L"..") )
continue;
sFileName = PathCreator( folder, fileinfo.cFileName );
wprintf( L"%s\n", sFileName ); /* Print result */
tmp = AddToArray( res, pAllocated, pUsed, sFileName );
if( !tmp ) return FreeAllFilesMemory(res), NULL;
res = tmp;
if( fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
wcscpy_s( sTmp, sFileName );
wcscat_s( sTmp, L"\\*" );
tmp = GetAllFilesImpl( sTmp, res, pAllocated, pUsed );
if( !tmp ) return NULL;
res = tmp;
}
} while( FindNextFileW(hSearch, &fileinfo) );
FindClose( hSearch );
}
return res;
}
/*! \brief List all files that match a pattern and return it as an array of
* wide strings, free result using \ref FreeAllFilesMemory
*/
wchar_t** GetAllFiles( wchar_t const* folder )
{
size_t nAllocated = 0, nUsed = 0;
wchar_t** res = GetAllFilesImpl( folder, NULL, &nAllocated, &nUsed );
if( res ) {
/* to indicate end of result add a NULL string */
wchar_t** tmp = AddToArray( res, &nAllocated, &nUsed, NULL );
if( !tmp ) return FreeAllFilesMemory(res), NULL;
res = tmp;
}
return res;
}
I need to search a (non-text) file for the byte sequence "9µ}Æ" (or "\x39\xb5\x7d\xc6").
After 5 hours of searching online this is the best I could do. It works but I wanted to know if there is a better way:
char buffer;
int pos=in.tellg();
// search file for string
while(!in.eof()){
in.read(&buffer, 1);
pos=in.tellg();
if(buffer=='9'){
in.read(&buffer, 1);
pos=in.tellg();
if(buffer=='µ'){
in.read(&buffer, 1);
pos=in.tellg();
if(buffer=='}'){
in.read(&buffer, 1);
pos=in.tellg();
if(buffer=='Æ'){
cout << "found";
}
}
}
}
in.seekg((streampos) pos);
Note:
I can't use getline(). It's not a text file so there are probably not many line breaks.
Before I tried using a multi-character buffer and then copying the buffer to a C++ string, and then using string::find(). This didn't work because there are many '\0' characters throughout the file, so the sequence in the buffer would be cut very short when it was copied to the string.
Similar to what bames53 posted; I used a vector as a buffer:
std::ifstream ifs("file.bin");
ifs.seekg(0, std::ios::end);
std::streamsize f_size = ifs.tellg();
ifs.seekg(0, std::ios::beg);
std::vector<unsigned char> buffer(f_size);
ifs.read(buffer.data(), f_size);
std::vector<unsigned char> seq = {0x39, 0xb5, 0x7d, 0xc6};
bool found = std::search(buffer.begin(), buffer.end(), seq.begin(), seq.end()) != buffer.end();
If you don't mind loading the entire file into an in-memory array (or using mmap() to make it look like the file is in memory), you could then search for your character sequence in-memory, which is a bit easier to do:
// Works much like strstr(), except it looks for a binary sub-sequence rather than a string sub-sequence
const char * MemMem(const char * lookIn, int numLookInBytes, const char * lookFor, int numLookForBytes)
{
if (numLookForBytes == 0) return lookIn; // hmm, existential questions here
else if (numLookForBytes == numLookInBytes) return (memcmp(lookIn, lookFor, numLookInBytes) == 0) ? lookIn : NULL;
else if (numLookForBytes < numLookInBytes)
{
const char * startedAt = lookIn;
int matchCount = 0;
for (int i=0; i<numLookInBytes; i++)
{
if (lookIn[i] == lookFor[matchCount])
{
if (matchCount == 0) startedAt = &lookIn[i];
if (++matchCount == numLookForBytes) return startedAt;
}
else matchCount = 0;
}
}
return NULL;
}
.... then you can just call the above function on the in-memory data array:
char * ret = MemMem(theInMemoryArrayContainingFilesBytes, numBytesInFile, myShortSequence, 4);
if (ret != NULL) printf("Found it at offset %i\n", ret-theInMemoryArrayContainingFilesBytes);
else printf("It's not there.\n");
This program loads the entire file into memory and then uses std::search on it.
int main() {
std::string filedata;
{
std::ifstream fin("file.dat");
std::stringstream ss;
ss << fin.rdbuf();
filedata = ss.str();
}
std::string key = "\x39\xb5\x7d\xc6";
auto result = std::search(std::begin(filedata), std::end(filedata),
std::begin(key), std::end(key));
if (std::end(filedata) != result) {
std::cout << "found\n";
// result is an iterator pointing at '\x39'
}
}
const char delims[] = { 0x39, 0xb5, 0x7d, 0xc6 };
char buffer[4];
const size_t delim_size = 4;
const size_t last_index = delim_size - 1;
for ( size_t i = 0; i < last_index; ++i )
{
if ( ! ( is.get( buffer[i] ) ) )
return false; // stream to short
}
while ( is.get(buffer[last_index]) )
{
if ( memcmp( buffer, delims, delim_size ) == 0 )
break; // you are arrived
memmove( buffer, buffer + 1, last_index );
}
You are looking for 4 bytes:
unsigned int delim = 0xc67db539;
unsigned int uibuffer;
char * buffer = reinterpret_cast<char *>(&uibuffer);
for ( size_t i = 0; i < 3; ++i )
{
if ( ! ( is.get( buffer[i] ) ) )
return false; // stream to short
}
while ( is.get(buffer[3]) )
{
if ( uibuffer == delim )
break; // you are arrived
uibuffer >>= 8;
}
Because you said you cannot search the entire file because of null terminator characters in the string, here's an alternative for you, which reads the entire file in and uses recursion to find the first occurrence of a string inside of the whole file.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
string readFile (char *fileName) {
ifstream fi (fileName);
if (!fi)
cerr << "ERROR: Cannot open file" << endl;
else {
string str ((istreambuf_iterator<char>(fi)), istreambuf_iterator<char>());
return str;
}
return NULL;
}
bool findFirstOccurrenceOf_r (string haystack, char *needle, int haystack_pos, int needle_pos, int needle_len) {
if (needle_pos == needle_len)
return true;
if (haystack[haystack_pos] == needle[needle_pos])
return findFirstOccurrenceOf_r (haystack, needle, haystack_pos+1, needle_pos+1, needle_len);
return false;
}
int findFirstOccurrenceOf (string haystack, char *needle, int length) {
int pos = -1;
for (int i = 0; i < haystack.length() - length; i++) {
if (findFirstOccurrenceOf_r (haystack, needle, i, 0, length))
return i;
}
return pos;
}
int main () {
char str_to_find[4] = {0x39, 0xB5, 0x7D, 0xC6};
string contents = readFile ("input");
int pos = findFirstOccurrenceOf (contents, str_to_find, 4);
cout << pos << endl;
}
If the file is not too large, your best solution would be to load the whole file into memory, so you don't need to keep reading from the drive. If the file is too large to load in at once, you would want to load in chunks of the file at a time. But if you do load in chucks, make sure you check to edges of the chunks. It's possible that your chunk happens to split right in the middle of the string you're searching for.
Note that I'm using a C++ compiler ( hence, the cast on the calloc function calls) to do this, but the code is essentially C.
Basically, I have a typedef to an unsigned char known as viByte, which I'm using to create a string buffer to parse a file from binary (a TGA file, to be exact - but, that's irrelevant).
I'm writing basic functions for it right now; append, prepend, new, etc.
The problem is that, on the first iteration of the first loop in viByteBuf_Prepend, I get a segmentation fault. I need to know why, exactly, as this is something which could keep me up all night without some pointers (pun intended).
I also would like to know if my algorithms are correct in terms of how the buffer is pre-pending the viByte string. For example, I have a feeling that using memset too much might be a bad idea, and whether or not my printf format for the unsigned char is correct (I have a feeling it isn't, as nothing is getting output to my console).
Compiling on GCC, Linux.
Ze Code
#ifdef VI_BYTEBUF_DEBUG
void viByteBuf_TestPrepend( void )
{
viByteBuf* buf = viByteBuf_New( 4 );
buf->str = ( viByte* ) 0x1;
printf(" Before viByteBuf_Prepend => %uc ", buf->str);
viByteBuf_Prepend( buf, 3, ( viByte* ) 0x2 );
printf(" After viByteBuf_Prepend => %uc ", buf->str);
}
#endif
viByteBuf* viByteBuf_New( unsigned int len )
{
viByteBuf* buf = ( viByteBuf* ) calloc( sizeof( viByteBuf ), 1 );
const int buflen = len + 1;
buf->str = ( viByte* ) calloc( sizeof( viByte ), buflen );
buf->len = buflen;
buf->str[ buflen ] = '\0';
return buf;
}
void viByteBuf_Prepend( viByteBuf* buf, unsigned int len, viByte* str )
{
unsigned int pos, i;
const unsigned int totallen = buf->len + len;
viByteBuf* tmp = viByteBuf_New( totallen );
viByte* strpos = buf->str;
memset( tmp->str, 0, tmp->len );
int index;
for( i = 0; i < buf->len; ++i )
{
index = ( buf->len - i ) - 1;
*strpos = buf->str[ 0 ];
++strpos;
}
memset( buf->str, 0, buf->len );
printf( "%uc\n", buf->str );
i = totallen;
for ( pos = 0; pos < len; ++pos )
{
tmp->str[ pos ] = str[ pos ];
tmp->str[ i ] = buf->str[ i ];
--i;
}
memset( buf->str, 0, buf->len );
buf->len = tmp->len;
memcpy( buf->str, tmp->str, tmp->len );
viByteBuf_Free( tmp );
//memset( )
//realloc( ( viByteBuf* ) buf, sizeof( viByteBuf ) * tmp->len );
}
Many thank yous.
Update
Sorry, I should have explicitly posted the code where the segmentation fault lies. It is right here:
for( i = 0; i < buf->len; ++i )
{
index = ( buf->len - i ) - 1;
*strpos = buf->str[ 0 ]; //<--segmentation fault.
++strpos;
}
On your code you have buf->str[ buflen ] = '\0';, but you only allocate space for buflen. I think you meant buf->str[ len ] = '\0';.