Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Finding correct filesize over 4gb in windows

I have used this c++ code to find the filesize of some files in windows(using visual studio):

(p_findFileData->nFileSizeHigh * MAXDWORD) + p_findFileData->nFileSizeLow);

This did not give me the correct filesize if the file was greater than 4gb. After some research I tried:

(p_findFileData->nFileSizeHigh * (MAXDWORD+1)) + p_findFileData->nFileSizeLow); 

as I read that nfilesizehigh and nfilesizelow are 32 bits each of 64 bit value of the filesize and if the filesize value is greater than 32 bits, we multiply maxdword(which in my case is 0xffffffff) by nfilesizehigh. The second solution also did not work and gave me a smaller size than what it was. I again tried this:

ULONGLONG FileSize = (FindFileData.nFileSizeHigh * 4294967296) + FindFileData.nFileSizeLow;

and it worked. Also I used another solution to get the file size too along with this:

 ULONGLONG FileSize = FindFileData.nFileSizeHigh;
 FileSize <<= sizeof( FindFileData.nFileSizeHigh ) *8; 
 FileSize |= FindFileData.nFileSizeLow;

The above solution works too:

I wanted to know why the first 2 solutions did not work and also the explanation for the last solution if possible as I want to know the internal working of the code. Help would be greatly appreciated.

like image 474
phantomsays Avatar asked Mar 04 '13 19:03

phantomsays


4 Answers

Use the ULARGE_INTEGER struct to combine the values, instead of trying to calculate/shift them manually:

ULARGE_INTEGER ul;
ul.HighPart = p_findFileData->nFileSizeHigh;
ul.LowPart = p_findFileData->nFileSizeLow;
ULONGLONG FileSize = ul.QuadPart;
like image 62
Remy Lebeau Avatar answered Oct 23 '22 10:10

Remy Lebeau


I would use :

ULONGLONG FileSize = (static_cast<ULONGLONG>(FindFileData.nFileSizeHigh) <<
                      sizeof(FindFileData.nFileSizeLow) *8) |
                     FindFileData.nFileSizeLow;

The cast to ULONGLONG is necessary to make the 32-bit value into a 64-bit value BEFORE it goes into the FileSize variable.

You can of course use a multiplication too - but it isn't "better" in any particular way, and most likely a tiny bit slower [probably no big deal in this particular case, but there's no benefit in using multiply that I can see.

And now for the "not working" variants:

Even if this DOES work:

 (p_findFileData->nFileSizeHigh * MAXDWORD) + p_findFileData->nFileSizeLow);

you'd get the wrong value, since MAXDWORD is one smaller than 4GB, so you end up with the wrong value [yes, it's perhaps close, but it will be at the very least 1 byte wrong, possibly a lot more]. However, since we're dealing with 32-bit values, it actually becomes:

 -p_findFileData->nFileSizeHigh + p_findFileData->nFileSizeLow;

because MAXDWORD is the same as -1 (yes, it may well be an unsigned value, but if you make the value overflow that way, it behaves just the same as a negative signed value).

This one is mathematically correct, but doesn't work due to the fact that it overflows a 32-bit value.

 (p_findFileData->nFileSizeHigh * (MAXDWORD+1)) + p_findFileData->nFileSizeLow);

So you get the low part + (0 * high part) which of course is incorrect.

With cast, this will work:

 static_cast<ULONGLONG>(p_findFileData->nFileSizeHigh) * (MAXDWORD+1) +
 p_findFileData->nFileSizeLow;
like image 30
Mats Petersson Avatar answered Oct 23 '22 12:10

Mats Petersson


(p_findFileData->nFileSizeHigh * MAXDWORD) + p_findFileData->nFileSizeLow);

This does not give the right result because MAXDWORD is the wrong value.

(p_findFileData->nFileSizeHigh * (MAXDWORD+1)) + p_findFileData->nFileSizeLow); 

This does not work because you are adding +1 to MAXDWORD before casting the type to something big enough to hold the value.

(FindFileData.nFileSizeHigh * 4294967296) + FindFileData.nFileSizeLow;

This works because 4294967296 is a 64 bit type.

FileSize <<= sizeof( FindFileData.nFileSizeHigh ) *8; 
FileSize |= FindFileData.nFileSizeLow;

This works because you shift the high bits by 32 (which is identical to multiplying by 4294967296) and then 'add' the low bits using bitwise or.

like image 32
Thomas Avatar answered Oct 23 '22 12:10

Thomas


I had exactly the same problem and the answer was really easy for my code. Just cast MAXDWORD to a 64 bit int (eg uint64_t). The explanation behind this can be found in the answers I received here.

like image 1
Ben Avatar answered Oct 23 '22 10:10

Ben