Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

loading a file's bytes?

Posted on 2011-04-21
17
Medium Priority
?
502 Views
Last Modified: 2012-06-21
Hello all.

I'm trying to unzip a file in Objective C using some standard C classes. The problem is I don't know anything of standard C. In the code below, it opens a stored file and loads it into a stream with open_file_func which I assume is the C method to load a file. However I have the zipped file already in the memory as an array of bytes.

us.filestream seems to be a void* but when I simply cast my array of bytes to (void*) it crashes immediately in the next line.
So appearantly open_file_func does more than load a file's bytes.

Anyone have any idea?


/* unz_s contain internal information about the zipfile
*/
typedef struct
{
    zlib_filefunc_def z_filefunc;
    voidpf filestream;        /* io structore of the zipfile */
    unz_global_info gi;       /* public global information */
    uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/
    uLong num_file;             /* number of the current file in the zipfile*/
    uLong pos_in_central_dir;   /* pos of the current file in the central dir*/
    uLong current_file_ok;      /* flag about the usability of the current file*/
    uLong central_pos;          /* position of the beginning of the central dir*/

    uLong size_central_dir;     /* size of the central directory  */
    uLong offset_central_dir;   /* offset of start of central directory with
                                   respect to the starting disk number */

    unz_file_info cur_file_info; /* public info about the current file in zip*/
    unz_file_info_internal cur_file_info_internal; /* private info about it*/
    file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current
                                        file if we are decompressing it */
    int encrypted;
#    ifndef NOUNCRYPT
    unsigned long keys[3];     /* keys defining the pseudo-random sequence */
    const unsigned long* pcrc_32_tab;
#    endif
} unz_s;



/*
  Open a Zip file. path contain the full pathname (by example,
     on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer
     "zlib/zlib114.zip".
     If the zipfile cannot be opened (file doesn't exist or in not valid), the
       return value is NULL.
     Else, the return value is a unzFile Handle, usable with other function
       of this unzip package.
*/
extern unzFile ZEXPORT unzOpen2 (path, pzlib_filefunc_def)
    const char *path;
    zlib_filefunc_def* pzlib_filefunc_def;
{
    unz_s us;
    unz_s *s;
    uLong central_pos,uL;

    uLong number_disk;          /* number of the current dist, used for
                                   spaning ZIP, unsupported, always 0*/
    uLong number_disk_with_CD;  /* number the the disk with central dir, used
                                   for spaning ZIP, unsupported, always 0*/
    uLong number_entry_CD;      /* total number of entries in
                                   the central dir
                                   (same than number_entry on nospan) */

    int err=UNZ_OK;

    if (unz_copyright[0]!=' ')
        return NULL;

    if (pzlib_filefunc_def==NULL)
        fill_fopen_filefunc(&us.z_filefunc);
    else
        us.z_filefunc = *pzlib_filefunc_def;

    



    // INSTEAD OF LOADING FROM A FILE, I WANT TO USE A FILE I HAVE IN MEMORY AS A BYTE ARRAY
    // z_filefunc.zopen_file points to open_file_func, ZLIB_FILEFUNC_MODE_READ = 1, ZLIB_FILEFUNC_MODE_EXISTING = 4
    us.filestream = (*(us.z_filefunc.zopen_file))(us.z_filefunc.opaque,
                                                 path,
                                                 ZLIB_FILEFUNC_MODE_READ |
                                                 ZLIB_FILEFUNC_MODE_EXISTING);





    
    if (us.filestream==NULL)
        return NULL;

    central_pos = unzlocal_SearchCentralDir(&us.z_filefunc,us.filestream);
    if (central_pos==0)
        err=UNZ_ERRNO;

    if (ZSEEK(us.z_filefunc, us.filestream,
                                      central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0)
        err=UNZ_ERRNO;

    /* the signature, already checked */
    if (unzlocal_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK)
        err=UNZ_ERRNO;

    /* number of this disk */
    if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK)
        err=UNZ_ERRNO;

    /* number of the disk with the start of the central directory */
    if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK)
        err=UNZ_ERRNO;

    /* total number of entries in the central dir on this disk */
    if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK)
        err=UNZ_ERRNO;

    /* total number of entries in the central dir */
    if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK)
        err=UNZ_ERRNO;

    if ((number_entry_CD!=us.gi.number_entry) ||
        (number_disk_with_CD!=0) ||
        (number_disk!=0))
        err=UNZ_BADZIPFILE;

    /* size of the central directory */
    if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK)
        err=UNZ_ERRNO;

    /* offset of start of central directory with respect to the
          starting disk number */
    if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK)
        err=UNZ_ERRNO;

    /* zipfile comment length */
    if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK)
        err=UNZ_ERRNO;

    if ((central_pos<us.offset_central_dir+us.size_central_dir) &&
        (err==UNZ_OK))
        err=UNZ_BADZIPFILE;

    if (err!=UNZ_OK)
    {
        ZCLOSE(us.z_filefunc, us.filestream);
        return NULL;
    }

    us.byte_before_the_zipfile = central_pos -
                            (us.offset_central_dir+us.size_central_dir);
    us.central_pos = central_pos;
    us.pfile_in_zip_read = NULL;
    us.encrypted = 0;


    s=(unz_s*)ALLOC(sizeof(unz_s));
    *s=us;
    unzGoToFirstFile((unzFile)s);
    return (unzFile)s;
}

