summaryrefslogtreecommitdiff
path: root/Source/cmString.cxx
blob: aefaa6465e1c796eb501829c2978efec94c75635 (plain)
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
// NOLINTNEXTLINE(bugprone-reserved-identifier)
#define _SCL_SECURE_NO_WARNINGS

#include "cmString.hxx"

#include <memory>
#include <ostream>
#include <stdexcept>
#include <string>
#include <type_traits>

namespace cm {

static std::string const empty_string_;

void String::internally_mutate_to_stable_string()
{
  // We assume that only one thread mutates this instance at
  // a time even if we point to a shared string buffer referenced
  // by other threads.
  *this = String(this->data(), this->size());
}

bool String::is_stable() const
{
  return this->str_if_stable() != nullptr;
}

void String::stabilize()
{
  if (this->is_stable()) {
    return;
  }
  this->internally_mutate_to_stable_string();
}

std::string const* String::str_if_stable() const
{
  if (!this->data()) {
    // We view no string.
    // This is stable for the lifetime of our current value.
    return &empty_string_;
  }

  if (this->string_ && this->data() == this->string_->data() &&
      this->size() == this->string_->size()) {
    // We view an entire string.
    // This is stable for the lifetime of our current value.
    return this->string_.get();
  }

  return nullptr;
}

std::string const& String::str()
{
  if (std::string const* s = this->str_if_stable()) {
    return *s;
  }
  // Mutate to hold a std::string that is stable for the lifetime
  // of our current value.
  this->internally_mutate_to_stable_string();
  return *this->string_;
}

const char* String::c_str()
{
  const char* c = this->data();
  if (c == nullptr) {
    return c;
  }

  // We always point into a null-terminated string so it is safe to
  // access one past the end.  If it is a null byte then we can use
  // the pointer directly.
  if (c[this->size()] == '\0') {
    return c;
  }

  // Mutate to hold a std::string so we can get a null terminator.
  this->internally_mutate_to_stable_string();
  c = this->string_->c_str();
  return c;
}

String& String::insert(size_type index, size_type count, char ch)
{
  std::string s;
  s.reserve(this->size() + count);
  s.assign(this->data(), this->size());
  s.insert(index, count, ch);
  return *this = std::move(s);
}

String& String::erase(size_type index, size_type count)
{
  if (index > this->size()) {
    throw std::out_of_range("Index out of range in String::erase");
  }
  size_type const rcount = std::min(count, this->size() - index);
  size_type const rindex = index + rcount;
  std::string s;
  s.reserve(this->size() - rcount);
  s.assign(this->data(), index);
  s.append(this->data() + rindex, this->size() - rindex);
  return *this = std::move(s);
}

String String::substr(size_type pos, size_type count) const
{
  if (pos > this->size()) {
    throw std::out_of_range("Index out of range in String::substr");
  }
  return String(*this, pos, count);
}

String::String(std::string&& s, Private)
  : string_(std::make_shared<std::string>(std::move(s)))
  , view_(this->string_->data(), this->string_->size())
{
}

String::size_type String::copy(char* dest, size_type count,
                               size_type pos) const
{
  return this->view_.copy(dest, count, pos);
}

std::ostream& operator<<(std::ostream& os, String const& s)
{
  return os.write(s.data(), s.size());
}

std::string& operator+=(std::string& self, String const& s)
{
  return self += s.view();
}

String IntoString<char*>::into_string(const char* s)
{
  if (!s) {
    return String();
  }
  return std::string(s);
}

string_view AsStringView<String>::view(String const& s)
{
  return s.view();
}

} // namespace cm