diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0172dbce..11b6c02d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -155,6 +155,7 @@ jobs: fi nim --version nimble --version + nimble install -y --depsOnly env TEST_LANG=c nimble test env TEST_LANG=cpp nimble test @@ -173,5 +174,6 @@ jobs: - name: Test using VCC if: runner.os == 'Windows' run: | + export NIMFLAGS="-d:nimRawSetjmp" env TEST_LANG=c nimble testvcc env TEST_LANG=cpp nimble testvcc diff --git a/stew.nimble b/stew.nimble index d2fdbe22..1a52c25b 100644 --- a/stew.nimble +++ b/stew.nimble @@ -7,7 +7,8 @@ description = "Backports, standard library candidates and small utilities that license = "Apache License 2.0" skipDirs = @["tests"] -requires "nim >= 1.2.0" +requires "nim >= 1.2.0", + "unittest2" ### Helper functions proc test(args, path: string) = diff --git a/stew/enums.nim b/stew/enums.nim new file mode 100644 index 00000000..e5bd11b6 --- /dev/null +++ b/stew/enums.nim @@ -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 diff --git a/stew/objects.nim b/stew/objects.nim index cbf561ca..975f856a 100644 --- a/stew/objects.nim +++ b/stew/objects.nim @@ -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)[]: diff --git a/tests/all_tests.nim b/tests/all_tests.nim index 31b836ef..c0e6cb73 100644 --- a/tests/all_tests.nim +++ b/tests/all_tests.nim @@ -20,6 +20,7 @@ import test_byteutils, test_ctops, test_endians2, + test_enums, test_io2, test_keyed_queue, test_sorted_set, diff --git a/tests/ranges/tbitranges.nim b/tests/ranges/tbitranges.nim index 3589b61d..2467cc4c 100644 --- a/tests/ranges/tbitranges.nim +++ b/tests/ranges/tbitranges.nim @@ -1,5 +1,6 @@ import - random, unittest, + random, + unittest2, ../../stew/ranges/bitranges, ../../stew/bitseqs proc randomBytes(n: int): seq[byte] = diff --git a/tests/ranges/tstackarrays.nim b/tests/ranges/tstackarrays.nim index 45c89669..18f41285 100644 --- a/tests/ranges/tstackarrays.nim +++ b/tests/ranges/tstackarrays.nim @@ -1,5 +1,6 @@ import - unittest, math, + math, + unittest2, ../../stew/ptrops, ../../stew/ranges/[stackarrays] diff --git a/tests/ranges/ttypedranges.nim b/tests/ranges/ttypedranges.nim index d99c0b34..1e7ac6b0 100644 --- a/tests/ranges/ttypedranges.nim +++ b/tests/ranges/ttypedranges.nim @@ -1,5 +1,6 @@ import - unittest, sets, + sets, + unittest2, ../../stew/ranges/[typedranges, ptr_arith] suite "Typed ranges": diff --git a/tests/test_arrayops.nim b/tests/test_arrayops.nim index ccc8f2f5..f1d7dc50 100644 --- a/tests/test_arrayops.nim +++ b/tests/test_arrayops.nim @@ -10,7 +10,7 @@ {.used.} import - std/unittest, + unittest2, ../stew/arrayops suite "arrayops": diff --git a/tests/test_assign2.nim b/tests/test_assign2.nim index f5ebe268..1defc905 100644 --- a/tests/test_assign2.nim +++ b/tests/test_assign2.nim @@ -1,5 +1,5 @@ import - std/unittest, + unittest2, ../stew/assign2 suite "assign2": diff --git a/tests/test_base10.nim b/tests/test_base10.nim index 81a3ba20..4273abca 100644 --- a/tests/test_base10.nim +++ b/tests/test_base10.nim @@ -1,5 +1,6 @@ -import unittest -import ../stew/base10 +import + unittest2, + ../stew/base10 when defined(nimHasUsed): {.used.} diff --git a/tests/test_base32.nim b/tests/test_base32.nim index 7d34dfd4..1372c0cd 100644 --- a/tests/test_base32.nim +++ b/tests/test_base32.nim @@ -1,5 +1,6 @@ -import unittest -import ../stew/base32 +import + unittest2, + ../stew/base32 when defined(nimHasUsed): {.used.} diff --git a/tests/test_base58.nim b/tests/test_base58.nim index 8309cb38..becd7e6e 100644 --- a/tests/test_base58.nim +++ b/tests/test_base58.nim @@ -1,5 +1,6 @@ -import unittest -import ../stew/base58 +import + unittest2, + ../stew/base58 when defined(nimHasUsed): {.used.} diff --git a/tests/test_base64.nim b/tests/test_base64.nim index 34d37c27..2e4d9c1c 100644 --- a/tests/test_base64.nim +++ b/tests/test_base64.nim @@ -1,5 +1,6 @@ -import unittest -import ../stew/[base64, byteutils] +import + unittest2, + ../stew/[base64, byteutils] when defined(nimHasUsed): {.used.} diff --git a/tests/test_bitops2.nim b/tests/test_bitops2.nim index 1f81217c..422b1fba 100644 --- a/tests/test_bitops2.nim +++ b/tests/test_bitops2.nim @@ -1,6 +1,6 @@ -import unittest - -import ../stew/bitops2 +import + unittest2, + ../stew/bitops2 template test() = doAssert bitsof(8'u8) == 8 diff --git a/tests/test_bitseqs.nim b/tests/test_bitseqs.nim index bbc06b98..f474e362 100644 --- a/tests/test_bitseqs.nim +++ b/tests/test_bitseqs.nim @@ -1,5 +1,6 @@ import - unittest, strformat, + strformat, + unittest2, ../stew/[bitseqs] suite "Bit fields": diff --git a/tests/test_byteutils.nim b/tests/test_byteutils.nim index c1a6f7c8..bdfe7506 100644 --- a/tests/test_byteutils.nim +++ b/tests/test_byteutils.nim @@ -6,7 +6,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - std/unittest, + unittest2, ../stew/byteutils proc compilationTest {.exportc: "compilationTest".} = diff --git a/tests/test_ctops.nim b/tests/test_ctops.nim index f44d7461..30c65f47 100644 --- a/tests/test_ctops.nim +++ b/tests/test_ctops.nim @@ -1,5 +1,6 @@ -import unittest -import ../stew/ctops +import + unittest2, + ../stew/ctops suite "Constant-time operations test suite": test "isEqual() test": diff --git a/tests/test_endians2.nim b/tests/test_endians2.nim index 5971f30a..f0b0d982 100644 --- a/tests/test_endians2.nim +++ b/tests/test_endians2.nim @@ -1,6 +1,6 @@ -import unittest - -import ../stew/endians2 +import + unittest2, + ../stew/endians2 template test() = doAssert 0x01'u8.toBytesBE == [0x01'u8] diff --git a/tests/test_enums.nim b/tests/test_enums.nim new file mode 100644 index 00000000..f76c0db1 --- /dev/null +++ b/tests/test_enums.nim @@ -0,0 +1,144 @@ +import + typetraits, + unittest2, + ../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 + diff --git a/tests/test_interval_set.nim b/tests/test_interval_set.nim index 726b963b..5b363df5 100644 --- a/tests/test_interval_set.nim +++ b/tests/test_interval_set.nim @@ -10,7 +10,7 @@ # distributed except according to those terms. import - std/unittest, + unittest2, ../stew/interval_set const diff --git a/tests/test_io2.nim b/tests/test_io2.nim index c943fc78..7af968f0 100644 --- a/tests/test_io2.nim +++ b/tests/test_io2.nim @@ -1,5 +1,6 @@ -import unittest -import ../stew/io2 +import + unittest2, + ../stew/io2 suite "OS Input/Output procedures test suite": test "getCurrentDir() test": diff --git a/tests/test_keyed_queue.nim b/tests/test_keyed_queue.nim index f7cb974a..ad537f80 100644 --- a/tests/test_keyed_queue.nim +++ b/tests/test_keyed_queue.nim @@ -9,7 +9,8 @@ # according to those terms. import - std/[algorithm, sequtils, strformat, strutils, tables, unittest], + std/[algorithm, sequtils, strformat, strutils, tables], + unittest2, ../stew/keyed_queue, ../stew/keyed_queue/kq_debug diff --git a/tests/test_leb128.nim b/tests/test_leb128.nim index 0f7d11a1..9e72fc36 100644 --- a/tests/test_leb128.nim +++ b/tests/test_leb128.nim @@ -1,5 +1,6 @@ import - unittest, random, + random, + unittest2, ../stew/[byteutils, leb128, results] const edgeValues = { diff --git a/tests/test_macros.nim b/tests/test_macros.nim index b7d2c881..9dfc0594 100644 --- a/tests/test_macros.nim +++ b/tests/test_macros.nim @@ -1,8 +1,7 @@ import - unittest, + unittest2, ../stew/shims/macros - template unknown() {.pragma.} template zero() {.pragma.} template one(one: string) {.pragma.} diff --git a/tests/test_objects.nim b/tests/test_objects.nim index 78386ee7..4e919603 100644 --- a/tests/test_objects.nim +++ b/tests/test_objects.nim @@ -1,5 +1,6 @@ import - unittest, typetraits, + typetraits, + unittest2, ../stew/objects when defined(nimHasUsed): @@ -70,125 +71,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 diff --git a/tests/test_ptrops.nim b/tests/test_ptrops.nim index f78da613..5ce71efb 100644 --- a/tests/test_ptrops.nim +++ b/tests/test_ptrops.nim @@ -7,9 +7,9 @@ # # at your option. This file may not be copied, modified, or distributed except according to those terms. -import unittest - -import ../stew/ptrops +import + unittest2, + ../stew/ptrops var ints = [2, 3, 4] diff --git a/tests/test_sequtils2.nim b/tests/test_sequtils2.nim index 3c52f106..d6066c25 100644 --- a/tests/test_sequtils2.nim +++ b/tests/test_sequtils2.nim @@ -6,7 +6,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - std/unittest, + unittest2, ../stew/sequtils2 suite "sequtils2": diff --git a/tests/test_sets.nim b/tests/test_sets.nim index 5ee67350..bd0b8c04 100644 --- a/tests/test_sets.nim +++ b/tests/test_sets.nim @@ -1,5 +1,5 @@ import - unittest, + unittest2, ../stew/shims/sets suite "shims/sets": diff --git a/tests/test_sorted_set.nim b/tests/test_sorted_set.nim index 7583f67a..dfa9c5ab 100644 --- a/tests/test_sorted_set.nim +++ b/tests/test_sorted_set.nim @@ -10,8 +10,8 @@ import std/[algorithm, sequtils, strformat, tables], - ../stew/sorted_set, - unittest + unittest2, + ../stew/sorted_set const keyList = [ diff --git a/tests/test_templateutils.nim b/tests/test_templateutils.nim index b5977398..67ce1175 100644 --- a/tests/test_templateutils.nim +++ b/tests/test_templateutils.nim @@ -1,5 +1,5 @@ import - unittest, + unittest2, ../stew/templateutils var computations = newSeq[string]() diff --git a/tests/test_varints.nim b/tests/test_varints.nim index fd4b0ab1..b0839036 100644 --- a/tests/test_varints.nim +++ b/tests/test_varints.nim @@ -1,5 +1,6 @@ import - unittest, random, + random, + unittest2, ../stew/[varints, byteutils] const edgeValues = { diff --git a/tests/test_winacl.nim b/tests/test_winacl.nim index a86852c5..bc98c77c 100644 --- a/tests/test_winacl.nim +++ b/tests/test_winacl.nim @@ -1,5 +1,6 @@ -import std/unittest -import ../stew/io2 +import + unittest2, + ../stew/io2 when defined(windows): import ../stew/windows/acl