jaulib v1.4.0-2-g788cf73
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 * Explicit version number instantiation, with all components defined explicitly excluding git.
90 * @see #hasMajor()
91 * @see #hasMinor()
92 * @see #hasSub()
93 */
94 constexpr VersionNumber(int majorRev, int minorRev, int subMinorRev) noexcept
95 : VersionNumber(majorRev, minorRev, subMinorRev, 0, 0, false, HAS_MAJOR | HAS_MINOR | HAS_SUB) { }
96
97 /**
98 * Default ctor for zero version.
99 * @see #hasMajor()
100 * @see #hasMinor()
101 * @see #hasSub()
102 */
103 constexpr VersionNumber() noexcept
104 : VersionNumber(0, 0, 0, 0, 0, false, HAS_MAJOR | HAS_MINOR | HAS_SUB) { }
105
106 constexpr VersionNumber(const VersionNumber&) noexcept = default;
107 constexpr VersionNumber(VersionNumber&&) noexcept = default;
108 constexpr VersionNumber& operator=(const VersionNumber&) noexcept = default;
109 constexpr VersionNumber& operator=(VersionNumber&&) noexcept = default;
110
111 /** Returns <code>true</code>, if all version components are zero, otherwise <code>false</code>. */
112 constexpr bool isZero() const noexcept {
113 return m_major == 0 && m_minor == 0 && m_sub == 0;
114 }
115
116 /** Returns <code>true</code>, if the major component is defined explicitly, otherwise <code>false</code>. Undefined components has the value <code>0</code>. */
117 constexpr bool hasMajor() const noexcept { return 0 != (HAS_MAJOR & m_state); }
118 /** Returns <code>true</code>, if the optional minor component is defined explicitly, otherwise <code>false</code>. Undefined components has the value <code>0</code>. */
119 constexpr bool hasMinor() const noexcept { return 0 != (HAS_MINOR & m_state); }
120 /** Returns <code>true</code>, if the optional sub component is defined explicitly, otherwise <code>false</code>. Undefined components has the value <code>0</code>. */
121 constexpr bool hasSub() const noexcept { return 0 != (HAS_SUB & m_state); }
122 /** Returns <code>true</code>, if the optional git information is defined explicitly, otherwise <code>false</code>. */
123 constexpr bool hasGitInfo() const noexcept { return 0 != (HAS_GIT_INFO & m_state); }
124
125 constexpr int major() const noexcept { return m_major; }
126 constexpr int minor() const noexcept { return m_minor; }
127 constexpr int sub() const noexcept { return m_sub; }
128 constexpr int git_commits() const noexcept { return m_git_commits; }
129 constexpr uint64_t git_ssha() const noexcept { return m_git_ssha; }
130 constexpr bool git_dirty() const noexcept { return m_git_dirty; }
131
132 /** Two way comparison operator */
133 constexpr bool operator==(const VersionNumber& vo) const noexcept {
134 return m_major == vo.m_major && m_minor == vo.m_minor && m_sub == vo.m_sub &&
135 m_git_commits == vo.m_git_commits && m_git_ssha == vo.m_git_ssha && m_git_dirty == vo.m_git_dirty;
136 }
137
138 /** Three way std::strong_ordering comparison operator */
139 constexpr std::strong_ordering operator<=>(const VersionNumber& vo) const noexcept {
140 if ( m_major > vo.m_major ) {
141 return std::strong_ordering::greater;
142 } else if ( m_major < vo.m_major ) {
143 return std::strong_ordering::less;
144 } else if ( m_minor > vo.m_minor ) {
145 return std::strong_ordering::greater;
146 } else if ( m_minor < vo.m_minor ) {
147 return std::strong_ordering::less;
148 } else if ( m_sub > vo.m_sub ) {
149 return std::strong_ordering::greater;
150 } else if ( m_sub < vo.m_sub ) {
151 return std::strong_ordering::less;
152 }
153 if ( hasGitInfo() ) {
154 if ( m_git_commits > vo.m_git_commits ) {
155 return std::strong_ordering::greater;
156 } else if ( m_git_commits < vo.m_git_commits ) {
157 return std::strong_ordering::less;
158 } else if ( !m_git_dirty && vo.m_git_dirty ) {
159 return std::strong_ordering::greater;
160 } else if ( m_git_dirty && !vo.m_git_dirty ) {
161 return std::strong_ordering::less;
162 } else if ( m_git_ssha > vo.m_git_ssha ) { // no sane interpretation of m_git_ssha
163 return std::strong_ordering::greater;
164 } else if ( m_git_ssha < vo.m_git_ssha ) { // no sane interpretation of m_git_ssha
165 return std::strong_ordering::less;
166 }
167 }
168 return std::strong_ordering::equal;
169 }
170
171 constexpr std::size_t hash() const noexcept {
172 // 31 * x == (x << 5) - x
173 std::size_t h = 31 + major();
174 h = ((h << 5) - h) + minor();
175 h = ((h << 5) - h) + sub();
176 h = (h << 15) + git_ssha(); // 32-bit aligned
177 return (h << 1) + ( git_dirty() ? 1 : 0 );
178 }
179
180 std::string toString() const noexcept {
181 std::string res = std::to_string(m_major) + "." + std::to_string(m_minor) + "." + std::to_string(m_sub);
182 if( hasGitInfo() ) {
183 res.append(", git[post ").append(std::to_string(m_git_commits))
184 .append(", tip ").append(jau::toHexString(m_git_ssha, jau::lb_endian_t::little))
185 .append(", ");
186 if( git_dirty() ) {
187 res.append("dirty");
188 } else {
189 res.append("clean");
190 }
191 res.append("]");
192 }
193 return res;
194 }
195 };
196
197 inline std::ostream& operator<<(std::ostream& out, const VersionNumber& v) noexcept {
198 return out << v.toString();
199 }
200
201 /**
202 * Simple version number class containing a version number
203 * either being {@link #VersionNumber(int, int, int) defined explicit}
204 * or {@link #VersionNumber(String, String) derived from a string}.
205 *
206 * For the latter case, you can query whether a component has been defined explicitly by the given <code>versionString</code>,
207 * via {@link #hasMajor()}, {@link #hasMinor()} and {@link #hasSub()}.
208 *
209 * The state whether a component is defined explicitly <i>is not considered</i>
210 * in the {@link #hashCode()}, {@link #equals(Object)} or {@link #compareTo(Object)} methods,
211 * since the version number itself is treated regardless.
212 */
214 private:
215 ssize_t m_strEnd;
216 std::string m_version_str;
217
218 protected:
219 constexpr VersionNumberString(int majorRev, int minorRev, int subMinorRev,
220 int gitCommits, uint64_t gitSSHA, bool gitDirty,
221 uint16_t _state, ssize_t strEnd, std::string _version_str) noexcept
222 : VersionNumber(majorRev, minorRev, subMinorRev, gitCommits, gitSSHA, gitDirty, _state),
223 m_strEnd(strEnd), m_version_str(std::move(_version_str)) { }
224
225 public:
226 static std::regex getNonGitPattern(const std::string& delim) {
227 // v0.0.1-3-gd55f8a3-dirty
228 // return std::regex( R"(\D*(\d+)[^\.\s]*(?:\.\D*(\d+)[^\.\s]*(?:\.\D*(\d+))?)?)");
229 return std::regex( R"(\D*(\d+)[^\)" + delim + R"(\s]*(?:\)" + delim + R"(\D*(\d+)[^\)" + delim + R"(\s]*(?:\)" + delim + R"(\D*(\d+))?)?)");
230 }
231
232 static std::regex getGitPattern(const std::string& delim) {
233 // v0.0.1-3-gd55f8a3-dirty
234 // return std::regex( R"(\D*(\d+)[^\.\s]*(?:\.\D*(\d+)[^\.\s]*(?:\.\D*(\d+)(?:\-(\d+)\-g([0-9a-f]+)(\-dirty)?)?)?)?)");
235 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)?)?)?)?)");
236 }
237
238 static const std::regex& getDefaultPattern() noexcept { // NOLINT(bugprone-exception-escape)
239 static std::regex defPattern = getGitPattern(".");
240 return defPattern;
241 }
242
243 /**
244 * Default ctor for zero version.
245 * @see #hasMajor()
246 * @see #hasMinor()
247 * @see #hasSub()
248 */
249 constexpr VersionNumberString() noexcept
250 : VersionNumberString(0, 0, 0, 0, 0, false, -1, HAS_MAJOR | HAS_MINOR | HAS_SUB, "") { }
251
252 VersionNumberString(const VersionNumberString&) noexcept = default;
254 VersionNumberString& operator=(const VersionNumberString&) noexcept = default;
255 VersionNumberString& operator=(VersionNumberString&&) noexcept = default;
256
257 /**
258 * String derived version number instantiation.
259 * <p>
260 * You can query whether a component has been defined explicitly by the given <code>versionString</code>,
261 * via {@link #hasMajor()}, {@link #hasMinor()} and {@link #hasSub()}.
262 * </p>
263 * @param versionString should be given as [MAJOR[.MINOR[.SUB]]]
264 * @param versionPattern the {@link java.util.regex.Pattern pattern} parser, must be compatible w/ {@link #getVersionNumberPattern(String)}
265 *
266 * @see #hasMajor()
267 * @see #hasMinor()
268 * @see #hasSub()
269 */
270 VersionNumberString(const std::string& versionString, const std::regex& versionPattern) noexcept
271 : VersionNumber(0, 0, 0, 0, 0, false, 0),
272 m_strEnd(0), m_version_str(versionString)
273 {
274 // group1: \d* == digits major
275 // group2: \d* == digits minor
276 // group3: \d* == digits sub
277 // group4: \d* == digits commit-count
278 // group5: hex == hex short-sha
279 // group6: str == dirty
280 std::smatch match;
281 if ( std::regex_search(versionString, match, versionPattern) ) {
282 m_strEnd = match.position() + match.length();
283 if constexpr ( DBG_OUT ) {
284 std::cout << "XXX: " << versionString << std::endl;
285 std::cout << "XXX: match pos " << match.position() << ", len " << match.length() << ", sz " << match.size() << std::endl;
286 for ( size_t i = 0; i < match.size(); ++i ) {
287 const std::string& s = match[i];
288 std::cout << "- [" << i << "]: '" << s << "', len " << s.length() << std::endl;
289 }
290 }
291 if ( match.size() >= 2 && match[1].length() > 0 ) {
292 m_major = std::stoi(match[1]);
294 if ( match.size() >= 3 && match[2].length() > 0 ) {
295 m_minor = std::stoi(match[2]);
297 if ( match.size() >= 4 && match[3].length() > 0 ) {
298 m_sub = std::stoi(match[3]);
299 m_state |= HAS_SUB;
300 if ( match.size() >= 5 && match[4].length() > 0 ) {
301 m_git_commits = std::stoi(match[4]);
303 if constexpr ( DBG_OUT ) {
304 std::cout << "XXX: git commits " << m_git_commits << std::endl;
305 }
306 if ( match.size() >= 6 && match[5].length() > 0 ) {
307 const auto [git_ssha, len0, ok0] = jau::fromHexString(match[5].str());
308 if constexpr ( DBG_OUT ) {
309 std::cout << "XXX: git ssha ok " << ok0 << " '" << match[5] << "', hex " << jau::toHexString(git_ssha) << ", dec " << git_ssha << std::endl;
310 }
311 if ( ok0 ) {
313 }
314 if ( match.size() >= 7 && match[6].length() > 0 ) {
315 m_git_dirty = true;
316 if constexpr ( DBG_OUT ) {
317 std::cout << "XXX: git dirty " << m_git_dirty << std::endl;
318 }
319 }
320 }
321 }
322 }
323 }
324 }
325 }
326 }
327
328 /**
329 * String derived version number instantiation.
330 * <p>
331 * Utilizing the default {@link java.util.regex.Pattern pattern} parser with delimiter "<b>.</b>", see {@link #getDefaultVersionNumberPattern()}.
332 * </p>
333 * <p>
334 * You can query whether a component has been defined explicitly by the given <code>versionString</code>,
335 * via {@link #hasMajor()}, {@link #hasMinor()} and {@link #hasSub()}.
336 * </p>
337 * @param versionString should be given as [MAJOR[.MINOR[.SUB]]]
338 *
339 * @see #hasMajor()
340 * @see #hasMinor()
341 * @see #hasSub()
342 */
345
346 /**
347 * String derived version number instantiation.
348 * <p>
349 * Utilizing {@link java.util.regex.Pattern pattern} parser created via {@link #getVersionNumberPattern(String)}.
350 * </p>
351 * <p>
352 * You can query whether a component has been defined explicitly by the given <code>versionString</code>,
353 * via {@link #hasMajor()}, {@link #hasMinor()} and {@link #hasSub()}.
354 * </p>
355 * @param versionString should be given as [MAJOR[.MINOR[.SUB]]]
356 * @param delim the delimiter, e.g. "."
357 *
358 * @see #hasMajor()
359 * @see #hasMinor()
360 * @see #hasSub()
361 */
362 VersionNumberString(const std::string& versionString, const std::string& delim) noexcept
364
365 /** Returns true if constructed with a `version-string`, otherwise false. */
366 constexpr bool hasString() const noexcept { return m_version_str.length() > 0; }
367 /** Returns the used `version-string`, empty if not constructed with such. */
368 constexpr const std::string& versionString() const noexcept { return m_version_str; }
369
370 /**
371 * If constructed with `version-string`, returns the string offset <i>after</i> the last matching character,
372 * or <code>0</code> if none matched, or <code>-1</code> if not constructed with a string.
373 */
374 constexpr ssize_t endOfStringMatch() const noexcept { return m_strEnd; }
375
376 std::string toString() const noexcept {
377 std::string res = std::to_string(m_major) + "." + std::to_string(m_minor) + "." + std::to_string(m_sub);
378 if( hasGitInfo() ) {
379 res.append(", git[post ").append(std::to_string(m_git_commits))
380 .append(", tip ").append(jau::toHexString(m_git_ssha, jau::lb_endian_t::little))
381 .append(", ");
382 if( git_dirty() ) {
383 res.append("dirty");
384 } else {
385 res.append("clean");
386 }
387 res.append("]");
388 }
389 if ( hasString() ) {
390 res.append(" (").append(m_version_str).append(")");
391 }
392 return res;
393 }
394 };
395
396 inline std::ostream& operator<<(std::ostream& out, const VersionNumberString& v) noexcept {
397 return out << v.toString();
398 }
399
400} // namespace jau::util
401
402namespace std {
403 template<> struct hash<jau::util::VersionNumber>
404 {
405 std::size_t operator()(const jau::util::VersionNumber& v) const noexcept {
406 return v.hash();
407 }
408 };
409} // namespace std
410
411#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
@ little
Identifier for little endian, equivalent to endian::little.
SizeBoolPair fromHexString(std::vector< uint8_t > &out, const uint8_t hexstr[], const size_t hexstr_len, const lb_endian_t byteOrder=lb_endian_t::big, const Bool checkPrefix=Bool::True) noexcept
Converts a given hexadecimal string representation into a byte vector, lsb-first.
std::string toHexString(const void *data, const nsize_t length, const lb_endian_t byteOrder=lb_endian_t::big, const LoUpCase capitalization=LoUpCase::lower, const PrefixOpt prefix=PrefixOpt::prefix) noexcept
Produce a hexadecimal string representation of the given lsb-first byte values.
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