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