I'm using LMDB to store objects serialized by the excellent NESM library. Unfortunately, LMDB wants its data to be of type void*, and my serialized objects are all strings. At first, this wasn't a concern - I just set the data field to
addr myString
Unfortunately though, this doesn't work. I was able to get farther by creating a fixed array of characters and then filling that in with the information in my string like so:
proc asArray*(s: string, arr: var openarray[char]): void =
if s.len < arr.len:
for i, c in s:
arr[i] = c
else:
quit("Not enough space available for that!", -1)
but this assumes that whatever array I'm referencing is of a fixed length. While this approach does work, I would rather not create a one size fits all array[5000, char] if most of the time my strings are quite small.
I know there has to be an easy approach, I'm just not familiar enough with Nim and how it interfaces with C to invoke the correct magic via string or cstring alone. Any help would be greatly appreciated!
say you have a C function:
void store(void* data);
then you can using Nim ffi like this:
proc store(data: pointer) {.importc, dynlib: "shared lib name".}
or pretend that the argument is a cstring
proc store(data: cstring) {.importc, dynlib: "shared lib name".}
how to use the first one?
var mydata = "something"
store(mydata[0].addr) #get the underlying char* address
#or
store(mydata.cstring) # implicit cast from char* to void*
#or
store(cast[pointer](mydata.cstring)) #unnecessary, but ok too
how to use the second one?
var mydata = "something"
store(mydata) #use Nim's automatic conversion from string to cstring
#or
store(mydata.cstring) #unnecessary, but it's ok too.
if your C function is something like:
void store(void* data, int len);
then wrap it like this:
proc c_store(data: pointer, len: cint) {.importc:"store", dynlib: "shared lib name".}
# use ordinary wrapper
proc store(data: string) =
c_store(data.cstring, data.len.cint)
#or use template to minimize overhead
template store(data: string) =
c_store(data.cstring, data.len.cint)
#or use inline proc
proc store(data: string) {.inline.} =
c_store(data.cstring, data.len.cint)
Wow, that was an excellent response and the simple myString.cstring seems to be doing exactly what I needed. But now I'm looking to turn a void* into a string. To this end I've tried var myString = cast[ptr string](data)[], but this generates a segment fault:
Program received signal SIGSEGV, Segmentation fault.
0x000000000040cf9c in copyString (src=0x1) at /home/steve/nim-0.17.0/lib/system/sysstr.nim:93
93 if (src.reserved and seqShallowFlag) != 0:
I feel like I'm really close to resolving this. If anyone can point me toward what I'm missing, I would really appreciate it!
Untested:
var myString = $ cast[cstring](data)
This solution looks like it may do the trick. Unfortunately, when I had serialized my object to its string format, it had some non-standard content:
0x7ffff6dd4fee: 1 0 0 0 0 0 0 0
0x7ffff6dd4ff6: 42 0 0 0 0 0 96 106
0x7ffff6dd4ffe: 71
Is there a good way (apart from building a sequence up byte by byte and then converting it to a string) to tell Nim that this 17 byte buffer should be read as a string?
Okay, this seems to work great for converting pointers to Nim strings:
proc fromCString(p: pointer, len: int): string =
result = newStringOfCap(len)
var lowLevel = cast[cstring](p)
for i in 0 ..< len:
var character: char = lowLevel[i]
add(result, character)
Many thanks to the individuals who contributed in this thread!
variations to convert C pointer to Nim string, and perhaps faster too:
proc fromCString(p: pointer, len: int): string =
result = newString(len)
for i in 0.. <len:
result[i] = cast[cstring](p)[i]
or
proc fromCString(p: pointer, len: int): string =
result = newString(len)
copyMem(result.cstring, p, len)
Wow Jangko, I might just have to use that second example you provided. I can't imagine coming up with code that would do the job more efficiently!
It's kind of funny that in your first example you used cast[cstring](p)[i]. I was trying to get something similar to work for me but must have been slightly off on my syntax. This thread has been a very valuable one for me, and I'm thankful that the community is so knowledgeable and friendly. Thank you!