Open in new window

0
Comment
Question by:Snapples
  • 8
  • 5
  • 4
17 Comments
 
LVL 35

Expert Comment

by:sarabande
ID: 35440295
open_file_func isn't the c function to open a file (that is fopen or open) but a function pointer (member variable) in a structure named zlib_filefunc_def. a function pointer can be used to call a customized or arbitrary setable function. most of the functions used in the code you posted obviously are functions to manage zipped files or files in a zipped archive.

Sara
0
 
LVL 40

Expert Comment

by:mrjoltcola
ID: 35440745
From your code:

>>    voidpf filestream;        /* io structore of the zipfile */

iofdptr is a pointer to an IO structure, not an array of bytes you mention, so casting to an array or assigning to that member is incorrect.

So this API you are using (zlib or something), as Sara says, is not standard C library and you'll need to understand the library to understand how to either read from the stream or to override any portion of it. I recommend drilling down into the sourcecode for the library if available, looking for use of the actual C standard lib functions and STDIO functions like fopen, fread, etc. as those would be direct and simple to override with a memory buffer that you already have.
0
 
LVL 2

Author Comment

by:Snapples
ID: 35441036
Alright, I did.
The z_filefunc.zopen_file function can simply be replaced with: us.filestream = fopen(path, "r");

So us.filestream is simply a FILE*

So now I'm trying to simply read it from memory but it crashes. With bytes being my byte array and length the size of my byte array.

    FILE* file = NULL;
    fread(bytes,1,length,file);
    us.filestream = file;

if fopen works, why doesn't fread?

FILE* file = NULL;
    
    fread(bytes,1,length,file);
    us.filestream = file;

Open in new window

0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 35

Expert Comment

by:sarabande
ID: 35441157
you don't know whether the fopen was ok. you only assigned the return to a struct member what actually doesn't mean much cause depending on your c compiler options you may not have a check that the types of the assignment must match.

the code snippet crashes because the fread needs a file argument not equal to NULL. you could try

FILE* file = fopen(path, "r");
if (file != NULL)
{
  fread(bytes,1,length,file);
}

Open in new window


but in my opinion that would not change anything for the library.

Sara
0
 
LVL 2

Author Comment

by:Snapples
ID: 35441265
Well, I know fopen works because it succesfully and correctly unzips the file. So I don't see why it shouldn't work from a file in memory?
0
 
LVL 35

Expert Comment

