type
StringSlice* = ref object
str: ref string
start: int
stop: int
proc `$`*(str: StringSlice): string =
str.str[str.start .. str.stop]
proc newStringSlice*(str: string): StringSlice =
new result
new result.str
# result.str = new(ref string)
result.str[] = str
result.start = 0
result.stop = str.len-1
converter toStringSlice*(str: string): StringSlice =
newStringSlice(str)
proc `[]`*(str: StringSlice, slc: HSlice[int, int or BackwardsIndex]): StringSlice =
if slc.a < 0:
raise newException(IndexError, "index out of bounds")
new result
result.str = str.str
result.start = str.start + slc.a
when slc.b is BackwardsIndex:
if slc.b.int > str.len + 1:
raise newException(RangeError, "value out of range: " & $(str.len + 1 - slc.b.int))
result.stop = str.stop - slc.b.int + 1
else:
if slc.b + 1 < slc.a or slc.b > str.high:
raise newException(IndexError, "index out of bounds")
result.stop = str.start + slc.b
proc high*(str: StringSlice): int =
str.stop - str.start
proc len*(str: StringSlice): int =
str.high + 1
proc `&`*(sl1, sl2: StringSlice): StringSlice =
newStringSlice($sl1 & $sl2)
proc startsWith*[T: StringSlice or string](str: StringSlice, sub: T): bool =
if sub.len > str.len: return false
when T is StringSlice:
for i in sub.start..sub.stop:
if str.str[i + str.start - sub.start] != sub.str[i]: return false
else:
for idx, c in sub:
if str.str[idx + str.start] != c: return false
return true
proc `==`*[T: StringSlice or string](str: StringSlice, cmp: T): bool =
if cmp.isNil(): return false
if str.len != cmp.len: return false
when T is StringSlice:
for i in cmp.start..cmp.stop:
if str.str[i + str.start - cmp.start] != cmp.str[i]: return false
return true
else:
return str.startsWith(cmp)
import strutils
proc find*(a: SkipTable, s: StringSlice, sub: string, start: Natural = 0, last: Natural = 0): int =
## Searches for `sub` in `s` inside range `start`..`last` using preprocessed table `a`.
## If `last` is unspecified, it defaults to `s.high`.
##
## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
let
last = if last==0: s.high else: last
m = len(sub)
n = last + 1
# search:
var j = start
while j <= n - m:
block match:
for k in 0..m-1:
if sub[k] != s.str[s.start + k+j]: break match
return j
inc(j, a[s.str[s.start + j+m]])
return -1
when not (defined(js) or defined(nimdoc) or defined(nimscript)):
proc c_memchr(cstr: pointer, c: char, n: csize): pointer {.
importc: "memchr", header: "<string.h>" .}
const hasCStringBuiltin = true
else:
const hasCStringBuiltin = false
proc find*(s: StringSlice, sub: char, start: Natural = 0, last: Natural = 0): int =
## Searches for `sub` in `s` inside range `start`..`last`.
## If `last` is unspecified, it defaults to `s.high`.
##
## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
let last = if last==0: s.high else: last
when nimvm:
for i in start..last:
if sub == s.str[s.start + i]: return i
else:
when hasCStringBuiltin:
let found = c_memchr(s.str[s.start + start].unsafeAddr, sub, last-start+1)
if not found.isNil:
return cast[ByteAddress](found) -% cast[ByteAddress](s.str[s.start])
else:
for i in start..last:
if sub == s.str[s.start + i]: return i
return -1
proc find*(s: StringSlice, sub: string, start: Natural = 0, last: Natural = 0): int =
## Searches for `sub` in `s` inside range `start`..`last`.
## If `last` is unspecified, it defaults to `s.high`.
##
## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
if sub.len > s.len:
return -1
if sub.len == 1:
return find(s, sub[0], start, last)
var a {.noinit.}: SkipTable
initSkipTable(a, sub)
result = find(a, s, sub, start, last)
proc find*(s, sub: StringSlice): int =
s.find($sub)
proc strip*(s: StringSlice, first = true, last = true): StringSlice =
new result
result.str = s.str
result.start = s.start
result.stop = s.stop
if first:
for i in result.start..result.stop:
if not (result.str[i] in Whitespace): break
result.start += 1
if last:
for i in countdown(result.stop, result.start):
if not (result.str[i] in Whitespace): break
result.stop -= 1
iterator items*(a: StringSlice): char =
for i in a.start..a.stop:
yield a.str[i]
proc testStuff() =
var s = newStringSlice("Hello world")
var x = s[0 .. ^1][1 .. ^2][0 .. ^1]
s.str[][5] = 'c'
echo x
echo x.startsWith("elloc")
echo s.high
echo s.find("worl")
echo x.find("work")
static:
echo("--compiletime--")
teststuff()
echo("--runtime--")
testStuff()