1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/version.h"
6
7 #include <stddef.h>
8
9 #include <algorithm>
10 #include <ostream>
11 #include <string_view>
12
13 #include "base/check_op.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/string_util.h"
17
18 namespace base {
19
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.
ParseVersionNumbers(std::string_view version_str,std::vector<uint32_t> * parsed)27 bool ParseVersionNumbers(std::string_view version_str,
28 std::vector<uint32_t>* parsed) {
29 std::vector<std::string_view> numbers =
30 SplitStringPiece(version_str, ".", KEEP_WHITESPACE, SPLIT_WANT_ALL);
31 if (numbers.empty())
32 return false;
33
34 for (auto it = numbers.begin(); it != numbers.end(); ++it) {
35 if (StartsWith(*it, "+", CompareCase::SENSITIVE))
36 return false;
37
38 unsigned int num;
39 if (!StringToUint(*it, &num))
40 return false;
41
42 // This throws out leading zeros for the first item only.
43 if (it == numbers.begin() && NumberToString(num) != *it)
44 return false;
45
46 // StringToUint returns unsigned int but Version fields are uint32_t.
47 static_assert(sizeof (uint32_t) == sizeof (unsigned int),
48 "uint32_t must be same as unsigned int");
49 parsed->push_back(num);
50 }
51 return true;
52 }
53
54 // Compares version components in |components1| with components in
55 // |components2|. Returns -1, 0 or 1 if |components1| is less than, equal to,
56 // or greater than |components2|, respectively.
CompareVersionComponents(const std::vector<uint32_t> & components1,const std::vector<uint32_t> & components2)57 int CompareVersionComponents(const std::vector<uint32_t>& components1,
58 const std::vector<uint32_t>& components2) {
59 const size_t count = std::min(components1.size(), components2.size());
60 for (size_t i = 0; i < count; ++i) {
61 if (components1[i] > components2[i])
62 return 1;
63 if (components1[i] < components2[i])
64 return -1;
65 }
66 if (components1.size() > components2.size()) {
67 for (size_t i = count; i < components1.size(); ++i) {
68 if (components1[i] > 0)
69 return 1;
70 }
71 } else if (components1.size() < components2.size()) {
72 for (size_t i = count; i < components2.size(); ++i) {
73 if (components2[i] > 0)
74 return -1;
75 }
76 }
77 return 0;
78 }
79
80 } // namespace
81
82 Version::Version() = default;
83
84 Version::Version(const Version& other) = default;
85
86 Version::Version(Version&& other) = default;
87
88 Version::~Version() = default;
89
Version(std::string_view version_str)90 Version::Version(std::string_view version_str) {
91 std::vector<uint32_t> parsed;
92 if (!ParseVersionNumbers(version_str, &parsed))
93 return;
94
95 components_.swap(parsed);
96 }
97
Version(std::vector<uint32_t> components)98 Version::Version(std::vector<uint32_t> components)
99 : components_(std::move(components)) {}
100
IsValid() const101 bool Version::IsValid() const {
102 return (!components_.empty());
103 }
104
105 // static
IsValidWildcardString(std::string_view wildcard_string)106 bool Version::IsValidWildcardString(std::string_view wildcard_string) {
107 std::string_view version_string = wildcard_string;
108 if (EndsWith(version_string, ".*", CompareCase::SENSITIVE))
109 version_string = version_string.substr(0, version_string.size() - 2);
110
111 Version version(version_string);
112 return version.IsValid();
113 }
114
CompareToWildcardString(std::string_view wildcard_string) const115 int Version::CompareToWildcardString(std::string_view wildcard_string) const {
116 DCHECK(IsValid());
117 DCHECK(Version::IsValidWildcardString(wildcard_string));
118
119 // Default behavior if the string doesn't end with a wildcard.
120 if (!EndsWith(wildcard_string, ".*", CompareCase::SENSITIVE)) {
121 Version version(wildcard_string);
122 DCHECK(version.IsValid());
123 return CompareTo(version);
124 }
125
126 std::vector<uint32_t> parsed;
127 const bool success = ParseVersionNumbers(
128 wildcard_string.substr(0, wildcard_string.length() - 2), &parsed);
129 DCHECK(success);
130 const int comparison = CompareVersionComponents(components_, parsed);
131 // If the version is smaller than the wildcard version's |parsed| vector,
132 // then the wildcard has no effect (e.g. comparing 1.2.3 and 1.3.*) and the
133 // version is still smaller. Same logic for equality (e.g. comparing 1.2.2 to
134 // 1.2.2.* is 0 regardless of the wildcard). Under this logic,
135 // 1.2.0.0.0.0 compared to 1.2.* is 0.
136 if (comparison == -1 || comparison == 0)
137 return comparison;
138
139 // Catch the case where the digits of |parsed| are found in |components_|,
140 // which means that the two are equal since |parsed| has a trailing "*".
141 // (e.g. 1.2.3 vs. 1.2.* will return 0). All other cases return 1 since
142 // components is greater (e.g. 3.2.3 vs 1.*).
143 DCHECK_GT(parsed.size(), 0UL);
144 const size_t min_num_comp = std::min(components_.size(), parsed.size());
145 for (size_t i = 0; i < min_num_comp; ++i) {
146 if (components_[i] != parsed[i])
147 return 1;
148 }
149 return 0;
150 }
151
CompareTo(const Version & other) const152 int Version::CompareTo(const Version& other) const {
153 DCHECK(IsValid());
154 DCHECK(other.IsValid());
155 return CompareVersionComponents(components_, other.components_);
156 }
157
GetString() const158 std::string Version::GetString() const {
159 if (!IsValid())
160 return "invalid";
161
162 std::string version_str;
163 size_t count = components_.size();
164 for (size_t i = 0; i < count - 1; ++i) {
165 version_str.append(NumberToString(components_[i]));
166 version_str.append(".");
167 }
168 version_str.append(NumberToString(components_[count - 1]));
169 return version_str;
170 }
171
operator ==(const Version & v1,const Version & v2)172 bool operator==(const Version& v1, const Version& v2) {
173 return v1.CompareTo(v2) == 0;
174 }
175
operator !=(const Version & v1,const Version & v2)176 bool operator!=(const Version& v1, const Version& v2) {
177 return !(v1 == v2);
178 }
179
operator <(const Version & v1,const Version & v2)180 bool operator<(const Version& v1, const Version& v2) {
181 return v1.CompareTo(v2) < 0;
182 }
183
operator <=(const Version & v1,const Version & v2)184 bool operator<=(const Version& v1, const Version& v2) {
185 return v1.CompareTo(v2) <= 0;
186 }
187
operator >(const Version & v1,const Version & v2)188 bool operator>(const Version& v1, const Version& v2) {
189 return v1.CompareTo(v2) > 0;
190 }
191
operator >=(const Version & v1,const Version & v2)192 bool operator>=(const Version& v1, const Version& v2) {
193 return v1.CompareTo(v2) >= 0;
194 }
195
operator <<(std::ostream & stream,const Version & v)196 std::ostream& operator<<(std::ostream& stream, const Version& v) {
197 return stream << v.GetString();
198 }
199
200 } // namespace base
201