I'm testing the LMDB package and I'm stuck. When I retrieve key/value pairs they are saved to MDB_val structures:
type
Val* = object
mvSize*: csize #*< size of the data item
mvData*: pointer #*< address of the data item
I tried casting the generic pointer (mvData) to a cstring, but the data is not zero terminated so I get garbage attached. Knowing the starting address and number of bytes, what is the best way to capture the data (which is a string in my instance)? Thanks! What do you desire as the result?
When it is a Nim string, then you could create a new Nim String (newString(size)) of at least the desired size, and fill in the characters. To access the source characters, you may temporary cast your data to a cstring and access the cstring with the [] operator.
But is you want a plain C string with 0 termination -- well that is what we may get from the OS, you should not desire that. Otherwise you may need something like alloc() to allocate untraced memory.
If you think copying characters one by one is too slow -- something like low level copyMem() may be possible also, ask again...
Hi Stefan, the database I connected to was populated using strings, so no NUL terminators. If I have control of database load, I guess I could add a NUL character to the end of the input strings, which would allow me to cast to cstring on retrieval.
Thank you for the response...it was helpful! Now I need to learn about alloc() and copyMem(). :)
I guess I could add a NUL character to the end of the input strings, which would allow me to cast to cstring on retrieval.
If you can add the '0' character with which C strings are generally terminated, then you do not need alloc() and copyMem(). Bytes sequences terminated with '0' looks like regular C strings, so you should be able to cast to a C string and assign to a regular Nim string like "var str: string = $castcstring". The $ converts from cstring to nim string.
The NESM library may also give you a hint. It [de]serializes strings into|from structure you described. Documentation here
So the code will be:
from nesm import serializable
serializable:
type Value = string # Actual memory representation of serialized string equals to your structure
... # The "datasource" value is a stream that provides bytes of your structure
... # Lets assume it declared and configured over here (e.g. you may use newStringStream for string input)
let val = Value.deserialize(datasource)
echo val
The underlying implementation of the deserialize proc uses readData and copyMem (depending on type and options) and is being generated at compile time.
Something like the following should work (warning, untested code):
type
Val* = object
mvSize*: csize #*< size of the data item
mvData*: pointer #*< address of the data item
proc valToString*(v: Val): string =
result = newString(v.mvSize)
copyMem(addr result[0], v.mvData, v.mvSize)