Trying to write and read IpAddress from/to disk via readData(x.addr, x.sizeof) and write(x) seems to be flakey when both Ipv4 and IPv6 addresses are used. Using IPv4 alone seems to work.
Also, (using Nim 0.19.1/linux) I can't set the address_vX fields of IpAddress ("cannot prove ... field is accessible [ProveField]") although those fields are public.
Finally, it might be helpful to have an initializer from (open)array (matching the address_vX field) for IpAddress
Disclaimer: Maybe I'm just mistaken. If so, sorry, and please enlighten me.
The test code is a simple fstream.readData(ipa.addr, ipa.sizeoff) with ipa being an IpAddress and a prior fstream.write(ipa).
The problem seems not to appear in very simple code like write one IP then another one and then a third one ... and then read them back just as simply. In my real world case though the IP addresses are part of a struct/object with other data before and after it. And then I works (well, at least with a small number of test entries) if using only IPv4 but breaks when there are mixed IP addresses.
My assumption is that there is a problem somewhere deep down in Nims magic.
Reason: Looking at the file created I notice IpAddress (no matter which type) are written simply as a family byte (1 for IPv4 and 0 for IPv6) followed by the 4 or 16 IP address bytes. For strings it's even more magic as there is no length written out but just the bytes of the string.
One (as a user) can, of course work around that, e.g. by writing IP addresses out as strings (and let Nim's magic take care of it) and reading them back as strings too. Another way would be to write the length (in bytes) of the IP address somewhere in front of the address bytes. Certainly there are other ways too.
This issue has me thinking quite a bit and one of the points I came up with is that IMHO using 0 (first elem in family enum) as discriminator for IPv6 (and 1 for IPv4) might not be the smartest thing to do. It seems to me that using 4 and 16 (the byte array length of the addresses) would be useful and allow for some smart stuff like saving an implicit if.
Moreover it would be desirable to have real discriminated structures/objects to allow for one address field rather than address_v4 and address_v6 complicating life elsewhere.
Please note that the problem might be perfectly well on my side. I find myself still quite often thinking in C or Ada and quite possibly just didn't get the full hang of Nim yet and/or made an error that shows as an IpAddress related problem but actually isn't.
I found the problem worthy of mentioning anyway, if only because at the very minimum it seems to me that I got bitten by an unlucky combination of still very much lacking docs and Nim magic deep down where we users usually don't look.
The test code is a simple fstream.readData(ipa.addr, ipa.sizeof) with ipa being a var IpAddress
I would be very carefully doing that, as IpAddress seems to be a Sum Type/UniounType/Object variant. It may work without problems, just I am not sure.
Yes, that's in part what this (and, I assume, the problem) is about. I did study the doc (and the source) carefully.
More docs on Nim "toBinary" and "fromBinary" magic in types, used. eg. in FileStream read and write would be urgently needed IMHO.
Update:
Now it works (with my real world code). Kind of. But now the problem has shifted to a string field right after the IpAddress field of my struct/object.
As I need to be productive I can't afford to lose a lot of time digging through Nim's libraries' sources (again: Docs! We need good docs if we want Nim to become a language for professional use!) and experimenting.
So I got it stable with a quick hack. Nim's filestream.write(someString) does not write out the string length, so I simply write 2 bytes (enough for my case) with the length of the string field myself and upon reading it from file I read that length, do a mystr.setLen(tmpLen) plus when reading the string in the next step I don't use mystr.sizeof but tmpLen as parameter for the read proc. Possibly not needed after the setLen() but as I said I can't afford long experiments and I need reliable code.
(again: Docs! We need good docs if we want Nim to become a language for professional use!)
Note: Using low level routines like fstream.readData() with addr() for higher level data like objects or strings is newer a good decision. Internals of these data structures may change without notice, for strings that recently occurs. Even if such internals are documented I would prefer for my application code not to rely on that. Are there no higher level modules that you may use, maybe writing JSON? I don't know, as I have not yet needed writing something to disk yet.
I'm a human. Of course I use higher level functionality when available and adequate. I'm no less lazy than others - g
For configs for example I use high level modules (like json). But for some things the ugly old way of using low level binary writing is needed.
Btw: I'm less afraid than many because crypto is a major part of my work and that is low level by its very nature. Also of importance (well, to me): that's one of Nim's strengths; it can serve at a level almost as low a C as well as on a level not far from Python. If I had to choose the latter is very nice and great for productivity but the former is conditio sine qua non.
IMHO the real solution to that problem field would be to have something like "toBinary" and "fromBinary" for all types returning a byte array or seq. But I'm not complaining; I guess comes time come (stable) solutions.
I'm not familiar with the IP object but given it's definition:
type IpAddress = object
case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6)
of IpAddressFamily.IPv6:
address_v6*: array[0 .. 15, uint8] ## Contains the IP address in bytes in
## case of IPv6
of IpAddressFamily.IPv4:
address_v4*: array[0 .. 3, uint8] ## Contains the IP address in bytes in
## case of IPv4
You should use this:
case ip.family
of IPv6:
fstream.readData(ip.address_v6[0].addr, ip.address_v6.len)
of IPv4:
fstream.readData(ip.address_v4[0].addr, ip.address_v4.len)
Object variants use a discrimining first byte + the size of the biggest contained object so 1+16 bytes here.
This follows C representation but you can't rely on it. Also C compilers are free to leave garbage in [4 .. 15] if you are on IPv4 branch.
Yes that's one way but not what I want. I mean, come on, it's not something exotic to binary read/write from/to disk.
Anyway, I have found a working solution now after a lot of research. Thank you all for sharing your thoughts.
Anyway, I have found a working solution now after a lot of research
...and as a good citizen, I will post it here so when somebody after me struggles with the same issue, can see how I managed to solve it.
I would always have to look up the forum syntax how to do that.
the markdown code block is used literally on every forum, IM or any place where you can leave a comment.