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.