I want to add part of one string to another string. Naive way:
import strutils
var
a = "test"
b = "T"
b.add(a[1..a.high])
echo b
This invokes extra string copy because a[1..a.high] allocates new string. This case could be optimized into single memcpy without extra allocation. Something like this: b.add(a,range(1..a.high))?
Sounds like a task for term rewriting macros?
Yes I heard about this nice feature but cannot imagine how is should look like. I didn't noticed any in nim's core.
Never done it before, but i guess you would need a function that does the right thing:
proc appendToStringFromSliceOfAnotherStringRoutine(dst: var string, src: string, a, b: int) =
let ln = dst.len
dst.setLen(ln + b - a)
for i in 0 .. b - a: dst[ln + i] = src[a + i]
Then the reqriting template:
template appendToStringFromSliceOfAnotherString{add(dst, `[]`(src, `..`(a, b)))}(dst: var string, src: string, a, b: int) =
appendToStringFromSliceOfAnotherStringRoutine(dst, src, a, b)
Disclaimer - i've never tried to compile the code above.
Term rewriting sounds like overkill for this use case, you probably want something like OCaml's Buffer.add_substring (link).
I.e. something like:
proc addSlice*(s: var string, t: string, first, last: int) =
let last = if last > high(t): high(t) else: last
let first = if first < 0: 0 else: first
let n = if first <= last: last - first + 1 else: 0
if n > 0:
let k = len(s)
setLen(s, n + k)
copyMem(addr s[k], unsafeAddr t[first], n)
proc addSlice*(s: var string, t: string, slice: Slice) {.inline.} =
addSlice(s, t, slice.a, slice.b)
@yglukhov
After fixing mistype before int - it works!
import strutils
proc addSlice(s: var string, t: string, first, last: int) =
echo "Yes!"
let last = if last > high(t): high(t) else: last
let first = if first < 0: 0 else: first
let n = if first <= last: last - first + 1 else: 0
if n > 0:
let k = len(s)
setLen(s, n + k)
copyMem(addr s[k], unsafeAddr t[first], n)
template addToSlice{add(dst, `[]`(src, `..`(a, b)))}(dst: var string, src: string, a, b: int) =
addSlice(dst, src, a, b)
var
a = "test"
b = "T"
b.addSlice(a, 1,2) # Yes!
b.add(a[1..2]) # Yes!
Cool! As a side note, be aware that copyMem is not compatible with js backend.
Probably this optimization useless for js where byte-by-byte copying may be slower than object allocation.