From c9a8ada722a945b6de8c4c145ade25526545a4cc Mon Sep 17 00:00:00 2001 From: Daniil Yarancev <21169548+Yardanico@users.noreply.github.com> Date: Tue, 5 Jun 2018 21:27:51 +0300 Subject: [PATCH 01/12] Update .travis.yml --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index a3fa3f1da1..38e1eba28a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,8 @@ before_install: before_script: - set -e + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then unset -f cd; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then shell_session_update() { :; }; fi - git clone --depth 1 https://github.com/nim-lang/csources.git - cd csources - sh build.sh From 22f714585b943d0b2457c66a78917113072f4503 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Thu, 17 May 2018 12:56:18 +0200 Subject: [PATCH 02/12] Native access to Genode environment Add a 'GenodeEnv' type and a 'componentConstructHook' to the system module. The 'componentConstructHook' allows for detection of POSIX style programs that exit implicitly or native Genode components that initialize to serve RPC requests and OS signals. This hook takes a 'GenodeEnv' argument so that the environment interface is passed cleanly to application code after globals are initialized. This is an typed pointer to a C++ object, procedures for accessing the environment will be available from a Nimble library and not included in the standard library. The standard library has an internal pointer to the environment object but this is not for external use, the undocumented global environment pointer has been removed. --- compiler/cgen.nim | 10 ++++- .../genodealloc.nim => genode/alloc.nim} | 43 +++++++++++-------- lib/genode/env.nim | 29 +++++++++++++ lib/genode_cpp/threads.h | 1 + lib/nimbase.h | 5 --- lib/pure/concurrency/cpuinfo.nim | 8 ++-- lib/pure/os.nim | 2 +- lib/system.nim | 37 ++++++++++++++-- lib/system/osalloc.nim | 2 +- lib/system/threads.nim | 7 ++- 10 files changed, 107 insertions(+), 37 deletions(-) rename lib/{system/genodealloc.nim => genode/alloc.nim} (68%) create mode 100644 lib/genode/env.nim diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 6a16474c03..e749c78db1 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1035,14 +1035,19 @@ proc genMainProc(m: BModule) = "}$N$N" GenodeNimMain = - "Libc::Env *genodeEnv;$N" & + "extern Genode::Env *nim_runtime_env;$N" & + "extern void nim_component_construct(Genode::Env*);$N$N" & NimMainBody ComponentConstruct = "void Libc::Component::construct(Libc::Env &env) {$N" & - "\tgenodeEnv = &env;$N" & + "\t// Set Env used during runtime initialization$N" & + "\tnim_runtime_env = &env;$N" & "\tLibc::with_libc([&] () {$N\t" & + "\t// Initialize runtime and globals$N" & MainProcs & + "\t// Call application construct$N" & + "\t\tnim_component_construct(&env);$N" & "\t});$N" & "}$N$N" @@ -1059,6 +1064,7 @@ proc genMainProc(m: BModule) = elif platform.targetOS == osGenode: nimMain = GenodeNimMain otherMain = ComponentConstruct + m.includeHeader("") elif optGenDynLib in m.config.globalOptions: nimMain = PosixNimDllMain otherMain = PosixCDllMain diff --git a/lib/system/genodealloc.nim b/lib/genode/alloc.nim similarity index 68% rename from lib/system/genodealloc.nim rename to lib/genode/alloc.nim index 3646a842d8..52dc1c32cc 100644 --- a/lib/system/genodealloc.nim +++ b/lib/genode/alloc.nim @@ -8,10 +8,15 @@ # # Low level dataspace allocator for Genode. +# For interacting with dataspaces outside of the +# standard library see the Genode Nimble package. when not defined(genode): {.error: "Genode only module".} +when not declared(GenodeEnv): + include genode/env + type DataspaceCapability {. importcpp: "Genode::Dataspace_capability", pure.} = object @@ -31,35 +36,35 @@ type const SlabBackendSize = 4096 -proc ramAvail(): int {. - importcpp: "genodeEnv->pd().avail_ram().value".} +proc ramAvail(env: GenodeEnv): int {. + importcpp: "#->pd().avail_ram().value".} ## Return number of bytes available for allocation. -proc capsAvail(): int {. - importcpp: "genodeEnv->pd().avail_caps().value".} +proc capsAvail(env: GenodeEnv): int {. + importcpp: "#->pd().avail_caps().value".} ## Return the number of available capabilities. ## Each dataspace allocation consumes a capability. -proc allocDataspace(size: int): DataspaceCapability {. - importcpp: "genodeEnv->pd().alloc(@)".} +proc allocDataspace(env: GenodeEnv; size: int): DataspaceCapability {. + importcpp: "#->pd().alloc(@)".} ## Allocate a dataspace and its capability. -proc attachDataspace(ds: DataspaceCapability): pointer {. - importcpp: "genodeEnv->rm().attach(@)".} +proc attachDataspace(env: GenodeEnv; ds: DataspaceCapability): pointer {. + importcpp: "#->rm().attach(@)".} ## Attach a dataspace into the component address-space. -proc detachAddress(p: pointer) {. - importcpp: "genodeEnv->rm().detach(@)".} +proc detachAddress(env: GenodeEnv; p: pointer) {. + importcpp: "#->rm().detach(@)".} ## Detach a dataspace from the component address-space. -proc freeDataspace(ds: DataspaceCapability) {. - importcpp: "genodeEnv->pd().free(@)".} +proc freeDataspace(env: GenodeEnv; ds: DataspaceCapability) {. + importcpp: "#->pd().free(@)".} ## Free a dataspace. proc newMapSlab(): ptr MapSlab = let - ds = allocDataspace SlabBackendSize - p = attachDataspace ds + ds = runtimeEnv.allocDataspace SlabBackendSize + p = runtimeEnv.attachDataspace ds result = cast[ptr MapSlab](p) result.meta.ds = ds @@ -89,13 +94,13 @@ proc osAllocPages(size: int): pointer = # tack a new slab on the tail slab = slab.meta.next # move to next slab in linked list - map.ds = allocDataspace size + map.ds = runtimeEnv.allocDataspace size map.size = size - map.attachment = attachDataspace map.ds + map.attachment = runtimeEnv.attachDataspace map.ds result = map.attachment proc osTryAllocPages(size: int): pointer = - if ramAvail() >= size and capsAvail() > 1: + if runtimeEnv.ramAvail() >= size and runtimeEnv.capsAvail() > 4: result = osAllocPages size proc osDeallocPages(p: pointer; size: int) = @@ -107,8 +112,8 @@ proc osDeallocPages(p: pointer; size: int) = if m.size != size: echo "cannot partially detach dataspace" quit -1 - detachAddress m.attachment - freeDataspace m.ds + runtimeEnv.detachAddress m.attachment + runtimeEnv.freeDataspace m.ds m[] = Map() return slab = slab.meta.next diff --git a/lib/genode/env.nim b/lib/genode/env.nim new file mode 100644 index 0000000000..2b180d1b3b --- /dev/null +++ b/lib/genode/env.nim @@ -0,0 +1,29 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2018 Emery Hemingway +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# +# This file contains the minimum required definitions +# for interacting with the initial Genode environment. +# It is reserved for use only within the standard +# library. See ``componentConstructHook`` in the system +# module for accessing the Genode environment after the +# standard library has finished initializating. +# + +when not defined(genode): + {.error: "Genode only include".} + +type + GenodeEnvObj {.importcpp: "Genode::Env", header: "", pure.} = object + GenodeEnvPtr = ptr GenodeEnvObj + +const runtimeEnvSym = "nim_runtime_env" + +when not defined(nimscript): + var runtimeEnv {.importcpp: runtimeEnvSym.}: GenodeEnvPtr diff --git a/lib/genode_cpp/threads.h b/lib/genode_cpp/threads.h index a7cb2f17b5..c901efb45c 100644 --- a/lib/genode_cpp/threads.h +++ b/lib/genode_cpp/threads.h @@ -13,6 +13,7 @@ #define _GENODE_CPP__THREAD_H_ #include +#include #include namespace Nim { struct SysThread; } diff --git a/lib/nimbase.h b/lib/nimbase.h index 20ac9979bd..6dc7429103 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -502,11 +502,6 @@ typedef int Nim_and_C_compiler_disagree_on_target_architecture[sizeof(NI) == siz # include #endif -#if defined(__GENODE__) -#include -extern Libc::Env *genodeEnv; -#endif - /* Compile with -d:checkAbi and a sufficiently C11:ish compiler to enable */ #define NIM_CHECK_SIZE(typ, sz) \ _Static_assert(sizeof(typ) == sz, "Nim & C disagree on type size") diff --git a/lib/pure/concurrency/cpuinfo.nim b/lib/pure/concurrency/cpuinfo.nim index f014888119..6d41aa1b28 100644 --- a/lib/pure/concurrency/cpuinfo.nim +++ b/lib/pure/concurrency/cpuinfo.nim @@ -38,8 +38,10 @@ when defined(macosx) or defined(bsd): importc: "sysctl", nodecl.} when defined(genode): - proc affinitySpaceTotal(): cuint {. - importcpp: "genodeEnv->cpu().affinity_space().total()".} + include genode/env + + proc affinitySpaceTotal(env: GenodeEnvPtr): cuint {. + importcpp: "@->cpu().affinity_space().total()".} proc countProcessors*(): int {.rtl, extern: "ncpi$1".} = ## returns the numer of the processors/cores the machine has. @@ -83,7 +85,7 @@ proc countProcessors*(): int {.rtl, extern: "ncpi$1".} = var SC_NPROC_ONLN {.importc: "_SC_NPROC_ONLN", header: "".}: cint result = sysconf(SC_NPROC_ONLN) elif defined(genode): - result = affinitySpaceTotal().int + result = runtimeEnv.affinitySpaceTotal().int else: result = sysconf(SC_NPROCESSORS_ONLN) if result <= 0: result = 0 diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 04afb1eff6..5008b904cb 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1440,7 +1440,7 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = elif defined(solaris): result = getApplAux("/proc/" & $getpid() & "/path/a.out") elif defined(genode): - raiseOSError("POSIX command line not supported") + raiseOSError(OSErrorCode(-1), "POSIX command line not supported") elif defined(freebsd) or defined(dragonfly): result = getApplFreebsd() # little heuristic that may work on other POSIX-like systems: diff --git a/lib/system.nim b/lib/system.nim index fee9dc3141..fb02fde23e 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1498,11 +1498,21 @@ when defined(nimdoc): ## macro, use the `error `_ or `fatal ## `_ pragmas. - elif defined(genode): - proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn, - importcpp: "genodeEnv->parent().exit(@); Genode::sleep_forever()", - header: "".} + include genode/env + + var systemEnv {.exportc: runtimeEnvSym.}: GenodeEnvPtr + + type GenodeEnv* = GenodeEnvPtr + ## Opaque type representing Genode environment. + + proc quit*(env: GenodeEnv; errorcode: int) {.magic: "Exit", noreturn, + importcpp: "#->parent().exit(@); Genode::sleep_forever()", header: "".} + + proc quit*(errorcode: int = QuitSuccess) = + systemEnv.quit(errorCode) + + elif defined(nodejs): proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", @@ -4215,3 +4225,22 @@ when not defined(js): type ForLoopStmt* {.compilerProc.} = object ## special type that marks a macro ## as a `for-loop macro`:idx: + +when defined(genode): + var componentConstructHook*: proc (env: GenodeEnv) {.nimcall.} + ## Hook into the Genode component bootstrap process. + ## This hook is called after all globals are initialized. + ## When this hook is set the component will not automatically exit, + ## call ``quit`` explicitly to do so. This is the only available method + ## of accessing the initial Genode environment. + + proc nim_component_construct(env: GenodeEnv) {.exportc.} = + ## Procedure called during ``Component::construct`` by the loader. + if componentConstructHook.isNil: + env.quit(programResult) + # No native Genode application initialization, + # exit as would POSIX. + else: + componentConstructHook(env) + # Perform application initialization + # and return to thread entrypoint. diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim index 9609b6d39f..a63eadf8ec 100644 --- a/lib/system/osalloc.nim +++ b/lib/system/osalloc.nim @@ -78,7 +78,7 @@ when defined(emscripten): munmap(mmapDescr.realPointer, mmapDescr.realSize) elif defined(genode): - include genodealloc # osAllocPages, osTryAllocPages, osDeallocPages + include genode/alloc # osAllocPages, osTryAllocPages, osDeallocPages elif defined(posix): const diff --git a/lib/system/threads.nim b/lib/system/threads.nim index 861bde13f2..c8ea03f929 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -116,6 +116,7 @@ when defined(windows): importc: "SetThreadAffinityMask", stdcall, header: "".} elif defined(genode): + import genode/env const GenodeHeader = "genode_cpp/threads.h" type @@ -125,11 +126,12 @@ elif defined(genode): ThreadVarSlot = int proc initThread(s: var SysThread, + env: GenodeEnv, stackSize: culonglong, entry: GenodeThreadProc, arg: pointer, affinity: cuint) {. - importcpp: "#.initThread(genodeEnv, @)".} + importcpp: "#.initThread(@)".} proc threadVarAlloc(): ThreadVarSlot = 0 @@ -569,7 +571,7 @@ when hostOS == "windows": elif defined(genode): var affinityOffset: cuint = 1 - # CPU affinity offset for next thread, safe to roll-over + ## CPU affinity offset for next thread, safe to roll-over proc createThread*[TArg](t: var Thread[TArg], tp: proc (arg: TArg) {.thread, nimcall.}, @@ -580,6 +582,7 @@ elif defined(genode): t.dataFn = tp when hasSharedHeap: t.stackSize = ThreadStackSize t.sys.initThread( + runtimeEnv, ThreadStackSize.culonglong, threadProcWrapper[TArg], addr(t), affinityOffset) inc affinityOffset From 5e54cd9fc9ce21311a4991f550e420f5bf1bd726 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Sun, 3 Jun 2018 16:39:52 +0200 Subject: [PATCH 03/12] Set Genode Openssl shared-object strings --- lib/wrappers/openssl.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index a24575d116..de3bfa616a 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -44,6 +44,10 @@ else: const DLLSSLName* = "libssl" & versions & ".dylib" DLLUtilName* = "libcrypto" & versions & ".dylib" + elif defined(genode): + const + DLLSSLName* = "libssl.lib.so" + DLLUtilName* = "libcrypto.lib.so" else: const DLLSSLName* = "libssl.so" & versions From b2323de9140dfa3a1cbe007274970457bb1ecc76 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Thu, 7 Jun 2018 21:10:00 +0300 Subject: [PATCH 04/12] Fixed compilation error when Sockaddr_in4 or Sockaddr_in6 passed to fromSockAddr --- lib/pure/net.nim | 2 +- tests/stdlib/tnet.nim | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 60817484a2..5d2efebee6 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -447,7 +447,7 @@ proc fromSockAddr*(sa: Sockaddr_storage | SockAddr | Sockaddr_in | Sockaddr_in6, sl: Socklen, address: var IpAddress, port: var Port) {.inline.} = ## Converts `SockAddr` and `Socklen` to `IpAddress` and `Port`. Raises ## `ObjectConversionError` in case of invalid `sa` and `sl` arguments. - fromSockAddrAux(unsafeAddr sa, sl, address, port) + fromSockAddrAux(cast[ptr Sockaddr_storage](unsafeAddr sa), sl, address, port) when defineSsl: CRYPTO_malloc_init() diff --git a/tests/stdlib/tnet.nim b/tests/stdlib/tnet.nim index 64d690fc96..d364447dad 100644 --- a/tests/stdlib/tnet.nim +++ b/tests/stdlib/tnet.nim @@ -66,6 +66,18 @@ block: # "IpAddress/Sockaddr conversion" doAssert(ipaddr_1 == ipaddr_2) doAssert($ipaddr_1 == $ipaddr_2) + if sockaddr.ss_family == AF_INET.toInt: + var sockaddr4: Sockaddr_in + copyMem(addr sockaddr4, addr sockaddr, sizeof(sockaddr4)) + fromSockAddr(sockaddr4, socklen, ipaddr_2, port_2) + elif sockaddr.ss_family == AF_INET6.toInt: + var sockaddr6: Sockaddr_in6 + copyMem(addr sockaddr6, addr sockaddr, sizeof(sockaddr6)) + fromSockAddr(sockaddr6, socklen, ipaddr_2, port_2) + + doAssert(ipaddr_1 == ipaddr_2) + doAssert($ipaddr_1 == $ipaddr_2) + # ipv6 address of example.com test("2606:2800:220:1:248:1893:25c8:1946") From fbd91a474a86a5a52fe495df00e9a0e1bfa000df Mon Sep 17 00:00:00 2001 From: Dmitry Atamanov Date: Fri, 8 Jun 2018 19:01:40 +0300 Subject: [PATCH 05/12] Add the val parameter for CritBitTree[T].incl (#7988) * Add the val parameter for CritBitTree[T].incl * Updated changelog --- changelog.md | 1 + lib/pure/collections/critbits.nim | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/changelog.md b/changelog.md index 6fd12e62fe..aae275c1c8 100644 --- a/changelog.md +++ b/changelog.md @@ -85,6 +85,7 @@ - The `terminal` module now exports additional procs for generating ANSI color codes as strings. - Added the parameter ``val`` for the ``CritBitTree[int].inc`` proc. +- Added the parameter ``val`` for the ``CritBitTree[T].incl`` proc. - An exception raised from ``test`` block of ``unittest`` now shows its type in the error message - The proc ``tgamma`` was renamed to ``gamma``. ``tgamma`` is deprecated. diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim index eaba257ae8..2ab65f0021 100644 --- a/lib/pure/collections/critbits.nim +++ b/lib/pure/collections/critbits.nim @@ -175,6 +175,11 @@ proc incl*(c: var CritBitTree[void], key: string) = ## includes `key` in `c`. discard rawInsert(c, key) +proc incl*[T](c: var CritBitTree[T], key: string, val: T) = + ## inserts `key` with value `val` into `c`. + var n = rawInsert(c, key) + n.val = val + proc `[]=`*[T](c: var CritBitTree[T], key: string, val: T) = ## puts a (key, value)-pair into `t`. var n = rawInsert(c, key) @@ -375,3 +380,18 @@ when isMainModule: c.inc("a", 1) assert c["a"] == 1 + + var cf = CritBitTree[float]() + + cf.incl("a", 1.0) + assert cf["a"] == 1.0 + + cf.incl("b", 2.0) + assert cf["b"] == 2.0 + + cf.incl("c", 3.0) + assert cf["c"] == 3.0 + + assert cf.len == 3 + cf.excl("c") + assert cf.len == 2 From 3e799d7876110d970c365d61c05e887729488e2f Mon Sep 17 00:00:00 2001 From: data-man Date: Fri, 8 Jun 2018 19:29:19 +0300 Subject: [PATCH 06/12] Removed redundant conditions in CritBitTree.inc, speedup it. --- lib/pure/collections/critbits.nim | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim index 2ab65f0021..c94e080984 100644 --- a/lib/pure/collections/critbits.nim +++ b/lib/pure/collections/critbits.nim @@ -165,11 +165,8 @@ proc containsOrIncl*(c: var CritBitTree[void], key: string): bool = proc inc*(c: var CritBitTree[int]; key: string, val: int = 1) = ## increments `c[key]` by `val`. - let oldCount = c.count var n = rawInsert(c, key) - if c.count >= oldCount or oldCount == 0: - # not a new key: - inc n.val, val + inc n.val, val proc incl*(c: var CritBitTree[void], key: string) = ## includes `key` in `c`. From 1531738974a714037fc493306066bca8c6f4a723 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Fri, 8 Jun 2018 20:51:44 +0300 Subject: [PATCH 07/12] NEP-1.1. Relax 80 chars requirement. --- doc/nep1.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/nep1.rst b/doc/nep1.rst index c4d4456815..b302dc2a72 100644 --- a/doc/nep1.rst +++ b/doc/nep1.rst @@ -32,8 +32,8 @@ Style Guidelines Spacing and Whitespace Conventions ----------------------------------- -- Lines should be no longer than 80 characters. Limiting the amount of - information present on each line makes for more readable code - the reader +- Lines should preferably be no longer than 80 characters. Limiting the amount + of information present on each line makes for more readable code - the reader has smaller chunks to process. - Two spaces should be used for indentation of blocks; tabstops are not allowed From 24df909d8a953f2b7ba0e0d1adf3a256042cd9bc Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Wed, 6 Jun 2018 17:44:11 -0400 Subject: [PATCH 08/12] Make isUpper (and variants) work for strings with non-alpha chars The other variants are isLower, isUpperAscii and isLowerAscii Fixes https://github.com/nim-lang/Nim/issues/7963. This commit changes the behavior and signatures of: - isUpper, isLower in the unicode module - isUpperAscii, isLowerAscii in the strutils module A second mandatory parameter skipNonAlpha is added to these 4 procs. (This change affects only for the case where the input is a *string*.) --- With skipNonAlpha set to true, the behavior mimics the Python isupper and islower behavior i.e. non-alphabetic chars/runes are ignored when checking if the string is upper-case or lower-case. Before this commit: doAssert(not isUpper("A B")) After this commit: doAssert(not isUpper("A B", false)) <-- old behavior doAssert isUpper("A B", true) Below two are equivalent: isUpper("A B", true) isAlpha("A B") and isUpper("A B", false) .. and the similar for other 3 procs. --- changelog.md | 6 +++ lib/pure/strutils.nim | 85 +++++++++++++++++++++++++-------- lib/pure/unicode.nim | 108 +++++++++++++++++++++++++++++++----------- 3 files changed, 151 insertions(+), 48 deletions(-) diff --git a/changelog.md b/changelog.md index aae275c1c8..cb2a4b91d9 100644 --- a/changelog.md +++ b/changelog.md @@ -42,6 +42,12 @@ - ``math.`mod` `` for floats now behaves the same as ``mod`` for integers (previously it used floor division like Python). Use ``math.floorMod`` for the old behavior. +- For string inputs, ``unicode.isUpper`` and ``unicode.isLower`` now require a + second mandatory parameter ``skipNonAlpha``. + +- For string inputs, ``strutils.isUpperAscii`` and ``strutils.isLowerAscii`` now + require a second mandatory parameter ``skipNonAlpha``. + #### Breaking changes in the compiler - The undocumented ``#? braces`` parsing mode was removed. diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index a4fd20fdb8..bea0a02431 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -150,23 +150,52 @@ proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar, ## characters and there is at least one character in `s`. isImpl isSpaceAscii -proc isLowerAscii*(s: string): bool {.noSideEffect, procvar, - rtl, extern: "nsuIsLowerAsciiStr".} = - ## Checks whether or not `s` contains all lower case characters. - ## - ## This checks ASCII characters only. - ## Returns true if all characters in `s` are lower case - ## and there is at least one character in `s`. - isImpl isLowerAscii +template isCaseImpl(s, charProc, skipNonAlpha) = + var hasAtleastOneAlphaChar = false + if s.len == 0: return false + for c in s: + if skipNonAlpha: + var charIsAlpha = c.isAlphaAscii() + if not hasAtleastOneAlphaChar: + hasAtleastOneAlphaChar = charIsAlpha + if charIsAlpha and (not charProc(c)): + return false + else: + if not charProc(c): + return false + return if skipNonAlpha: hasAtleastOneAlphaChar else: true -proc isUpperAscii*(s: string): bool {.noSideEffect, procvar, - rtl, extern: "nsuIsUpperAsciiStr".} = - ## Checks whether or not `s` contains all upper case characters. +proc isLowerAscii*(s: string, skipNonAlpha: bool): bool = + ## Checks whether ``s`` is lower case. ## ## This checks ASCII characters only. - ## Returns true if all characters in `s` are upper case - ## and there is at least one character in `s`. - isImpl isUpperAscii + ## + ## If ``skipNonAlpha`` is true, returns true if all alphabetical + ## characters in ``s`` are lower case. Returns false if none of the + ## characters in ``s`` are alphabetical. + ## + ## If ``skipNonAlpha`` is false, returns true only if all characters + ## in ``s`` are alphabetical and lower case. + ## + ## For either value of ``skipNonAlpha``, returns false if ``s`` is + ## an empty string. + isCaseImpl(s, isLowerAscii, skipNonAlpha) + +proc isUpperAscii*(s: string, skipNonAlpha: bool): bool = + ## Checks whether ``s`` is upper case. + ## + ## This checks ASCII characters only. + ## + ## If ``skipNonAlpha`` is true, returns true if all alphabetical + ## characters in ``s`` are upper case. Returns false if none of the + ## characters in ``s`` are alphabetical. + ## + ## If ``skipNonAlpha`` is false, returns true only if all characters + ## in ``s`` are alphabetical and upper case. + ## + ## For either value of ``skipNonAlpha``, returns false if ``s`` is + ## an empty string. + isCaseImpl(s, isUpperAscii, skipNonAlpha) proc toLowerAscii*(c: char): char {.noSideEffect, procvar, rtl, extern: "nsuToLowerAsciiChar".} = @@ -2516,19 +2545,34 @@ when isMainModule: doAssert(not isLowerAscii('A')) doAssert(not isLowerAscii('5')) doAssert(not isLowerAscii('&')) + doAssert(not isLowerAscii(' ')) - doAssert isLowerAscii("abcd") - doAssert(not isLowerAscii("abCD")) - doAssert(not isLowerAscii("33aa")) + doAssert isLowerAscii("abcd", false) + doAssert(not isLowerAscii("33aa", false)) + doAssert(not isLowerAscii("a b", false)) + + doAssert(not isLowerAscii("abCD", true)) + doAssert isLowerAscii("33aa", true) + doAssert isLowerAscii("a b", true) + doAssert isLowerAscii("1, 2, 3 go!", true) + doAssert(not isLowerAscii(" ", true)) + doAssert(not isLowerAscii("(*&#@(^#$ ", true)) # None of the string chars are alphabets doAssert isUpperAscii('A') doAssert(not isUpperAscii('b')) doAssert(not isUpperAscii('5')) doAssert(not isUpperAscii('%')) - doAssert isUpperAscii("ABC") - doAssert(not isUpperAscii("AAcc")) - doAssert(not isUpperAscii("A#$")) + doAssert isUpperAscii("ABC", false) + doAssert(not isUpperAscii("A#$", false)) + doAssert(not isUpperAscii("A B", false)) + + doAssert(not isUpperAscii("AAcc", true)) + doAssert isUpperAscii("A#$", true) + doAssert isUpperAscii("A B", true) + doAssert isUpperAscii("1, 2, 3 GO!", true) + doAssert(not isUpperAscii(" ", true)) + doAssert(not isUpperAscii("(*&#@(^#$ ", true)) # None of the string chars are alphabets doAssert rsplit("foo bar", seps=Whitespace) == @["foo", "bar"] doAssert rsplit(" foo bar", seps=Whitespace, maxsplit=1) == @[" foo", "bar"] @@ -2601,4 +2645,3 @@ bar nonStaticTests() staticTests() static: staticTests() - diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index bfd01be550..978f569ac6 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -1392,7 +1392,7 @@ proc isCombining*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = (c >= 0xfe20 and c <= 0xfe2f)) template runeCheck(s, runeProc) = - ## Common code for rune.isLower, rune.isUpper, etc + ## Common code for isAlpha and isSpace. result = if len(s) == 0: false else: true var @@ -1403,16 +1403,6 @@ template runeCheck(s, runeProc) = fastRuneAt(s, i, rune, doInc=true) result = runeProc(rune) and result -proc isUpper*(s: string): bool {.noSideEffect, procvar, - rtl, extern: "nuc$1Str".} = - ## Returns true iff `s` contains all upper case unicode characters. - runeCheck(s, isUpper) - -proc isLower*(s: string): bool {.noSideEffect, procvar, - rtl, extern: "nuc$1Str".} = - ## Returns true iff `s` contains all lower case unicode characters. - runeCheck(s, isLower) - proc isAlpha*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nuc$1Str".} = ## Returns true iff `s` contains all alphabetic unicode characters. @@ -1423,6 +1413,56 @@ proc isSpace*(s: string): bool {.noSideEffect, procvar, ## Returns true iff `s` contains all whitespace unicode characters. runeCheck(s, isWhiteSpace) +template runeCaseCheck(s, runeProc, skipNonAlpha) = + ## Common code for rune.isLower and rune.isUpper. + if len(s) == 0: return false + + var + i = 0 + rune: Rune + hasAtleastOneAlphaRune = false + + while i < len(s): + fastRuneAt(s, i, rune, doInc=true) + if skipNonAlpha: + var runeIsAlpha = isAlpha(rune) + if not hasAtleastOneAlphaRune: + hasAtleastOneAlphaRune = runeIsAlpha + if runeIsAlpha and (not runeProc(rune)): + return false + else: + if not runeProc(rune): + return false + return if skipNonAlpha: hasAtleastOneAlphaRune else: true + +proc isLower*(s: string, skipNonAlpha: bool): bool = + ## Checks whether ``s`` is lower case. + ## + ## If ``skipNonAlpha`` is true, returns true if all alphabetical + ## runes in ``s`` are lower case. Returns false if none of the + ## runes in ``s`` are alphabetical. + ## + ## If ``skipNonAlpha`` is false, returns true only if all runes in + ## ``s`` are alphabetical and lower case. + ## + ## For either value of ``skipNonAlpha``, returns false if ``s`` is + ## an empty string. + runeCaseCheck(s, isLower, skipNonAlpha) + +proc isUpper*(s: string, skipNonAlpha: bool): bool = + ## Checks whether ``s`` is upper case. + ## + ## If ``skipNonAlpha`` is true, returns true if all alphabetical + ## runes in ``s`` are upper case. Returns false if none of the + ## runes in ``s`` are alphabetical. + ## + ## If ``skipNonAlpha`` is false, returns true only if all runes in + ## ``s`` are alphabetical and upper case. + ## + ## For either value of ``skipNonAlpha``, returns false if ``s`` is + ## an empty string. + runeCaseCheck(s, isUpper, skipNonAlpha) + template convertRune(s, runeProc) = ## Convert runes in `s` using `runeProc` as the converter. result = newString(len(s)) @@ -1755,25 +1795,39 @@ when isMainModule: doAssert(not isSpace("")) doAssert(not isSpace("ΑΓc \td")) - doAssert isLower("a") - doAssert isLower("γ") - doAssert(not isLower("Γ")) - doAssert(not isLower("4")) - doAssert(not isLower("")) + doAssert(not isLower(' '.Rune)) - doAssert isLower("abcdγ") - doAssert(not isLower("abCDΓ")) - doAssert(not isLower("33aaΓ")) + doAssert isLower("a", false) + doAssert isLower("γ", true) + doAssert(not isLower("Γ", false)) + doAssert(not isLower("4", true)) + doAssert(not isLower("", false)) + doAssert isLower("abcdγ", false) + doAssert(not isLower("33aaΓ", false)) + doAssert(not isLower("a b", false)) - doAssert isUpper("Γ") - doAssert(not isUpper("b")) - doAssert(not isUpper("α")) - doAssert(not isUpper("✓")) - doAssert(not isUpper("")) + doAssert(not isLower("abCDΓ", true)) + doAssert isLower("a b", true) + doAssert isLower("1, 2, 3 go!", true) + doAssert(not isLower(" ", true)) + doAssert(not isLower("(*&#@(^#$✓ ", true)) # None of the string runes are alphabets - doAssert isUpper("ΑΒΓ") - doAssert(not isUpper("AAccβ")) - doAssert(not isUpper("A#$β")) + doAssert(not isUpper(' '.Rune)) + + doAssert isUpper("Γ", false) + doAssert(not isUpper("α", false)) + doAssert(not isUpper("", false)) + doAssert isUpper("ΑΒΓ", false) + doAssert(not isUpper("A#$β", false)) + doAssert(not isUpper("A B", false)) + + doAssert(not isUpper("b", true)) + doAssert(not isUpper("✓", true)) + doAssert(not isUpper("AAccβ", true)) + doAssert isUpper("A B", true) + doAssert isUpper("1, 2, 3 GO!", true) + doAssert(not isUpper(" ", true)) + doAssert(not isUpper("(*&#@(^#$✓ ", true)) # None of the string runes are alphabets doAssert toUpper("Γ") == "Γ" doAssert toUpper("b") == "B" From c2aec1b6c19af8ee24b9da2bedb67836064199a0 Mon Sep 17 00:00:00 2001 From: hlaaf Date: Fri, 8 Jun 2018 23:34:19 +0300 Subject: [PATCH 09/12] Change parseEnum to something faster for method parsing in asynchttpserver (#7682) * Add faster method parsing to asynchttpserver * Make it readable * Align case statement --- lib/pure/asynchttpserver.nim | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index fe5a835d74..d27c2fb9cd 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -129,6 +129,20 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] = proc sendStatus(client: AsyncSocket, status: string): Future[void] = client.send("HTTP/1.1 " & status & "\c\L\c\L") +proc parseUppercaseMethod(name: string): HttpMethod = + result = + case name + of "GET": HttpGet + of "POST": HttpPost + of "HEAD": HttpHead + of "PUT": HttpPut + of "DELETE": HttpDelete + of "PATCH": HttpPatch + of "OPTIONS": HttpOptions + of "CONNECT": HttpConnect + of "TRACE": HttpTrace + else: raise newException(ValueError, "Invalid HTTP method " & name) + proc processRequest(server: AsyncHttpServer, req: FutureVar[Request], client: AsyncSocket, address: string, lineFut: FutureVar[string], @@ -172,8 +186,7 @@ proc processRequest(server: AsyncHttpServer, req: FutureVar[Request], case i of 0: try: - # TODO: this is likely slow. - request.reqMethod = parseEnum[HttpMethod]("http" & linePart) + request.reqMethod = parseUppercaseMethod(linePart) except ValueError: asyncCheck request.respondError(Http400) return From ae342f84de7d7b6101f766e3b3d08671a9f98d0e Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 9 Jun 2018 18:39:33 +0100 Subject: [PATCH 10/12] Revert "[RFC] NEP-1.1. Relax 80 chars requirement." --- doc/nep1.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/nep1.rst b/doc/nep1.rst index b302dc2a72..c4d4456815 100644 --- a/doc/nep1.rst +++ b/doc/nep1.rst @@ -32,8 +32,8 @@ Style Guidelines Spacing and Whitespace Conventions ----------------------------------- -- Lines should preferably be no longer than 80 characters. Limiting the amount - of information present on each line makes for more readable code - the reader +- Lines should be no longer than 80 characters. Limiting the amount of + information present on each line makes for more readable code - the reader has smaller chunks to process. - Two spaces should be used for indentation of blocks; tabstops are not allowed From 03653ab61e6eed6811c5df0677a2bf2aa722ef9c Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 21 Apr 2017 22:28:34 +0200 Subject: [PATCH 11/12] Fix type inference with static literals. Fixes #3977 --- compiler/semstmts.nim | 2 +- tests/generics/t3977.nim | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 tests/generics/t3977.nim diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 3687e50e9c..7213601de5 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -498,7 +498,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = def = fitNode(c, typ, def, def.info) #changeType(def.skipConv, typ, check=true) else: - typ = skipIntLit(def.typ) + typ = def.typ.skipTypes({tyStatic}).skipIntLit if typ.kind in tyUserTypeClasses and typ.isResolvedUserTypeClass: typ = typ.lastSon if hasEmpty(typ): diff --git a/tests/generics/t3977.nim b/tests/generics/t3977.nim new file mode 100644 index 0000000000..3140177447 --- /dev/null +++ b/tests/generics/t3977.nim @@ -0,0 +1,12 @@ +discard """ + output: '''42''' +""" + +type + Foo[N: static[int]] = object + +proc foo[N](x: Foo[N]) = + echo N + +var f1: Foo[42] +f1.foo From 5f2cdcd4fa0f3d5dd0156026c0685aa59d9f7f92 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 10 Jun 2018 16:44:49 +0300 Subject: [PATCH 12/12] fix #7653 --- compiler/ccgtypes.nim | 2 -- compiler/sighashes.nim | 25 +++++++++++++++---------- tests/cpp/tempty_generic_obj.nim | 16 ++++++++++++++++ tests/generics/t3977.nim | 4 +++- 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 7b44cddad3..4b0fd49aad 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -791,8 +791,6 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = else: addAbiCheck(m, t, result) of tyObject, tyTuple: if isImportedCppType(t) and origTyp.kind == tyGenericInst: - # for instantiated templates we do not go through the type cache as the - # the type cache is not aware of 'tyGenericInst'. let cppName = getTypeName(m, t, sig) var i = 0 var chunkStart = 0 diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 46b83c3862..0b95387cd9 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -12,7 +12,7 @@ import ast, md5, tables, ropes from hashes import Hash from astalgo import debug -from types import typeToString, preferDesc +import types from strutils import startsWith, contains when false: @@ -148,19 +148,23 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = of tyGenericInvocation: for i in countup(0, sonsLen(t) - 1): c.hashType t.sons[i], flags - return of tyDistinct: if CoType in flags: c.hashType t.lastSon, flags else: c.hashSym(t.sym) - return - of tyAlias, tySink, tyGenericInst, tyUserTypeClasses: + of tyGenericInst: + if sfInfixCall in t.base.sym.flags: + # This is an imported C++ generic type. + # We cannot trust the `lastSon` to hold a properly populated and unique + # value for each instantiation, so we hash the generic parameters here: + let normalizedType = t.skipGenericAlias + for i in 0 .. normalizedType.len - 2: + c.hashType t.sons[i], flags + else: + c.hashType t.lastSon, flags + of tyAlias, tySink, tyUserTypeClasses: c.hashType t.lastSon, flags - return - else: - discard - case t.kind of tyBool, tyChar, tyInt..tyUInt64: # no canonicalization for integral types, so that e.g. ``pid_t`` is # produced instead of ``NI``: @@ -168,11 +172,12 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = if t.sym != nil and {sfImportc, sfExportc} * t.sym.flags != {}: c.hashSym(t.sym) of tyObject, tyEnum: - c &= char(t.kind) if t.typeInst != nil: assert t.typeInst.kind == tyGenericInst - for i in countup(1, sonsLen(t.typeInst) - 2): + for i in countup(0, sonsLen(t.typeInst) - 2): c.hashType t.typeInst.sons[i], flags + return + c &= char(t.kind) # Every cyclic type in Nim need to be constructed via some 't.sym', so this # is actually safe without an infinite recursion check: if t.sym != nil: diff --git a/tests/cpp/tempty_generic_obj.nim b/tests/cpp/tempty_generic_obj.nim index b4c746a30b..b61c699f64 100644 --- a/tests/cpp/tempty_generic_obj.nim +++ b/tests/cpp/tempty_generic_obj.nim @@ -20,3 +20,19 @@ v.doSomething() var vf = initVector[float]() vf.doSomething() # Nim uses doSomething[int] here in C++ + +# Alternative definition: +# https://github.com/nim-lang/Nim/issues/7653 + +type VectorAlt* {.importcpp: "std::vector", header: "", nodecl.} [T] = object +proc mkVector*[T]: VectorAlt[T] {.importcpp: "std::vector<'*0>()", header: "", constructor, nodecl.} + +proc foo(): VectorAlt[cint] = + mkVector[cint]() + +proc bar(): VectorAlt[cstring] = + mkVector[cstring]() + +var x = foo() +var y = bar() + diff --git a/tests/generics/t3977.nim b/tests/generics/t3977.nim index 3140177447..eed1a7d634 100644 --- a/tests/generics/t3977.nim +++ b/tests/generics/t3977.nim @@ -1,12 +1,14 @@ discard """ - output: '''42''' + output: "42\n42" """ type Foo[N: static[int]] = object proc foo[N](x: Foo[N]) = + let n = N echo N + echo n var f1: Foo[42] f1.foo