To understand what's going on we need to have a look at how all these types 
look in memory, and how they work in C. Your initial buffer type is a `ptr 
byte`, that is simply a pointer to a single 8-bit entity in memory. When you go 
to `AquireMemory` you pass a pointer to such a pointer. This is done so that 
the procedure can allocate some memory and then put the pointer to that memory 
in your variable.

Now let's have a look at `cstring`. These are defined in C as a pointer to a 
string of characters, terminated by a null byte. Remember that `char`, and 
`byte` are both 8 bit it size, and the only difference is semantic. To get the 
length of a `cstring` we have to start at the first character, and move through 
the string of characters until we find a null character.

Knowing all this we can begin to understand what's going on in your code. After 
you've written the image to the memory stream and gotten a pointer you 
erroneously treat this as a cstring. Remember that these are null terminated, 
but you have binary data which can contain null bytes anywhere. The JPEG file 
specification has some kind of four byte header followed by a null byte, that 
is what you're picking up when you try to get the length of the `cstring`. In 
fact the whole point of also passing in a pointer to a number which is set to 
the amount of bytes written is so that you know how long the output is since 
you can't count it easily yourself.

The definition of a `cstring` as a pointer to a string of data also explains 
why `ptr byte` and `buffer[0].addr` work as `cstrings`. If `buffer` is a series 
of bytes, then `buffer[0]` is the first byte, and `buffer[0].addr` is the 
pointer to this first byte in a series of bytes. `ptr byte` ys similarly set to 
point to a series of bytes somewhere in memory by the `AllocateMemory` 
function. Note of course that these aren't guaranteed to be properly set up as 
strings with a terminating null characters and no null characters within the 
series.

Now, how do we actually deal with this in Nim? First of storing binary output 
in a string is a bad idea. This output isn't text, so why should we lie and 
pretend that it is? A `seq[byte]` would be a better type to return. Although 
maybe your wrapper is using strings and you want to keep using them. First we 
have a pointer to some data and the length of data stored behind that pointer. 
You shouldn't try to second guess or verify this size, the whole point is that 
it's hard to tell how long the data output is so the library is kind enough to 
tell you. With this information you can use `newSeq[byte](length.int)` or 
`newString(length.int)` to create a buffer with enough size to hold your data. 
Then you can copy the information over from the FreeImage buffer with 
`copyMem`. Of course this causes an extra memory copy, but looking at the 
FreeImage API there doesn't seem to exist a simple way to populate a 
pre-existing buffer. You can however use the `SaveToHandle` function by 
supplying your own reader/writer implementation in order to write to a string, 
sequence, or stream.

Reply via email to