File: replace.hpp

package info (click to toggle)
higan 098-2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 11,904 kB
  • ctags: 13,286
  • sloc: cpp: 108,285; ansic: 778; makefile: 32; sh: 18
file content (94 lines) | stat: -rw-r--r-- 3,391 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#pragma once

namespace nall {

template<bool Insensitive, bool Quoted>
auto string::_replace(rstring from, rstring to, long limit) -> string& {
  if(limit <= 0 || from.size() == 0) return *this;

  signed size = this->size();
  signed matches = 0;
  signed quoted = 0;

  //count matches first, so that we only need to reallocate memory once
  //(recording matches would also require memory allocation, so this is not done)
  { const char* p = data();
    for(signed n = 0; n <= size - (signed)from.size();) {
      if(Quoted) { if(p[n] == '\"') { quoted ^= 1; n++; continue; } if(quoted) { n++; continue; } }
      if(_compare<Insensitive>(p + n, size - n, from.data(), from.size())) { n++; continue; }

      if(++matches >= limit) break;
      n += from.size();
    }
  }
  if(matches == 0) return *this;

  //in-place overwrite
  if(to.size() == from.size()) {
    char* p = get();

    for(signed n = 0, remaining = matches, quoted = 0; n <= size - (signed)from.size();) {
      if(Quoted) { if(p[n] == '\"') { quoted ^= 1; n++; continue; } if(quoted) { n++; continue; } }
      if(_compare<Insensitive>(p + n, size - n, from.data(), from.size())) { n++; continue; }

      memory::copy(p + n, to.data(), to.size());

      if(!--remaining) break;
      n += from.size();
    }
  }

  //left-to-right shrink
  else if(to.size() < from.size()) {
    char* p = get();
    signed offset = 0;
    signed base = 0;

    for(signed n = 0, remaining = matches, quoted = 0; n <= size - (signed)from.size();) {
      if(Quoted) { if(p[n] == '\"') { quoted ^= 1; n++; continue; } if(quoted) { n++; continue; } }
      if(_compare<Insensitive>(p + n, size - n, from.data(), from.size())) { n++; continue; }

      if(offset) memory::move(p + offset, p + base, n - base);
      memory::copy(p + offset + (n - base), to.data(), to.size());
      offset += (n - base) + to.size();

      n += from.size();
      base = n;
      if(!--remaining) break;
    }

    memory::move(p + offset, p + base, size - base);
    resize(size - matches * (from.size() - to.size()));
  }

  //right-to-left expand
  else if(to.size() > from.size()) {
    resize(size + matches * (to.size() - from.size()));
    char* p = get();

    signed offset = this->size();
    signed base = size;

    for(signed n = size, remaining = matches; n >= (signed)from.size();) {  //quoted reused from parent scope since we are iterating backward
      if(Quoted) { if(p[n] == '\"') { quoted ^= 1; n--; continue; } if(quoted) { n--; continue; } }
      if(_compare<Insensitive>(p + n - from.size(), size - n + from.size(), from.data(), from.size())) { n--; continue; }

      memory::move(p + offset - (base - n), p + base - (base - n), base - n);
      memory::copy(p + offset - (base - n) - to.size(), to.data(), to.size());
      offset -= (base - n) + to.size();

      if(!--remaining) break;
      n -= from.size();
      base = n;
    }
  }

  return *this;
}

auto string::replace(rstring from, rstring to, long limit) -> string& { return _replace<0, 0>(from, to, limit); }
auto string::ireplace(rstring from, rstring to, long limit) -> string& { return _replace<1, 0>(from, to, limit); }
auto string::qreplace(rstring from, rstring to, long limit) -> string& { return _replace<0, 1>(from, to, limit); }
auto string::iqreplace(rstring from, rstring to, long limit) -> string& { return _replace<1, 1>(from, to, limit); }

};