diff --git a/changelog.md b/changelog.md index db5b3c14b4d79..aff858818561e 100644 --- a/changelog.md +++ b/changelog.md @@ -173,9 +173,10 @@ provided by the operating system. dumping (on select signals) and notifying the parent process about the cause of termination. -- `hashes.hash` now supports `object`, but can be overloaded. +- Added `std/strbasics` for high performance string operations. + Added `strip`, `setSlice`, `add(a: var string, b: openArray[char])`. -- Added `strip` and `setSlice` to `std/strbasics`. +- `hashes.hash` now supports `object`, but can be overloaded. - Added to `wrapnils` an option-like API via `??.`, `isSome`, `get`. diff --git a/lib/std/strbasics.nim b/lib/std/strbasics.nim index ce061adca59ac..357e562084a85 100644 --- a/lib/std/strbasics.nim +++ b/lib/std/strbasics.nim @@ -8,9 +8,25 @@ # ## This module provides some high performance string operations. +## +## Experimental API, subject to change. const whitespaces = {' ', '\t', '\v', '\r', '\l', '\f'} +proc add*(x: var string, y: openArray[char]) = + ## Concatenates `x` and `y` in place. `y` must not overlap with `x` to + ## allow future `memcpy` optimizations. + # Use `{.noalias.}` ? + let n = x.len + x.setLen n + y.len + # pending https://github.com/nim-lang/Nim/issues/14655#issuecomment-643671397 + # use x.setLen(n + y.len, isInit = false) + var i = 0 + while i < y.len: + x[n + i] = y[i] + i.inc + # xxx use `nimCopyMem(x[n].addr, y[0].addr, y.len)` after some refactoring + func stripSlice(s: openArray[char], leading = true, trailing = true, chars: set[char] = whitespaces): Slice[int] = ## Returns the slice range of `s` which is stripped `chars`. runnableExamples: diff --git a/lib/system.nim b/lib/system.nim index 6d26beb73f0ed..4b182905b7313 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1069,15 +1069,16 @@ proc add*(x: var string, y: char) {.magic: "AppendStrCh", noSideEffect.} ## tmp.add('a') ## tmp.add('b') ## assert(tmp == "ab") -proc add*(x: var string, y: string) {.magic: "AppendStrStr", noSideEffect.} + +proc add*(x: var string, y: string) {.magic: "AppendStrStr", noSideEffect.} = ## Concatenates `x` and `y` in place. ## - ## .. code-block:: Nim - ## var tmp = "" - ## tmp.add("ab") - ## tmp.add("cd") - ## assert(tmp == "abcd") - + ## See also `strbasics.add`. + runnableExamples: + var tmp = "" + tmp.add("ab") + tmp.add("cd") + assert tmp == "abcd" type Endianness* = enum ## Type describing the endianness of a processor. diff --git a/tests/stdlib/tstrbasics.nim b/tests/stdlib/tstrbasics.nim index 4dd232351ecbd..8a3b22889db9c 100644 --- a/tests/stdlib/tstrbasics.nim +++ b/tests/stdlib/tstrbasics.nim @@ -1,126 +1,139 @@ discard """ targets: "c cpp js" + matrix: "--gc:refc; --gc:arc" """ import std/[strbasics, sugar] - -proc teststrip() = - var a = " vhellov " - strip(a) - doAssert a == "vhellov" - - a = " vhellov " - a.strip(leading = false) - doAssert a == " vhellov" - - a = " vhellov " - a.strip(trailing = false) - doAssert a == "vhellov " - - a.strip() - a.strip(chars = {'v'}) - doAssert a == "hello" - - a = " vhellov " - a.strip() - a.strip(leading = false, chars = {'v'}) - doAssert a == "vhello" - - var c = "blaXbla" - c.strip(chars = {'b', 'a'}) - doAssert c == "laXbl" - c = "blaXbla" - c.strip(chars = {'b', 'a', 'l'}) - doAssert c == "X" - - block: - var a = "xxxxxx" - a.strip(chars={'x'}) - doAssert a.len == 0 - - block: - var a = "x" - a.strip(chars={'x'}) - doAssert a.len == 0 - - block: - var a = "x" - a.strip(chars={'1'}) - doAssert a.len == 1 - - block: - var a = "" - a.strip(chars={'x'}) - doAssert a.len == 0 - - block: - var a = "xxx xxx" - a.strip(chars={'x'}) - doAssert a == " " - - block: - var a = "xxx wind" - a.strip(chars={'x'}) - doAssert a == " wind" - - block: - var a = "xxx iii" - a.strip(chars={'i'}) - doAssert a == "xxx " - - block: - var a = "xxx iii" - doAssert a.dup(strip(chars = {'i'})) == "xxx " - doAssert a.dup(strip(chars = {' '})) == "xxx iii" - doAssert a.dup(strip(chars = {'x'})) == " iii" - doAssert a.dup(strip(chars = {'x', ' '})) == "iii" - doAssert a.dup(strip(chars = {'x', 'i'})) == " " - doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0 - - block: - var a = "x i" - doAssert a.dup(strip(chars = {'i'})) == "x " - doAssert a.dup(strip(chars = {' '})) == "x i" - doAssert a.dup(strip(chars = {'x'})) == " i" - doAssert a.dup(strip(chars = {'x', ' '})) == "i" - doAssert a.dup(strip(chars = {'x', 'i'})) == " " - doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0 - - block: - var a = "" - doAssert a.dup(strip(chars = {'i'})).len == 0 - doAssert a.dup(strip(chars = {' '})).len == 0 - doAssert a.dup(strip(chars = {'x'})).len == 0 - doAssert a.dup(strip(chars = {'x', ' '})).len == 0 - doAssert a.dup(strip(chars = {'x', 'i'})).len == 0 - doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0 - - block: - var a = " " - doAssert a.dup(strip(chars = {'i'})) == " " - doAssert a.dup(strip(chars = {' '})).len == 0 - doAssert a.dup(strip(chars = {'x'})) == " " - doAssert a.dup(strip(chars = {'x', ' '})).len == 0 - doAssert a.dup(strip(chars = {'x', 'i'})) == " " - doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0 - - - block: - var a = "Hello, Nim!" - doassert a.dup(setSlice(7 .. 9)) == "Nim" - doAssert a.dup(setSlice(0 .. 0)) == "H" - doAssert a.dup(setSlice(0 .. 1)) == "He" - doAssert a.dup(setSlice(0 .. 10)) == a - doAssert a.dup(setSlice(1 .. 0)).len == 0 - doAssert a.dup(setSlice(20 .. -1)).len == 0 - - - doAssertRaises(AssertionDefect): - discard a.dup(setSlice(-1 .. 1)) - - doAssertRaises(AssertionDefect): - discard a.dup(setSlice(1 .. 11)) - -static: teststrip() -teststrip() +proc main() = + when not defined(gcArc): # pending bug #17173 + block: # strip + var a = " vhellov " + strip(a) + doAssert a == "vhellov" + + a = " vhellov " + a.strip(leading = false) + doAssert a == " vhellov" + + a = " vhellov " + a.strip(trailing = false) + doAssert a == "vhellov " + + a.strip() + a.strip(chars = {'v'}) + doAssert a == "hello" + + a = " vhellov " + a.strip() + a.strip(leading = false, chars = {'v'}) + doAssert a == "vhello" + + var c = "blaXbla" + c.strip(chars = {'b', 'a'}) + doAssert c == "laXbl" + c = "blaXbla" + c.strip(chars = {'b', 'a', 'l'}) + doAssert c == "X" + + block: + var a = "xxxxxx" + a.strip(chars={'x'}) + doAssert a.len == 0 + + block: + var a = "x" + a.strip(chars={'x'}) + doAssert a.len == 0 + + block: + var a = "x" + a.strip(chars={'1'}) + doAssert a.len == 1 + + block: + var a = "" + a.strip(chars={'x'}) + doAssert a.len == 0 + + block: + var a = "xxx xxx" + a.strip(chars={'x'}) + doAssert a == " " + + block: + var a = "xxx wind" + a.strip(chars={'x'}) + doAssert a == " wind" + + block: + var a = "xxx iii" + a.strip(chars={'i'}) + doAssert a == "xxx " + + block: + var a = "xxx iii" + doAssert a.dup(strip(chars = {'i'})) == "xxx " + doAssert a.dup(strip(chars = {' '})) == "xxx iii" + doAssert a.dup(strip(chars = {'x'})) == " iii" + doAssert a.dup(strip(chars = {'x', ' '})) == "iii" + doAssert a.dup(strip(chars = {'x', 'i'})) == " " + doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0 + + block: + var a = "x i" + doAssert a.dup(strip(chars = {'i'})) == "x " + doAssert a.dup(strip(chars = {' '})) == "x i" + doAssert a.dup(strip(chars = {'x'})) == " i" + doAssert a.dup(strip(chars = {'x', ' '})) == "i" + doAssert a.dup(strip(chars = {'x', 'i'})) == " " + doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0 + + block: + var a = "" + doAssert a.dup(strip(chars = {'i'})).len == 0 + doAssert a.dup(strip(chars = {' '})).len == 0 + doAssert a.dup(strip(chars = {'x'})).len == 0 + doAssert a.dup(strip(chars = {'x', ' '})).len == 0 + doAssert a.dup(strip(chars = {'x', 'i'})).len == 0 + doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0 + + block: + var a = " " + doAssert a.dup(strip(chars = {'i'})) == " " + doAssert a.dup(strip(chars = {' '})).len == 0 + doAssert a.dup(strip(chars = {'x'})) == " " + doAssert a.dup(strip(chars = {'x', ' '})).len == 0 + doAssert a.dup(strip(chars = {'x', 'i'})) == " " + doAssert a.dup(strip(chars = {'x', 'i', ' '})).len == 0 + + block: # setSlice + var a = "Hello, Nim!" + doassert a.dup(setSlice(7 .. 9)) == "Nim" + doAssert a.dup(setSlice(0 .. 0)) == "H" + doAssert a.dup(setSlice(0 .. 1)) == "He" + doAssert a.dup(setSlice(0 .. 10)) == a + doAssert a.dup(setSlice(1 .. 0)).len == 0 + doAssert a.dup(setSlice(20 .. -1)).len == 0 + + doAssertRaises(AssertionDefect): + discard a.dup(setSlice(-1 .. 1)) + + doAssertRaises(AssertionDefect): + discard a.dup(setSlice(1 .. 11)) + + block: # add + var a0 = "hi" + var b0 = "foobar" + when nimvm: + discard # pending bug #15952 + else: + a0.add b0.toOpenArray(1,3) + doAssert a0 == "hioob" + proc fn(c: openArray[char]): string = + result.add c + doAssert fn("def") == "def" + doAssert fn(['d','\0', 'f'])[2] == 'f' + +static: main() +main() diff --git a/tests/vm/tstring_openarray.nim b/tests/vm/tstring_openarray.nim index 1b8a1304c9cc2..9318344f85292 100644 --- a/tests/vm/tstring_openarray.nim +++ b/tests/vm/tstring_openarray.nim @@ -12,16 +12,16 @@ proc set_all[T](s: var openArray[T]; val: T) = for i in 0..