Add stew/enums and enumStrValues{Array,Seq}
This commit is contained in:
parent
4cab7b0879
commit
1f67530aa0
|
@ -0,0 +1,58 @@
|
|||
import
|
||||
macros, sequtils
|
||||
|
||||
macro enumRangeInt64*(a: type[enum]): untyped =
|
||||
## This macro returns an array with all the ordinal values of an enum
|
||||
let
|
||||
values = a.getType[1][1..^1]
|
||||
valuesOrded = values.mapIt(newCall("int64", it))
|
||||
newNimNode(nnkBracket).add(valuesOrded)
|
||||
|
||||
macro enumStrValuesArrayImpl(a: type[enum]): untyped =
|
||||
## This macro returns an array with all the ordinal values of an enum
|
||||
let
|
||||
values = a.getType[1][1..^1]
|
||||
valuesOrded = values.mapIt(newCall("$", it))
|
||||
newNimNode(nnkBracket).add(valuesOrded)
|
||||
|
||||
# TODO: This should be a proc returning a lent view over the
|
||||
# const value. This will ensure only a single instace
|
||||
# of the array is generated.
|
||||
template enumStrValuesArray*(E: type[enum]): auto =
|
||||
const values = enumStrValuesArrayImpl E
|
||||
values
|
||||
|
||||
# TODO: This should be a proc returning a lent view over the
|
||||
# const value. This will ensure only a single instace
|
||||
# of the sequence is generated.
|
||||
template enumStrValuesSeq*(E: type[enum]): seq[string] =
|
||||
const values = @(enumStrValuesArray E)
|
||||
values
|
||||
|
||||
macro hasHoles*(T: type[enum]): bool =
|
||||
# As an enum is always sorted, just substract the first and the last ordinal value
|
||||
# and compare the result to the number of element in it will do the trick.
|
||||
let len = T.getType[1].len - 2
|
||||
|
||||
quote: `T`.high.ord - `T`.low.ord != `len`
|
||||
|
||||
proc contains*[I: SomeInteger](e: type[enum], v: I): bool =
|
||||
when I is uint64:
|
||||
if v > int.high.uint64:
|
||||
return false
|
||||
when e.hasHoles():
|
||||
v.int64 in enumRangeInt64(e)
|
||||
else:
|
||||
v.int64 in e.low.int64 .. e.high.int64
|
||||
|
||||
func checkedEnumAssign*[E: enum, I: SomeInteger](res: var E, value: I): bool =
|
||||
## This function can be used to safely assign a tainted integer value (coming
|
||||
## from untrusted source) to an enum variable. The function will return `true`
|
||||
## if the integer value is within the acceped values of the enum and `false`
|
||||
## otherwise.
|
||||
|
||||
if value notin E:
|
||||
return false
|
||||
|
||||
res = E value
|
||||
return true
|
|
@ -1,6 +1,11 @@
|
|||
import
|
||||
macros,
|
||||
sequtils
|
||||
macros, sequtils
|
||||
|
||||
import
|
||||
enums
|
||||
|
||||
export
|
||||
enums
|
||||
|
||||
template init*(lvalue: var auto) =
|
||||
mixin init
|
||||
|
@ -68,41 +73,6 @@ proc baseType*(obj: RootObj): cstring =
|
|||
proc baseType*(obj: ref RootObj): cstring =
|
||||
obj[].baseType
|
||||
|
||||
macro enumRangeInt64*(a: type[enum]): untyped =
|
||||
## This macro returns an array with all the ordinal values of an enum
|
||||
let
|
||||
values = a.getType[1][1..^1]
|
||||
valuesOrded = values.mapIt(newCall("int64", it))
|
||||
newNimNode(nnkBracket).add(valuesOrded)
|
||||
|
||||
macro hasHoles*(T: type[enum]): bool =
|
||||
# As an enum is always sorted, just substract the first and the last ordinal value
|
||||
# and compare the result to the number of element in it will do the trick.
|
||||
let len = T.getType[1].len - 2
|
||||
|
||||
quote: `T`.high.ord - `T`.low.ord != `len`
|
||||
|
||||
proc contains*[I: SomeInteger](e: type[enum], v: I): bool =
|
||||
when I is uint64:
|
||||
if v > int.high.uint64:
|
||||
return false
|
||||
when e.hasHoles():
|
||||
v.int64 in enumRangeInt64(e)
|
||||
else:
|
||||
v.int64 in e.low.int64 .. e.high.int64
|
||||
|
||||
func checkedEnumAssign*[E: enum, I: SomeInteger](res: var E, value: I): bool =
|
||||
## This function can be used to safely assign a tainted integer value (coming
|
||||
## from untrusted source) to an enum variable. The function will return `true`
|
||||
## if the integer value is within the acceped values of the enum and `false`
|
||||
## otherwise.
|
||||
|
||||
if value notin E:
|
||||
return false
|
||||
|
||||
res = E value
|
||||
return true
|
||||
|
||||
func isZeroMemory*[T](x: T): bool =
|
||||
# TODO: iterate over words here
|
||||
for b in cast[ptr array[sizeof(T), byte]](unsafeAddr x)[]:
|
||||
|
|
|
@ -20,6 +20,7 @@ import
|
|||
test_byteutils,
|
||||
test_ctops,
|
||||
test_endians2,
|
||||
test_enums,
|
||||
test_io2,
|
||||
test_keyed_queue,
|
||||
test_sorted_set,
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
import
|
||||
unittest, typetraits,
|
||||
../stew/enums
|
||||
|
||||
suite "enums":
|
||||
test "enumRangeInt64":
|
||||
type
|
||||
WithoutHoles = enum
|
||||
A1, A2, A3
|
||||
WithoutHoles2 = enum
|
||||
B1 = 4, B2 = 5, B3 = 6
|
||||
WithHoles = enum
|
||||
C1 = 1, C2 = 3, C3 = 5
|
||||
|
||||
check:
|
||||
enumRangeInt64(WithoutHoles) == [ 0'i64, 1, 2 ]
|
||||
enumRangeInt64(WithoutHoles2) == [ 4'i64, 5, 6 ]
|
||||
enumRangeInt64(WithHoles) == [ 1'i64, 3, 5 ]
|
||||
|
||||
test "enumStringValues":
|
||||
type
|
||||
RegularEnum = enum
|
||||
A1, A2, A3
|
||||
|
||||
EnumWithHoles = enum
|
||||
C1 = 1, C2 = 3, C3 = 5
|
||||
|
||||
StringyEnum = enum
|
||||
A = "value A"
|
||||
B = "value B"
|
||||
|
||||
check:
|
||||
enumStrValuesArray(RegularEnum) == ["A1", "A2", "A3"]
|
||||
enumStrValuesArray(EnumWithHoles) == ["C1", "C2", "C3"]
|
||||
enumStrValuesArray(StringyEnum) == ["value A", "value B"]
|
||||
|
||||
enumStrValuesSeq(RegularEnum) == @["A1", "A2", "A3"]
|
||||
enumStrValuesSeq(EnumWithHoles) == @["C1", "C2", "C3"]
|
||||
enumStrValuesSeq(StringyEnum) == @["value A", "value B"]
|
||||
|
||||
test "contains":
|
||||
type
|
||||
WithoutHoles = enum
|
||||
A1, A2, A3
|
||||
WithoutHoles2 = enum
|
||||
B1 = 4, B2 = 5, B3 = 6
|
||||
WithHoles = enum
|
||||
C1 = 1, C2 = 3, C3 = 5
|
||||
WithoutHoles3 = enum
|
||||
D1 = -1, D2 = 0, D3 = 1
|
||||
WithHoles2 = enum
|
||||
E1 = -5, E2 = 0, E3 = 5
|
||||
|
||||
check:
|
||||
1 in WithoutHoles
|
||||
5 notin WithoutHoles
|
||||
1 notin WithoutHoles2
|
||||
5 in WithoutHoles2
|
||||
1 in WithHoles
|
||||
2 notin WithHoles
|
||||
6 notin WithHoles
|
||||
5 in WithHoles
|
||||
1.byte in WithoutHoles
|
||||
4294967295'u32 notin WithoutHoles3
|
||||
-1.int8 in WithoutHoles3
|
||||
-4.int16 notin WithoutHoles3
|
||||
-5.int16 in WithHoles2
|
||||
5.uint64 in WithHoles2
|
||||
-12.int8 notin WithHoles2
|
||||
int64.high notin WithoutHoles
|
||||
int64.high notin WithHoles
|
||||
int64.low notin WithoutHoles
|
||||
int64.low notin WithHoles
|
||||
int64.high.uint64 * 2 notin WithoutHoles
|
||||
int64.high.uint64 * 2 notin WithHoles
|
||||
|
||||
test "hasHoles":
|
||||
type
|
||||
EnumWithOneValue = enum
|
||||
A0
|
||||
|
||||
WithoutHoles = enum
|
||||
A1, B1, C1
|
||||
|
||||
WithoutHoles2 = enum
|
||||
A2 = 2, B2 = 3, C2 = 4
|
||||
|
||||
WithHoles = enum
|
||||
A3, B3 = 2, C3
|
||||
|
||||
WithBigHoles = enum
|
||||
A4 = 0, B4 = 2000, C4 = 4000
|
||||
|
||||
check:
|
||||
hasHoles(EnumWithOneValue) == false
|
||||
hasHoles(WithoutHoles) == false
|
||||
hasHoles(WithoutHoles2) == false
|
||||
hasHoles(WithHoles) == true
|
||||
hasHoles(WithBigHoles) == true
|
||||
|
||||
test "checkedEnumAssign":
|
||||
type
|
||||
SomeEnum = enum
|
||||
A1, B1, C1
|
||||
|
||||
AnotherEnum = enum
|
||||
A2 = 2, B2, C2
|
||||
|
||||
EnumWithHoles = enum
|
||||
A3, B3 = 3, C3
|
||||
var
|
||||
e1 = A1
|
||||
e2 = A2
|
||||
e3 = A3
|
||||
|
||||
check:
|
||||
checkedEnumAssign(e1, 2)
|
||||
e1 == C1
|
||||
not checkedEnumAssign(e1, 5)
|
||||
e1 == C1
|
||||
checkedEnumAssign(e1, 0)
|
||||
e1 == A1
|
||||
not checkedEnumAssign(e1, -1)
|
||||
e1 == A1
|
||||
|
||||
checkedEnumAssign(e2, 2)
|
||||
e2 == A2
|
||||
not checkedEnumAssign(e2, 5)
|
||||
e2 == A2
|
||||
checkedEnumAssign(e2, 4)
|
||||
e2 == C2
|
||||
not checkedEnumAssign(e2, 1)
|
||||
e2 == C2
|
||||
|
||||
checkedEnumAssign(e3, 4)
|
||||
e3 == C3
|
||||
not checkedEnumAssign(e3, 1)
|
||||
e3 == C3
|
||||
checkedEnumAssign(e3, 0)
|
||||
e3 == A3
|
||||
not checkedEnumAssign(e3, -1)
|
||||
e3 == A3
|
||||
|
|
@ -70,125 +70,6 @@ suite "Objects":
|
|||
T6 is DistinctBar
|
||||
T6 isnot Bar
|
||||
|
||||
test "enumRangeInt64":
|
||||
type
|
||||
WithoutHoles = enum
|
||||
A1, A2, A3
|
||||
WithoutHoles2 = enum
|
||||
B1 = 4, B2 = 5, B3 = 6
|
||||
WithHoles = enum
|
||||
C1 = 1, C2 = 3, C3 = 5
|
||||
|
||||
check:
|
||||
enumRangeInt64(WithoutHoles) == [ 0'i64, 1, 2 ]
|
||||
enumRangeInt64(WithoutHoles2) == [ 4'i64, 5, 6 ]
|
||||
enumRangeInt64(WithHoles) == [ 1'i64, 3, 5 ]
|
||||
|
||||
|
||||
test "contains":
|
||||
type
|
||||
WithoutHoles = enum
|
||||
A1, A2, A3
|
||||
WithoutHoles2 = enum
|
||||
B1 = 4, B2 = 5, B3 = 6
|
||||
WithHoles = enum
|
||||
C1 = 1, C2 = 3, C3 = 5
|
||||
WithoutHoles3 = enum
|
||||
D1 = -1, D2 = 0, D3 = 1
|
||||
WithHoles2 = enum
|
||||
E1 = -5, E2 = 0, E3 = 5
|
||||
|
||||
check:
|
||||
1 in WithoutHoles
|
||||
5 notin WithoutHoles
|
||||
1 notin WithoutHoles2
|
||||
5 in WithoutHoles2
|
||||
1 in WithHoles
|
||||
2 notin WithHoles
|
||||
6 notin WithHoles
|
||||
5 in WithHoles
|
||||
1.byte in WithoutHoles
|
||||
4294967295'u32 notin WithoutHoles3
|
||||
-1.int8 in WithoutHoles3
|
||||
-4.int16 notin WithoutHoles3
|
||||
-5.int16 in WithHoles2
|
||||
5.uint64 in WithHoles2
|
||||
-12.int8 notin WithHoles2
|
||||
int64.high notin WithoutHoles
|
||||
int64.high notin WithHoles
|
||||
int64.low notin WithoutHoles
|
||||
int64.low notin WithHoles
|
||||
int64.high.uint64 * 2 notin WithoutHoles
|
||||
int64.high.uint64 * 2 notin WithHoles
|
||||
|
||||
|
||||
test "hasHoles":
|
||||
type
|
||||
EnumWithOneValue = enum
|
||||
A0
|
||||
|
||||
WithoutHoles = enum
|
||||
A1, B1, C1
|
||||
|
||||
WithoutHoles2 = enum
|
||||
A2 = 2, B2 = 3, C2 = 4
|
||||
|
||||
WithHoles = enum
|
||||
A3, B3 = 2, C3
|
||||
|
||||
WithBigHoles = enum
|
||||
A4 = 0, B4 = 2000, C4 = 4000
|
||||
|
||||
check:
|
||||
hasHoles(EnumWithOneValue) == false
|
||||
hasHoles(WithoutHoles) == false
|
||||
hasHoles(WithoutHoles2) == false
|
||||
hasHoles(WithHoles) == true
|
||||
hasHoles(WithBigHoles) == true
|
||||
|
||||
test "checkedEnumAssign":
|
||||
type
|
||||
SomeEnum = enum
|
||||
A1, B1, C1
|
||||
|
||||
AnotherEnum = enum
|
||||
A2 = 2, B2, C2
|
||||
|
||||
EnumWithHoles = enum
|
||||
A3, B3 = 3, C3
|
||||
var
|
||||
e1 = A1
|
||||
e2 = A2
|
||||
e3 = A3
|
||||
|
||||
check:
|
||||
checkedEnumAssign(e1, 2)
|
||||
e1 == C1
|
||||
not checkedEnumAssign(e1, 5)
|
||||
e1 == C1
|
||||
checkedEnumAssign(e1, 0)
|
||||
e1 == A1
|
||||
not checkedEnumAssign(e1, -1)
|
||||
e1 == A1
|
||||
|
||||
checkedEnumAssign(e2, 2)
|
||||
e2 == A2
|
||||
not checkedEnumAssign(e2, 5)
|
||||
e2 == A2
|
||||
checkedEnumAssign(e2, 4)
|
||||
e2 == C2
|
||||
not checkedEnumAssign(e2, 1)
|
||||
e2 == C2
|
||||
|
||||
checkedEnumAssign(e3, 4)
|
||||
e3 == C3
|
||||
not checkedEnumAssign(e3, 1)
|
||||
e3 == C3
|
||||
checkedEnumAssign(e3, 0)
|
||||
e3 == A3
|
||||
not checkedEnumAssign(e3, -1)
|
||||
e3 == A3
|
||||
|
||||
test "isZeroMemory":
|
||||
type
|
||||
Foo = object
|
||||
|
|
Loading…
Reference in New Issue