jaulib v1.3.8
Jau Support Library (C++, Java, ..)
Loading...
Searching...
No Matches
VersionNumber.hpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2010-2024 Gothel Software e.K.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25#ifndef JAU_VERSIONNUMBER_HPP_
26#define JAU_VERSIONNUMBER_HPP_
27
28#include <compare>
29#include <cstddef>
30#include <iostream>
31#include <ostream>
32#include <regex>
33#include <string>
34
35#include <jau/int_types.hpp>
36#include <jau/string_util.hpp>
37
38namespace jau::util {
39
40 /**
41 * Simple version number class containing a version number
42 * either being {@link #VersionNumber(int, int, int) defined explicit}
43 * or {@link #VersionNumber(String, String) derived from a string}.
44 *
45 * For the latter case, you can query whether a component has been defined explicitly by the given <code>versionString</code>,
46 * via {@link #hasMajor()}, {@link #hasMinor()} and {@link #hasSub()}.
47 *
48 * The state whether a component is defined explicitly <i>is not considered</i>
49 * in the {@link #hashCode()}, {@link #equals(Object)} or {@link #compareTo(Object)} methods,
50 * since the version number itself is treated regardless.
51 */
53 protected:
56 uint64_t m_git_ssha;
58
59 uint16_t m_state;
60
61 constexpr static const bool DBG_OUT = false;
62
63 constexpr static const uint16_t HAS_MAJOR = 1U << 0;
64 constexpr static const uint16_t HAS_MINOR = 1U << 1;
65 constexpr static const uint16_t HAS_SUB = 1U << 2;
66 constexpr static const uint16_t HAS_GIT_INFO = 1U << 3;
67
68 constexpr VersionNumber(int majorRev, int minorRev, int subMinorRev,
69 int gitCommits, uint64_t gitSSHA, bool gitDirty,
70 uint16_t _state) noexcept
71 : m_major(majorRev), m_minor(minorRev), m_sub(subMinorRev),
72 m_git_commits(gitCommits), m_git_ssha(gitSSHA), m_git_dirty(gitDirty),
73 m_state(_state) {}
74
75 public:
76 /**
77 * Explicit version number instantiation, with all components defined explicitly including git.
78 * @see #hasMajor()
79 * @see #hasMinor()
80 * @see #hasSub()
81 * @see #hasGitInfo()
82 */
83 constexpr VersionNumber(int majorRev, int minorRev, int subMinorRev, int gitCommits, uint64_t gitSSHA, bool gitDirty) noexcept
84 : VersionNumber(majorRev, minorRev, subMinorRev,
85 gitCommits, gitSSHA, gitDirty,
87 {}
88
89 /**
90 * Explicit version number instantiation, with all components defined explicitly excluding git.
91 * @see #hasMajor()
92 * @see #hasMinor()
93 * @see #hasSub()
94 */
95 constexpr VersionNumber(int majorRev, int minorRev, int subMinorRev) noexcept
96 : VersionNumber(majorRev, minorRev, subMinorRev, 0, 0, false, HAS_MAJOR | HAS_MINOR | HAS_SUB)
97 {}
98
99 /**
100 * Default ctor for zero version.
101 * @see #hasMajor()
102 * @see #hasMinor()
103 * @see #hasSub()
104 */
105 constexpr VersionNumber() noexcept
106 : VersionNumber(0, 0, 0, 0, 0, false, HAS_MAJOR | HAS_MINOR | HAS_SUB)
107 {}
108
109 constexpr VersionNumber(const VersionNumber&) noexcept = default;
110 constexpr VersionNumber(VersionNumber&&) noexcept = default;
111 constexpr VersionNumber& operator=(const VersionNumber&) noexcept = default;
112 constexpr VersionNumber& operator=(VersionNumber&&) noexcept = default;
113
114 /** Returns <code>true</code>, if all version components are zero, otherwise <code>false</code>. */
115 constexpr bool isZero() const noexcept {
116 return m_major == 0 && m_minor == 0 && m_sub == 0;
117 }
118
119 /** Returns <code>true</code>, if the major component is defined explicitly, otherwise <code>false</code>. Undefined components has the value <code>0</code>. */
120 constexpr bool hasMajor() const noexcept { return 0 != ( HAS_MAJOR & m_state ); }
121 /** Returns <code>true</code>, if the optional minor component is defined explicitly, otherwise <code>false</code>. Undefined components has the value <code>0</code>. */
122 constexpr bool hasMinor() const noexcept { return 0 != ( HAS_MINOR & m_state ); }
123 /** Returns <code>true</code>, if the optional sub component is defined explicitly, otherwise <code>false</code>. Undefined components has the value <code>0</code>. */
124 constexpr bool hasSub() const noexcept { return 0 != ( HAS_SUB & m_state ); }
125 /** Returns <code>true</code>, if the optional git information is defined explicitly, otherwise <code>false</code>. */
126 constexpr bool hasGitInfo() const noexcept { return 0 != ( HAS_GIT_INFO & m_state ); }
127
128 constexpr int major() const noexcept { return m_major; }
129 constexpr int minor() const noexcept { return m_minor; }
130 constexpr int sub() const noexcept { return m_sub; }
131 constexpr int git_commits() const noexcept { return m_git_commits; }
132 constexpr uint64_t git_ssha() const noexcept { return m_git_ssha; }
133 constexpr bool git_dirty() const noexcept { return m_git_dirty; }
134
135 /** Two way comparison operator */
136 constexpr bool operator==(const VersionNumber& vo) const noexcept {
137 return m_major == vo.m_major && m_minor == vo.m_minor && m_sub == vo.m_sub &&
138 m_git_commits == vo.m_git_commits && m_git_ssha == vo.m_git_ssha && m_git_dirty == vo.m_git_dirty;
139 }
140
141 /** Three way std::strong_ordering comparison operator */
142 constexpr std::strong_ordering operator<=>(const VersionNumber& vo) const noexcept {
143 if( m_major > vo.m_major ) {
144 return std::strong_ordering::greater;
145 } else if( m_major < vo.m_major ) {
146 return std::strong_ordering::less;
147 } else if( m_minor > vo.m_minor ) {
148 return std::strong_ordering::greater;
149 } else if( m_minor < vo.m_minor ) {
150 return std::strong_ordering::less;
151 } else if( m_sub > vo.m_sub ) {
152 return std::strong_ordering::greater;
153 } else if( m_sub < vo.m_sub ) {
154 return std::strong_ordering::less;
155 }
156 if( hasGitInfo() ) {
157 if( m_git_commits > vo.m_git_commits ) {
158 return std::strong_ordering::greater;
159 } else if( m_git_commits < vo.m_git_commits ) {
160 return std::strong_ordering::less;
161 } else if( !m_git_dirty && vo.m_git_dirty ) {
162 return std::strong_ordering::greater;
163 } else if( m_git_dirty && !vo.m_git_dirty ) {
164 return std::strong_ordering::less;
165 } else if( m_git_ssha > vo.m_git_ssha ) { // no sane interpretation of m_git_ssha
166 return std::strong_ordering::greater;
167 } else if( m_git_ssha < vo.m_git_ssha ) { // no sane interpretation of m_git_ssha
168 return std::strong_ordering::less;
169 }
170 }
171 return std::strong_ordering::equal;
172 }
173
174 constexpr std::size_t hash() const noexcept {
175 // 31 * x == (x << 5) - x
176 std::size_t h = 31 + major();
177 h = ((h << 5) - h) + minor();
178 h = ((h << 5) - h) + sub();
179 h = (h << 15) + git_ssha(); // 32-bit aligned
180 return (h << 1) + ( git_dirty() ? 1 : 0 );
181 }
182
183 std::string toString() const noexcept {
184 std::string res = std::to_string(m_major) + "." + std::to_string(m_minor) + "." + std::to_string(m_sub);
185 if( hasGitInfo() ) {
186 res.append(", git[post ").append(std::to_string(m_git_commits))
187 .append(", tip ").append(jau::to_hexstring(m_git_ssha, true))
188 .append(", ");
189 if( git_dirty() ) {
190 res.append("dirty");
191 } else {
192 res.append("clean");
193 }
194 res.append("]");
195 }
196 return res;
197 }
198
199 };
200
201 inline std::ostream& operator<<(std::ostream& out, const VersionNumber& v) noexcept {
202 return out << v.toString();
203 }
204
205 /**
206 * Simple version number class containing a version number
207 * either being {@link #VersionNumber(int, int, int) defined explicit}
208 * or {@link #VersionNumber(String, String) derived from a string}.
209 *
210 * For the latter case, you can query whether a component has been defined explicitly by the given <code>versionString</code>,
211 * via {@link #hasMajor()}, {@link #hasMinor()} and {@link #hasSub()}.
212 *
213 * The state whether a component is defined explicitly <i>is not considered</i>
214 * in the {@link #hashCode()}, {@link #equals(Object)} or {@link #compareTo(Object)} methods,
215 * since the version number itself is treated regardless.
216 */
218 private:
219 ssize_t m_strEnd;
220 std::string m_version_str;
221
222 protected:
223 constexpr VersionNumberString(int majorRev, int minorRev, int subMinorRev,
224 int gitCommits, uint64_t gitSSHA, bool gitDirty,
225 uint16_t _state, ssize_t strEnd, std::string _version_str) noexcept
226 : VersionNumber(majorRev, minorRev, subMinorRev, gitCommits, gitSSHA, gitDirty, _state),
227 m_strEnd(strEnd), m_version_str(std::move(_version_str)) {}
228
229 public:
230 static std::regex getNonGitPattern(const std::string& delim) {
231 // v0.0.1-3-gd55f8a3-dirty
232 // return std::regex( R"(\D*(\d+)[^\.\s]*(?:\.\D*(\d+)[^\.\s]*(?:\.\D*(\d+))?)?)");
233 return std::regex( R"(\D*(\d+)[^\)" + delim + R"(\s]*(?:\)" + delim + R"(\D*(\d+)[^\)" + delim + R"(\s]*(?:\)" + delim + R"(\D*(\d+))?)?)");
234 }
235
236 static std::regex getGitPattern(const std::string& delim) {
237 // v0.0.1-3-gd55f8a3-dirty
238 // return std::regex( R"(\D*(\d+)[^\.\s]*(?:\.\D*(\d+)[^\.\s]*(?:\.\D*(\d+)(?:\-(\d+)\-g([0-9a-f]+)(\-dirty)?)?)?)?)");
239 return std::regex( R"(\D*(\d+)[^\)" + delim + R"(\s]*(?:\)" + delim + R"(\D*(\d+)[^\)" + delim + R"(\s]*(?:\)" + delim + R"(\D*(\d+)(?:\-(\d+)\-g([0-9a-f]+)(\-dirty)?)?)?)?)");
240 }
241
242 static const std::regex& getDefaultPattern() noexcept { // NOLINT(bugprone-exception-escape)
243 static std::regex defPattern = getGitPattern(".");
244 return defPattern;
245 }
246
247 /**
248 * Default ctor for zero version.
249 * @see #hasMajor()
250 * @see #hasMinor()
251 * @see #hasSub()
252 */
253 constexpr VersionNumberString() noexcept
254 : VersionNumberString(0, 0, 0, 0, 0, false, -1, HAS_MAJOR | HAS_MINOR | HAS_SUB, "")
255 {}
256
257 VersionNumberString(const VersionNumberString&) noexcept = default;
259 VersionNumberString& operator=(const VersionNumberString&) noexcept = default;
260 VersionNumberString& operator=(VersionNumberString&&) noexcept = default;
261
262 /**
263 * String derived version number instantiation.
264 * <p>
265 * You can query whether a component has been defined explicitly by the given <code>versionString</code>,
266 * via {@link #hasMajor()}, {@link #hasMinor()} and {@link #hasSub()}.
267 * </p>
268 * @param versionString should be given as [MAJOR[.MINOR[.SUB]]]
269 * @param versionPattern the {@link java.util.regex.Pattern pattern} parser, must be compatible w/ {@link #getVersionNumberPattern(String)}
270 *
271 * @see #hasMajor()
272 * @see #hasMinor()
273 * @see #hasSub()
274 */
275 VersionNumberString(const std::string& versionString, const std::regex& versionPattern) noexcept
276 : VersionNumber(0, 0, 0, 0, 0, false, 0),
277 m_strEnd(0), m_version_str(versionString)
278 {
279 // group1: \d* == digits major
280 // group2: \d* == digits minor
281 // group3: \d* == digits sub
282 // group4: \d* == digits commit-count
283 // group5: hex == hex short-sha
284 // group6: str == dirty
285 std::smatch match;
286 if( std::regex_search(versionString, match, versionPattern) ) {
287 m_strEnd = match.position() + match.length();
288 if constexpr ( DBG_OUT ) {
289 std::cout << "XXX: " << versionString << std::endl;
290 std::cout << "XXX: match pos " << match.position() << ", len " << match.length() << ", sz " << match.size() << std::endl;
291 for(size_t i=0; i<match.size(); ++i) {
292 const std::string& s = match[i];
293 std::cout << "- [" << i << "]: '" << s << "', len " << s.length() << std::endl;
294 }
295 }
296 if( match.size() >= 2 && match[1].length() > 0 ) {
297 m_major = std::stoi(match[1]);
299 if( match.size() >= 3 && match[2].length() > 0 ) {
300 m_minor = std::stoi(match[2]);
302 if( match.size() >= 4 && match[3].length() > 0 ) {
303 m_sub = std::stoi(match[3]);
304 m_state |= HAS_SUB;
305 if( match.size() >= 5 && match[4].length() > 0 ) {
306 m_git_commits = std::stoi(match[4]);
308 if constexpr ( DBG_OUT ) {
309 std::cout << "XXX: git commits " << m_git_commits << std::endl;
310 }
311 if( match.size() >= 6 && match[5].length() > 0 ) {
313 if constexpr ( DBG_OUT ) {
314 std::cout << "XXX: git ssha '" << match[5] << "', hex " << jau::to_hexstring(m_git_ssha) << ", dec " << m_git_ssha << std::endl;
315 }
316 if( match.size() >= 7 && match[6].length() > 0 ) {
317 m_git_dirty = true;
318 if constexpr ( DBG_OUT ) {
319 std::cout << "XXX: git dirty " << m_git_dirty << std::endl;
320 }
321 }
322 }
323 }
324 }
325 }
326 }
327 }
328 }
329
330 /**
331 * String derived version number instantiation.
332 * <p>
333 * Utilizing the default {@link java.util.regex.Pattern pattern} parser with delimiter "<b>.</b>", see {@link #getDefaultVersionNumberPattern()}.
334 * </p>
335 * <p>
336 * You can query whether a component has been defined explicitly by the given <code>versionString</code>,
337 * via {@link #hasMajor()}, {@link #hasMinor()} and {@link #hasSub()}.
338 * </p>
339 * @param versionString should be given as [MAJOR[.MINOR[.SUB]]]
340 *
341 * @see #hasMajor()
342 * @see #hasMinor()
343 * @see #hasSub()
344 */
348
349 /**
350 * String derived version number instantiation.
351 * <p>
352 * Utilizing {@link java.util.regex.Pattern pattern} parser created via {@link #getVersionNumberPattern(String)}.
353 * </p>
354 * <p>
355 * You can query whether a component has been defined explicitly by the given <code>versionString</code>,
356 * via {@link #hasMajor()}, {@link #hasMinor()} and {@link #hasSub()}.
357 * </p>
358 * @param versionString should be given as [MAJOR[.MINOR[.SUB]]]
359 * @param delim the delimiter, e.g. "."
360 *
361 * @see #hasMajor()
362 * @see #hasMinor()
363 * @see #hasSub()
364 */
365 VersionNumberString(const std::string& versionString, const std::string& delim) noexcept
367 { }
368
369
370 /** Returns true if constructed with a `version-string`, otherwise false. */
371 constexpr bool hasString() const noexcept { return m_version_str.length() > 0; }
372 /** Returns the used `version-string`, empty if not constructed with such. */
373 constexpr const std::string& versionString() const noexcept { return m_version_str; }
374
375 /**
376 * If constructed with `version-string`, returns the string offset <i>after</i> the last matching character,
377 * or <code>0</code> if none matched, or <code>-1</code> if not constructed with a string.
378 */
379 constexpr ssize_t endOfStringMatch() const noexcept { return m_strEnd; }
380
381 std::string toString() const noexcept {
382 std::string res = std::to_string(m_major) + "." + std::to_string(m_minor) + "." + std::to_string(m_sub);
383 if( hasGitInfo() ) {
384 res.append(", git[post ").append(std::to_string(m_git_commits))
385 .append(", tip ").append(jau::to_hexstring(m_git_ssha, true))
386 .append(", ");
387 if( git_dirty() ) {
388 res.append("dirty");
389 } else {
390 res.append("clean");
391 }
392 res.append("]");
393 }
394 if( hasString() ) {
395 res.append(" (").append(m_version_str).append(")");
396 }
397 return res;
398 }
399
400 };
401
402 inline std::ostream& operator<<(std::ostream& out, const VersionNumberString& v) noexcept {
403 return out << v.toString();
404 }
405
406} // namespace jau::util
407
408namespace std {
409 template<> struct hash<jau::util::VersionNumber>
410 {
411 std::size_t operator()(const jau::util::VersionNumber& v) const noexcept {
412 return v.hash();
413 }
414 };
415}
416
417#endif /* JAU_VERSIONNUMBER_HPP_ */
Simple version number class containing a version number either being defined explicit or derived from...
static std::regex getGitPattern(const std::string &delim)
VersionNumberString(VersionNumberString &&) noexcept=default
constexpr bool hasString() const noexcept
Returns true if constructed with a version-string, otherwise false.
static std::regex getNonGitPattern(const std::string &delim)
VersionNumberString(const std::string &versionString) noexcept
String derived version number instantiation.
static const std::regex & getDefaultPattern() noexcept
std::string toString() const noexcept
constexpr const std::string & versionString() const noexcept
Returns the used version-string, empty if not constructed with such.
VersionNumberString(const std::string &versionString, const std::string &delim) noexcept
String derived version number instantiation.
constexpr VersionNumberString() noexcept
Default ctor for zero version.
VersionNumberString(const VersionNumberString &) noexcept=default
constexpr VersionNumberString(int majorRev, int minorRev, int subMinorRev, int gitCommits, uint64_t gitSSHA, bool gitDirty, uint16_t _state, ssize_t strEnd, std::string _version_str) noexcept
constexpr ssize_t endOfStringMatch() const noexcept
If constructed with version-string, returns the string offset after the last matching character,...
Simple version number class containing a version number either being defined explicit or derived from...
std::string toString() const noexcept
constexpr bool hasMajor() const noexcept
Returns true, if the major component is defined explicitly, otherwise false.
constexpr VersionNumber() noexcept
Default ctor for zero version.
constexpr uint64_t git_ssha() const noexcept
static constexpr const uint16_t HAS_MINOR
static constexpr const uint16_t HAS_SUB
constexpr int major() const noexcept
constexpr VersionNumber(int majorRev, int minorRev, int subMinorRev) noexcept
Explicit version number instantiation, with all components defined explicitly excluding git.
static constexpr const uint16_t HAS_MAJOR
constexpr VersionNumber(int majorRev, int minorRev, int subMinorRev, int gitCommits, uint64_t gitSSHA, bool gitDirty, uint16_t _state) noexcept
constexpr std::size_t hash() const noexcept
static constexpr const uint16_t HAS_GIT_INFO
constexpr bool isZero() const noexcept
Returns true, if all version components are zero, otherwise false.
constexpr bool hasMinor() const noexcept
Returns true, if the optional minor component is defined explicitly, otherwise false.
constexpr VersionNumber(int majorRev, int minorRev, int subMinorRev, int gitCommits, uint64_t gitSSHA, bool gitDirty) noexcept
Explicit version number instantiation, with all components defined explicitly including git.
static constexpr const bool DBG_OUT
constexpr bool operator==(const VersionNumber &vo) const noexcept
Two way comparison operator.
constexpr int sub() const noexcept
constexpr bool hasGitInfo() const noexcept
Returns true, if the optional git information is defined explicitly, otherwise false.
constexpr int git_commits() const noexcept
constexpr int minor() const noexcept
constexpr bool git_dirty() const noexcept
constexpr VersionNumber(VersionNumber &&) noexcept=default
constexpr bool hasSub() const noexcept
Returns true, if the optional sub component is defined explicitly, otherwise false.
constexpr std::strong_ordering operator<=>(const VersionNumber &vo) const noexcept
Three way std::strong_ordering comparison operator.
constexpr VersionNumber(const VersionNumber &) noexcept=default
std::string to_hexstring(value_type const &v, const bool skipLeading0x=false) noexcept
Produce a lower-case hexadecimal string representation with leading 0x in MSB of the given pointer.
uint64_t from_hexstring(std::string const &s, const bool lsbFirst=!jau::is_little_endian(), const bool checkLeading0x=true) noexcept
Converts a given hexadecimal string representation into a uint64_t value according to hexStringBytes(...
std::ostream & operator<<(std::ostream &out, const VersionNumber &v) noexcept
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition backtrace.hpp:32
STL namespace.
std::size_t operator()(const jau::util::VersionNumber &v) const noexcept