Quick actions

cmd+k|ctrl+k

Navigation

Languages

Untitled

Snippet info

Language

Nim

Visibility

unlisted

Author

legacy-anonymous

Created

2018-05-07T21:54:10Z

Updated

2018-05-07T21:54:10Z


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()
INFO