by:sarabande
ID: 35441273
note, it is unlikely that the library would use a byte array for the whole archive cause such archives can be very big. instead it will read it in logical trunks where the information is read from the archive file itself. so the only chance i see is - as mrjoltcola said - you need to find the fopen and fread calls made by the library functions and replace them by equivalent calls to your data.

Sara
0
 
LVL 2

Author Comment

by:Snapples
ID: 35441348
Oh I'm sorry, it would seem I forgot to attach the right code 2 posts ago. I did find those. Which is why I came to the conclusion that us.filestream is actually a FILE*.

Here are the fopen and fread calls as made by the library.

So instead of:
us.filestream = (*(us.z_filefunc.zopen_file))(us.z_filefunc.opaque,path, ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_EXISTING);

I can simply use:
us.filestream = fopen(path, "r");




// CHECKOUT open
voidpf ZCALLBACK fopen_file_func (opaque, filename, mode)
   voidpf opaque;
   const char* filename;
   int mode;
{
    FILE* file = NULL;
    const char* mode_fopen = NULL;
    if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ)
        mode_fopen = "rb";
    else
    if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
        mode_fopen = "r+b";
    else
    if (mode & ZLIB_FILEFUNC_MODE_CREATE)
        mode_fopen = "wb";

    if ((filename!=NULL) && (mode_fopen != NULL))
        file = fopen(filename, mode_fopen);
    
    return file;
}



// CHECKOUT read
uLong ZCALLBACK fread_file_func (opaque, stream, buf, size)
   voidpf opaque;
   voidpf stream;
   void* buf;
   uLong size;
{
    uLong ret;
    ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream);
    return ret;
}

Open in new window

0
 
LVL 40

Expert Comment

by:mrjoltcola
ID: 35441530
If you already have the bytes in memory, then C's fopen() and fread() don't help you.

Override fopen_file_func() and fread_file_func() to return data from your memory buffer.

The problem may be that you were mixing overrides. If "voidpf filestream" is initialized to be an array of bytes (char * or void * allocated by you) then you need to override all of the zlib callback functions to handle it as such. By default all of the builtins expect it to be FILE *. If you set it to the result of an malloc, for example, then fread_file_func() would simply access it like an array.

voidpf ZCALLBACK fopen_file_func (opaque, filename, mode)
{
   void * memoryBuf = malloc(...);
   return memoryBuf; 
}

uLong ZCALLBACK fread_file_func (opaque, stream, buf, size)
{
   memcpy(buf, memoryBuf, size);
   return ret;
}

Open in new window


Now, not being familar with the API, I assume opaque is of type "struct unz_s". If that is so, your fopen_file_funct() would stash the memoryBuf in the filestream struct member, and the fread_file_func would access it from the opaque object.

uLong ZCALLBACK fread_file_func (opaque, stream, buf, size)
{
   void * memoryBuf = ((unz_s*)opaque)->filestream;
   memcpy(buf, memoryBuf, size);
   return ret;
}

Open in new window


Hope you get the idea.
0
 
LVL 2

Author Comment

by:Snapples
ID: 35441813
Hmm I see what you mean. The problem is that the library uses fseek several times for which a FILE* is required. So I'm afraid I'm forced to use FILE*. Is there no way I can somehow cast my byte array to a FILE*?
0
 
LVL 2

Author Comment

by:Snapples
ID: 35441873
And to be honest, I still don't quite understand why a regular fread doesn't work. It's supposed to read the data from the memory (my void* data) and store it to a FILE*, so why does that give problems?
0
 
LVL 40

Expert Comment

by:mrjoltcola
ID: 35442163
>>The problem is that the library uses fseek several times for which a FILE* is required

No, seek is very easy to override. All it amounts to is changing the offset of your index into the memory buffer.


>>I still don't quite understand why a regular fread doesn't work. It's supposed to read the data from the memory (my void* data) and store it to a FILE*

fread() does't read from memory and store in a FILE *. fread() reads from a FILE * and stores in memory. You have it reversed. Perhaps you mean fwrite() ?


