Coverage Report

Created: 2026-10-26 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/solidity/libyul/YulString.h
Line
Count
Source
1
/*
2
  This file is part of solidity.
3
4
  solidity is free software: you can redistribute it and/or modify
5
  it under the terms of the GNU General Public License as published by
6
  the Free Software Foundation, either version 3 of the License, or
7
  (at your option) any later version.
8
9
  solidity is distributed in the hope that it will be useful,
10
  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
  GNU General Public License for more details.
13
14
  You should have received a copy of the GNU General Public License
15
  along with solidity.  If not, see <http://www.gnu.org/licenses/>.
16
*/
17
// SPDX-License-Identifier: GPL-3.0
18
/**
19
 * String abstraction that avoids copies.
20
 */
21
22
#pragma once
23
24
#include <libsolutil/UnorderedContainers.h>
25
26
#include <fmt/format.h>
27
28
#include <deque>
29
#include <memory>
30
#include <vector>
31
#include <string>
32
#include <string_view>
33
#include <functional>
34
35
namespace solidity::yul
36
{
37
38
/// Repository for YulStrings.
39
/// Owns the string data for all YulStrings, which can be referenced by a Handle.
40
/// A Handle consists of an ID (that depends on the insertion order of YulStrings and is potentially
41
/// non-deterministic) and a deterministic string hash.
42
class YulStringRepository
43
{
44
public:
45
  struct Handle
46
  {
47
    size_t id;
48
    std::uint64_t hash;
49
  };
50
51
  static YulStringRepository& instance()
52
280M
  {
53
280M
    static YulStringRepository inst;
54
280M
    return inst;
55
280M
  }
56
57
  Handle stringToHandle(std::string_view const _string)
58
81.3M
  {
59
81.3M
    if (_string.empty())
60
2.03M
      return { 0, emptyHash() };
61
79.3M
    std::uint64_t h = hash(_string);
62
79.3M
    auto range = m_hashToID.equal_range(h);
63
79.3M
    for (auto it = range.first; it != range.second; ++it)
64
35.2M
      if (m_strings[it->second] == _string)
65
35.2M
        return Handle{it->second, h};
66
44.0M
    m_strings.emplace_back(_string);
67
44.0M
    size_t id = m_strings.size() - 1;
68
44.0M
    m_hashToID.emplace_hint(range.second, std::make_pair(h, id));
69
70
44.0M
    return Handle{id, h};
71
79.3M
  }
72
198M
  std::string const& idToString(size_t _id) const { return m_strings.at(_id); }
73
74
  static std::uint64_t hash(std::string_view const v)
75
79.3M
  {
76
    // FNV hash - can be replaced by a better one, e.g. xxhash64
77
79.3M
    std::uint64_t hash = emptyHash();
78
79.3M
    for (char c: v)
79
859M
    {
80
859M
      hash *= 1099511628211u;
81
859M
      hash ^= static_cast<uint64_t>(c);
82
859M
    }
83
84
79.3M
    return hash;
85
79.3M
  }
86
172M
  static constexpr std::uint64_t emptyHash() { return 14695981039346656037u; }
87
  /// Clear the repository.
88
  /// Use with care - there cannot be any dangling YulString references.
89
  /// If references need to be cleared manually, register the callback via
90
  /// resetCallback.
91
  static void reset()
92
137k
  {
93
137k
    for (auto const& cb: resetCallbacks())
94
137k
      cb();
95
137k
    instance() = YulStringRepository{};
96
137k
  }
97
  /// Struct that registers a reset callback as a side-effect of its construction.
98
  /// Useful as static local variable to register a reset callback once.
99
  struct ResetCallback
100
  {
101
    ResetCallback(std::function<void()> _fun)
102
17
    {
103
17
      YulStringRepository::resetCallbacks().emplace_back(std::move(_fun));
104
17
    }
105
  };
106
107
private:
108
137k
  YulStringRepository() = default;
109
  YulStringRepository(YulStringRepository const&) = delete;
110
  YulStringRepository(YulStringRepository&&) = default;
111
  YulStringRepository& operator=(YulStringRepository const& _rhs) = delete;
112
137k
  YulStringRepository& operator=(YulStringRepository&& _rhs) = default;
113
114
  static std::vector<std::function<void()>>& resetCallbacks()
115
137k
  {
116
137k
    static std::vector<std::function<void()>> callbacks;
117
137k
    return callbacks;
118
137k
  }
119
120
  std::deque<std::string> m_strings = {""};
121
  util::unordered_multimap<std::uint64_t, size_t> m_hashToID = {{emptyHash(), 0}};
122
};
123
124
/// Wrapper around handles into the YulString repository.
125
/// Equality of two YulStrings is determined by comparing their ID.
126
/// The <-operator depends on the string hash and is not consistent
127
/// with string comparisons (however, it is still deterministic).
128
class YulString
129
{
130
public:
131
90.8M
  YulString() = default;
132
81.3M
  explicit YulString(std::string_view const _s): m_handle(YulStringRepository::instance().stringToHandle(_s)) {}
133
  YulString(YulString const&) = default;
134
  YulString(YulString&&) = default;
135
  YulString& operator=(YulString const&) = default;
136
  YulString& operator=(YulString&&) = default;
137
138
  /// This is not consistent with the string ordering!
139
  /// First compares the string hashes. If they are equal
140
  /// it checks for identical IDs (only identical strings have
141
  /// identical IDs and identical strings do not compare as "less").
142
  /// If the hashes are identical and the strings are distinct, it
143
  /// falls back to string comparison.
144
  std::strong_ordering operator<=>(YulString const& _other) const
145
13.3G
  {
146
13.3G
    if (auto const order = m_handle.hash <=> _other.m_handle.hash; order != std::strong_ordering::equal)
147
11.4G
      return order;
148
1.96G
    if (m_handle.id == _other.m_handle.id)
149
1.96G
      return std::strong_ordering::equal;
150
0
    return str().compare(_other.str()) <=> 0;
151
1.96G
  }
152
  /// Equality is determined based on the string ID.
153
2.58G
  bool operator==(YulString const& _other) const { return m_handle.id == _other.m_handle.id; }
154
155
98.3M
  bool empty() const { return m_handle.id == 0; }
156
  std::string const& str() const
157
198M
  {
158
198M
    return YulStringRepository::instance().idToString(m_handle.id);
159
198M
  }
160
161
3.46G
  uint64_t hash() const { return m_handle.hash; }
162
163
private:
164
  /// Handle of the string. Assumes that the empty string has ID zero.
165
  YulStringRepository::Handle m_handle{ 0, YulStringRepository::emptyHash() };
166
};
167
168
inline YulString operator""_yulname(char const* _string, std::size_t _size)
169
707k
{
170
707k
  return YulString(std::string_view(_string, _size));
171
707k
}
172
173
}
174
175
namespace std
176
{
177
template<> struct hash<solidity::yul::YulString>
178
{
179
  size_t operator()(solidity::yul::YulString const& x) const
180
3.27G
  {
181
3.27G
    return static_cast<size_t>(x.hash());
182
3.27G
  }
183
};
184
}