Avi Drissman | e4622aa | 2022-09-08 20:36:06 | [diff] [blame] | 1 | // Copyright 2012 The Chromium Authors |
[email protected] | 19b8d82f | 2009-01-29 19:18:57 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
[email protected] | 19b8d82f | 2009-01-29 19:18:57 | [diff] [blame] | 5 | #include "base/version.h" |
| 6 | |
[email protected] | c014f2b3 | 2013-09-03 23:29:12 | [diff] [blame] | 7 | #include <stddef.h> |
| 8 | |
[email protected] | b566c11 | 2010-12-21 08:27:25 | [diff] [blame] | 9 | #include <algorithm> |
Hans Wennborg | e93e9b2 | 2021-07-12 16:58:29 | [diff] [blame] | 10 | #include <ostream> |
Helmut Januschka | 6bfb01f5 | 2024-05-28 15:47:45 | [diff] [blame] | 11 | #include <string_view> |
[email protected] | b566c11 | 2010-12-21 08:27:25 | [diff] [blame] | 12 | |
Hans Wennborg | c3cffa6 | 2020-04-27 10:09:12 | [diff] [blame] | 13 | #include "base/check_op.h" |
[email protected] | dfa049e | 2013-02-07 02:57:22 | [diff] [blame] | 14 | #include "base/strings/string_number_conversions.h" |
[email protected] | 5ae0b763e | 2013-02-07 23:01:39 | [diff] [blame] | 15 | #include "base/strings/string_split.h" |
[email protected] | c851cfd | 2013-06-10 20:11:14 | [diff] [blame] | 16 | #include "base/strings/string_util.h" |
[email protected] | 26931bc | 2010-03-25 22:19:04 | [diff] [blame] | 17 | |
[email protected] | 1f04ef4 | 2013-04-22 07:35:50 | [diff] [blame] | 18 | namespace base { |
| 19 | |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 20 | namespace { |
| 21 | |
| 22 | // Parses the |numbers| vector representing the different numbers |
| 23 | // inside the version string and constructs a vector of valid integers. It stops |
| 24 | // when it reaches an invalid item (including the wildcard character). |parsed| |
| 25 | // is the resulting integer vector. Function returns true if all numbers were |
| 26 | // parsed successfully, false otherwise. |
Helmut Januschka | 6bfb01f5 | 2024-05-28 15:47:45 | [diff] [blame] | 27 | bool ParseVersionNumbers(std::string_view version_str, |
wfh | bf68f4d5b | 2015-03-10 01:32:59 | [diff] [blame] | 28 | std::vector<uint32_t>* parsed) { |
Helmut Januschka | 6bfb01f5 | 2024-05-28 15:47:45 | [diff] [blame] | 29 | std::vector<std::string_view> numbers = |
brettw | 89365dc | 2015-06-16 05:52:47 | [diff] [blame] | 30 | SplitStringPiece(version_str, ".", KEEP_WHITESPACE, SPLIT_WANT_ALL); |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 31 | if (numbers.empty()) { |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 32 | return false; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 33 | } |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 34 | |
brettw | 89365dc | 2015-06-16 05:52:47 | [diff] [blame] | 35 | for (auto it = numbers.begin(); it != numbers.end(); ++it) { |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 36 | if (StartsWith(*it, "+", CompareCase::SENSITIVE)) { |
wfh | f151249 | 2015-02-22 05:41:39 | [diff] [blame] | 37 | return false; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 38 | } |
brettw | 89365dc | 2015-06-16 05:52:47 | [diff] [blame] | 39 | |
wfh | bf68f4d5b | 2015-03-10 01:32:59 | [diff] [blame] | 40 | unsigned int num; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 41 | if (!StringToUint(*it, &num)) { |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 42 | return false; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 43 | } |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 44 | |
wfh | f151249 | 2015-02-22 05:41:39 | [diff] [blame] | 45 | // This throws out leading zeros for the first item only. |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 46 | if (it == numbers.begin() && NumberToString(num) != *it) { |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 47 | return false; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 48 | } |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 49 | |
wfh | bf68f4d5b | 2015-03-10 01:32:59 | [diff] [blame] | 50 | // StringToUint returns unsigned int but Version fields are uint32_t. |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 51 | static_assert(sizeof(uint32_t) == sizeof(unsigned int), |
| 52 | "uint32_t must be same as unsigned int"); |
wfh | bf68f4d5b | 2015-03-10 01:32:59 | [diff] [blame] | 53 | parsed->push_back(num); |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 54 | } |
| 55 | return true; |
| 56 | } |
| 57 | |
| 58 | // Compares version components in |components1| with components in |
[email protected] | 8c71390f | 2013-06-21 05:00:30 | [diff] [blame] | 59 | // |components2|. Returns -1, 0 or 1 if |components1| is less than, equal to, |
| 60 | // or greater than |components2|, respectively. |
wfh | bf68f4d5b | 2015-03-10 01:32:59 | [diff] [blame] | 61 | int CompareVersionComponents(const std::vector<uint32_t>& components1, |
| 62 | const std::vector<uint32_t>& components2) { |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 63 | const size_t count = std::min(components1.size(), components2.size()); |
| 64 | for (size_t i = 0; i < count; ++i) { |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 65 | if (components1[i] > components2[i]) { |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 66 | return 1; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 67 | } |
| 68 | if (components1[i] < components2[i]) { |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 69 | return -1; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 70 | } |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 71 | } |
| 72 | if (components1.size() > components2.size()) { |
| 73 | for (size_t i = count; i < components1.size(); ++i) { |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 74 | if (components1[i] > 0) { |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 75 | return 1; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 76 | } |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 77 | } |
| 78 | } else if (components1.size() < components2.size()) { |
| 79 | for (size_t i = count; i < components2.size(); ++i) { |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 80 | if (components2[i] > 0) { |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 81 | return -1; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 82 | } |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 83 | } |
| 84 | } |
| 85 | return 0; |
| 86 | } |
| 87 | |
| 88 | } // namespace |
| 89 | |
Chris Watkins | bb7211c | 2017-11-29 07:16:38 | [diff] [blame] | 90 | Version::Version() = default; |
[email protected] | 9989c9bb | 2011-01-07 20:23:43 | [diff] [blame] | 91 | |
vmpstr | e65942b | 2016-02-25 00:50:31 | [diff] [blame] | 92 | Version::Version(const Version& other) = default; |
| 93 | |
David Bertoni | d8a8c00 | 2024-07-18 19:30:46 | [diff] [blame] | 94 | Version::Version(Version&& other) = default; |
| 95 | |
Chris Watkins | bb7211c | 2017-11-29 07:16:38 | [diff] [blame] | 96 | Version::~Version() = default; |
[email protected] | 1513bf8 | 2011-06-07 17:43:20 | [diff] [blame] | 97 | |
Helmut Januschka | 6bfb01f5 | 2024-05-28 15:47:45 | [diff] [blame] | 98 | Version::Version(std::string_view version_str) { |
wfh | bf68f4d5b | 2015-03-10 01:32:59 | [diff] [blame] | 99 | std::vector<uint32_t> parsed; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 100 | if (!ParseVersionNumbers(version_str, &parsed)) { |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 101 | return; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 102 | } |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 103 | |
[email protected] | 76002478 | 2011-06-07 17:21:30 | [diff] [blame] | 104 | components_.swap(parsed); |
| 105 | } |
[email protected] | 9989c9bb | 2011-01-07 20:23:43 | [diff] [blame] | 106 | |
Daniel Cheng | 31b35a56 | 2018-04-23 10:34:55 | [diff] [blame] | 107 | Version::Version(std::vector<uint32_t> components) |
| 108 | : components_(std::move(components)) {} |
staraz | 8fb3808 | 2016-07-25 18:48:21 | [diff] [blame] | 109 | |
[email protected] | 76002478 | 2011-06-07 17:21:30 | [diff] [blame] | 110 | bool Version::IsValid() const { |
| 111 | return (!components_.empty()); |
| 112 | } |
| 113 | |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 114 | // static |
Helmut Januschka | 6bfb01f5 | 2024-05-28 15:47:45 | [diff] [blame] | 115 | bool Version::IsValidWildcardString(std::string_view wildcard_string) { |
| 116 | std::string_view version_string = wildcard_string; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 117 | if (EndsWith(version_string, ".*", CompareCase::SENSITIVE)) { |
Greg Thompson | 15a9d17 | 2019-08-06 21:13:55 | [diff] [blame] | 118 | version_string = version_string.substr(0, version_string.size() - 2); |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 119 | } |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 120 | |
| 121 | Version version(version_string); |
| 122 | return version.IsValid(); |
| 123 | } |
| 124 | |
Helmut Januschka | 6bfb01f5 | 2024-05-28 15:47:45 | [diff] [blame] | 125 | int Version::CompareToWildcardString(std::string_view wildcard_string) const { |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 126 | DCHECK(IsValid()); |
| 127 | DCHECK(Version::IsValidWildcardString(wildcard_string)); |
| 128 | |
| 129 | // Default behavior if the string doesn't end with a wildcard. |
brettw | 89365dc | 2015-06-16 05:52:47 | [diff] [blame] | 130 | if (!EndsWith(wildcard_string, ".*", CompareCase::SENSITIVE)) { |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 131 | Version version(wildcard_string); |
| 132 | DCHECK(version.IsValid()); |
| 133 | return CompareTo(version); |
| 134 | } |
| 135 | |
wfh | bf68f4d5b | 2015-03-10 01:32:59 | [diff] [blame] | 136 | std::vector<uint32_t> parsed; |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 137 | const bool success = ParseVersionNumbers( |
| 138 | wildcard_string.substr(0, wildcard_string.length() - 2), &parsed); |
| 139 | DCHECK(success); |
| 140 | const int comparison = CompareVersionComponents(components_, parsed); |
| 141 | // If the version is smaller than the wildcard version's |parsed| vector, |
| 142 | // then the wildcard has no effect (e.g. comparing 1.2.3 and 1.3.*) and the |
| 143 | // version is still smaller. Same logic for equality (e.g. comparing 1.2.2 to |
| 144 | // 1.2.2.* is 0 regardless of the wildcard). Under this logic, |
| 145 | // 1.2.0.0.0.0 compared to 1.2.* is 0. |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 146 | if (comparison == -1 || comparison == 0) { |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 147 | return comparison; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 148 | } |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 149 | |
| 150 | // Catch the case where the digits of |parsed| are found in |components_|, |
| 151 | // which means that the two are equal since |parsed| has a trailing "*". |
| 152 | // (e.g. 1.2.3 vs. 1.2.* will return 0). All other cases return 1 since |
| 153 | // components is greater (e.g. 3.2.3 vs 1.*). |
| 154 | DCHECK_GT(parsed.size(), 0UL); |
| 155 | const size_t min_num_comp = std::min(components_.size(), parsed.size()); |
| 156 | for (size_t i = 0; i < min_num_comp; ++i) { |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 157 | if (components_[i] != parsed[i]) { |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 158 | return 1; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 159 | } |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 160 | } |
| 161 | return 0; |
| 162 | } |
| 163 | |
[email protected] | 19b8d82f | 2009-01-29 19:18:57 | [diff] [blame] | 164 | int Version::CompareTo(const Version& other) const { |
[email protected] | 76002478 | 2011-06-07 17:21:30 | [diff] [blame] | 165 | DCHECK(IsValid()); |
| 166 | DCHECK(other.IsValid()); |
[email protected] | 810b2508 | 2012-07-04 16:22:48 | [diff] [blame] | 167 | return CompareVersionComponents(components_, other.components_); |
[email protected] | 19b8d82f | 2009-01-29 19:18:57 | [diff] [blame] | 168 | } |
| 169 | |
Devlin Cronin | fde745d | 2019-09-25 22:21:57 | [diff] [blame] | 170 | std::string Version::GetString() const { |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 171 | if (!IsValid()) { |
John Rummell | 42b76ce4 | 2021-10-11 23:44:40 | [diff] [blame] | 172 | return "invalid"; |
Peter Kasting | 134ef9af | 2024-12-28 02:30:09 | [diff] [blame] | 173 | } |
John Rummell | 42b76ce4 | 2021-10-11 23:44:40 | [diff] [blame] | 174 | |
[email protected] | 19b8d82f | 2009-01-29 19:18:57 | [diff] [blame] | 175 | std::string version_str; |
[email protected] | 6dc910c | 2010-11-10 17:02:19 | [diff] [blame] | 176 | size_t count = components_.size(); |
| 177 | for (size_t i = 0; i < count - 1; ++i) { |
Raul Tambre | a9c1364 | 2019-03-25 13:34:42 | [diff] [blame] | 178 | version_str.append(NumberToString(components_[i])); |
[email protected] | 19b8d82f | 2009-01-29 19:18:57 | [diff] [blame] | 179 | version_str.append("."); |
| 180 | } |
Raul Tambre | a9c1364 | 2019-03-25 13:34:42 | [diff] [blame] | 181 | version_str.append(NumberToString(components_[count - 1])); |
[email protected] | 19b8d82f | 2009-01-29 19:18:57 | [diff] [blame] | 182 | return version_str; |
| 183 | } |
[email protected] | 1f04ef4 | 2013-04-22 07:35:50 | [diff] [blame] | 184 | |
robpercival | dcd8b10 | 2016-01-25 19:39:00 | [diff] [blame] | 185 | bool operator==(const Version& v1, const Version& v2) { |
| 186 | return v1.CompareTo(v2) == 0; |
| 187 | } |
| 188 | |
robpercival | dcd8b10 | 2016-01-25 19:39:00 | [diff] [blame] | 189 | bool operator<(const Version& v1, const Version& v2) { |
| 190 | return v1.CompareTo(v2) < 0; |
| 191 | } |
| 192 | |
| 193 | bool operator<=(const Version& v1, const Version& v2) { |
| 194 | return v1.CompareTo(v2) <= 0; |
| 195 | } |
| 196 | |
| 197 | bool operator>(const Version& v1, const Version& v2) { |
| 198 | return v1.CompareTo(v2) > 0; |
| 199 | } |
| 200 | |
| 201 | bool operator>=(const Version& v1, const Version& v2) { |
| 202 | return v1.CompareTo(v2) >= 0; |
| 203 | } |
| 204 | |
| 205 | std::ostream& operator<<(std::ostream& stream, const Version& v) { |
| 206 | return stream << v.GetString(); |
| 207 | } |
| 208 | |
[email protected] | 1f04ef4 | 2013-04-22 07:35:50 | [diff] [blame] | 209 | } // namespace base |