I'm having trouble using a set variable based on an enum type that has gaps between values. I'm trying to implement a binary protocol where some of the information is represented as flags. However, some of these flags skip over the next bit or three or are obsolete/unused. Not only that, but some of the bitflag data is 4 bytes in size. I want to know if there's any way to make the following code work or if the only option is to use consts. It currently spits out a much larger file than 4 bytes.
import streams
type
HoleyMoley {.pure, size: 4.} = enum
someFlags = 1,
areOk = 2,
butOthers = 16,
notReally = 0x8000
var userFlags: set[HoleyMoley] = {HoleyMoley.someFlags, HoleyMoley.notReally}
let f = newFileStream("JustTheFlags.dat", fmWrite)
A set is a bitvector. Having 0x8000 as a possible value means that your bitvector needs to have at least 0x8000 bits.
I think you rather want to have something like this:
type HoleyMoley = enum
someFlags = 0, # will set the bit 2^0
areOk = 1, # will set the bit 2^1
butOthers = 4, # will set the bit 2^4
notReally = 15 # will set the bit 2^15
Something like this?
var hm = {HoleyMoley.areOk, HoleyMoley.notReally}
var f = newFileStream("hm.dat", fmWrite)
f.write(hm)
f.close()
I thought that creates a set[HoleyMoley] though?
Yes, the set represents the active flags.
A set in Nim is represented as a bit array. That means that, say, the set of numbers 1, 2, and 5 will be represented with the bit pattern 00100110. The whole set of your flags will be 1000000000010011.
What is missing is how to convert from the bytes in your binary stream to the equivalent set, and back. I am no sure how to do this - there should be an appropriate constructor but I could not find any
Yes, the set is the collection of flags. Example:
type Foo {.size: 2, pure.} = enum
first = 1,
second = 2,
third = 3
echo "sizeof(set[Foo]) = ", sizeof(set[Foo])
echo "{Foo.first} = ", cast[int8]({Foo.first})
echo "{Foo.second, Foo.third} = ", cast[int8]({Foo.second, Foo.third})
echo "{} = ", cast[int8](set[Foo]({}))
Output:
sizeof(set[Foo]) = 1
{Foo.first} = 1
{Foo.second, Foo.third} = 6
{} = 0
Where: 1 = 2^0, 6 = 2^1 + 2^2, 0 = 0 (no flag given)
Note that the size of the set is 1, because the compiler notes that it needs just 1 byte to fit all the values of Foo. You can force it to be 4 bytes by including higher values in the enum:
type Foo {.size: 2, pure.} = enum
first = 1,
second = 2,
third = 3,
unused = 31
echo "sizeof(set[Foo]) = ", sizeof(set[Foo])
echo "{Foo.first} = ", cast[int32]({Foo.first})
echo "{Foo.second, Foo.third} = ", cast[int32]({Foo.second, Foo.third})
echo "{} = ", cast[int32](set[Foo]({}))
As the compiler needs 32 bits to fit unused in the set, the set has a size of 4 bytes (= 32 bits).