fopen/fread/fwrite are part of the C STDIO library. They deal with FILE *.

So I really don't understand your question. You said you already have the data in memory. So how did it get in memory?
0
 
LVL 35

Expert Comment

by:sarabande
ID: 35442451
so if you spotted the fseek and fread calls in the source you can replace them by your code. but you can't convert your array to a FILE* .

Sara
0
 
LVL 2

Author Comment

by:Snapples
ID: 35446726
so if you spotted the fseek and fread calls in the source you can replace them by your code. but you can't convert your array to a FILE* .

Exactly.
Currently I've fixed it this way:

    FILE* file = tmpfile();
    fwrite(data, 1, length, file);
        
    us.filestream = file;

Open in new window


Unless someone has a better suggestion?
0
 
LVL 40

Expert Comment

by:mrjoltcola
ID: 35447724
>>Currently I've fixed it this way:

The code you list writes data from the "data" variable to the filestream. Then it assigns "us.filestream = file".


So can you clarify your question?
0
 
LVL 2

Author Comment

by:Snapples
ID: 35450937
Okay, the thing is, I'm working in Objective C for the iPhone. However the library I'm using (minizip) is written in standard C. In my project I download a zip file, as soon as it finishes downloading it becomes an NSData* object in the memory. The default solution to unzip this downloaded file, according to several libraries is to write to file to the device's HDD, then open it again using the library and unzip it. As you can see, writing it to a file and opening it again is a waste since I already have the file in the memory and on a mobile device this unnecessary write and read causes a slowdown.
So instead of writing it to a file first I want to unzip the file directly from the NSData* object in the memory, from which I can easily get the byte array.

In the code I've posted, data is the byte array, which I get by calling:
void* data = [NSDataObject bytes];

So what I need is for the array of bytes to be cast as a FILE* but that doesn't seem to work. So for now I'm creating a temp file with the content of my byte array.


0
 
LVL 40

Accepted Solution

by:
mrjoltcola earned 2000 total points
ID: 35451012
Ok, this is much clearer.

So you are on the right track, until the last sentence...

>>So what I need is for the array of bytes to be cast as a FILE* but that doesn't seem to work

No, you don't want that, and you cannot do that. A FILE * is a C standard thing. You can't cast it just to make it work. It isn't a buffer, anyway. It is a complex structure for the IO layer. Forget about using it.

What you need to do is override ALL of the minizip callback functions (at least all of the ones that you are using that deal with FILE *) to use your memory buffer instead. You can see from the minizip callback function signatures that they have opaqe and stream as objects that get passed in. This is likely where you pass in your custom objects, or perhaps this is where unz_s struct gets passed, and you could make use of unz_s.filestream.

The point is, you are overriding all of the callbacks to use a memory buffer, so forget about FILE * altogether. Personally I would probably create my own custom struct that holds the "void * data" and the size of the data.



0
 
LVL 2

Author Comment

by:Snapples
ID: 35453976
Thank you for explaining, it makes sense to me now. I figured a FILE* was nothing more than a typedef with the file's actual data.
If I ever get a problem like this again I'll definitely use your solution.
But in this case it's not really worth the effort for what I need, the app will never have to update itself more once, maybe twice based on the customer. So for this case tmpfile will do since it doesn't slow the device down anyway.

Thanks for the help.
0

Featured Post

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Basic understanding on "OO- Object Orientation" is needed for designing a logical solution to solve a problem. Basic OOAD is a prerequisite for a coder to ensure that they follow the basic design of OO. This would help developers to understand the b…
Examines three attack vectors, specifically, the different types of malware used in malicious attacks, web application attacks, and finally, network based attacks.  Concludes by examining the means of securing and protecting critical systems and inf…
Video by: Grant
The goal of this video is to provide viewers with basic examples to understand and use while-loops in the C programming language.
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
Suggested Courses

810 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question