From baf3e0359fe3b384f015702e337c3b4c9aea3ab0 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Tue, 12 Oct 2021 18:57:03 +0300 Subject: Add support for version iteration in string representation --- libbpkg/manifest.cxx | 115 ++++++++++++++++++++++++++++++++++++--------------- libbpkg/manifest.hxx | 55 ++++++++++++++++++++---- 2 files changed, 128 insertions(+), 42 deletions(-) (limited to 'libbpkg') diff --git a/libbpkg/manifest.cxx b/libbpkg/manifest.cxx index c8bf456..48ec383 100644 --- a/libbpkg/manifest.cxx +++ b/libbpkg/manifest.cxx @@ -4,14 +4,17 @@ #include #include +#include #include #include #include -#include // strncmp(), strcmp() -#include // move() -#include // uint*_t, UINT16_MAX -#include // find(), find_if_not(), find_first_of(), replace() -#include // invalid_argument +#include // strtoull() +#include // strncmp(), strcmp(), strchr() +#include // move() +#include // uint*_t +#include // find(), find_if_not(), find_first_of(), replace() +#include // invalid_argument +#include // remove_reference #include #include @@ -172,12 +175,12 @@ namespace bpkg canonical_upstream ( data_type (upstream.c_str (), data_type::parse::upstream, - false /* fold_zero_revision */). + none). canonical_upstream), canonical_release ( data_type (release ? release->c_str () : nullptr, data_type::parse::release, - false /* fold_zero_revision */). + none). canonical_release) { // Check members constrains. @@ -254,9 +257,12 @@ namespace bpkg } version::data_type:: - data_type (const char* v, parse pr, bool fold_zero_rev) + data_type (const char* v, parse pr, version::flags fl) { - if (fold_zero_rev) + if ((fl & version::fold_zero_revision) != 0) + assert (pr == parse::full); + + if ((fl & version::allow_iteration) != 0) assert (pr == parse::full); // Otherwise compiler gets confused with string() member. @@ -271,32 +277,75 @@ namespace bpkg return; } - assert (v != nullptr); - - optional ep; - auto bad_arg = [](const string& d) {throw invalid_argument (d);}; - auto uint16 = [&bad_arg](const string& s, const char* what) -> uint16_t + auto parse_uint = [&bad_arg](const string& s, auto& r, const char* what) { - try - { - uint64_t v (stoull (s)); + using type = typename remove_reference::type; - if (v <= UINT16_MAX) // From . - return static_cast (v); - } - catch (const std::exception&) + if (!s.empty () && s[0] != '-' && s[0] != '+') // strtoull() allows these. { - // Fall through. + const char* b (s.c_str ()); + char* e (nullptr); + errno = 0; // We must clear it according to POSIX. + uint64_t v (strtoull (b, &e, 10)); // Can't throw. + + if (errno != ERANGE && + e == b + s.size () && + v <= numeric_limits::max ()) + { + r = static_cast (v); + return; + } } - bad_arg (string (what) + " should be 2-byte unsigned integer"); + bad_arg (string (what) + " should be " + + std::to_string (sizeof (type)) + "-byte unsigned integer"); + }; - assert (false); // Can't be here. - return 0; + auto parse_uint16 = [&parse_uint](const string& s, const char* what) + { + uint16_t r; + parse_uint (s, r, what); + return r; + }; + + auto parse_uint32 = [&parse_uint](const string& s, const char* what) + { + uint32_t r; + parse_uint (s, r, what); + return r; }; + assert (v != nullptr); + + // Parse the iteration, if allowed. + // + // Note that allowing iteration is not very common, so let's handle it in + // an ad hoc way not to complicate the subsequent parsing. + // + string storage; + if (pr == parse::full) + { + iteration = 0; + + // Note that if not allowed but the iteration is present, then the below + // version parsing code will fail with appropriate diagnostics. + // + if ((fl & version::allow_iteration) != 0) + { + if (const char* p = strchr (v, '#')) + { + iteration = parse_uint32 (p + 1, "iteration"); + + storage.assign (v, p - v); + v = storage.c_str (); + } + } + } + + optional ep; + enum class mode {epoch, upstream, release, revision}; mode m (pr == parse::full ? (v[0] == '+' @@ -351,7 +400,7 @@ namespace bpkg if (lnn >= cb) // Contains non-digits. bad_arg ("epoch should be 2-byte unsigned integer"); - ep = uint16 (string (cb, p), "epoch"); + ep = parse_uint16 (string (cb, p), "epoch"); } else canon_part->add (cb, p, lnn < cb); @@ -424,9 +473,9 @@ namespace bpkg if (lnn >= cb) // Contains non-digits. bad_arg ("revision should be 2-byte unsigned integer"); - std::uint16_t rev (uint16 (cb, "revision")); + uint16_t rev (parse_uint16 (cb, "revision")); - if (rev != 0 || !fold_zero_rev) + if (rev != 0 || (fl & fold_zero_revision) == 0) revision = rev; } else if (cb != p) @@ -658,7 +707,7 @@ namespace bpkg if (mnv != "$") try { - min_version = version (mnv, false /* fold_zero_revision */); + min_version = version (mnv, version::none); } catch (const invalid_argument& e) { @@ -685,7 +734,7 @@ namespace bpkg if (mxv != "$") try { - max_version = version (mxv, false /* fold_zero_revision */); + max_version = version (mxv, version::none); } catch (const invalid_argument& e) { @@ -789,7 +838,7 @@ namespace bpkg // version. // if (vs != "$") - v = version (vs, false /* fold_zero_revision */); + v = version (vs, version::none); switch (operation) { @@ -4901,13 +4950,13 @@ namespace bpkg } version - extract_package_version (const char* s, bool fold_zero_revision) + extract_package_version (const char* s, version::flags fl) { using traits = string::traits_type; if (const char* p = traits::find (s, traits::length (s), '/')) { - version r (p + 1, fold_zero_revision); + version r (p + 1, fl); if (r.release && r.release->empty ()) throw invalid_argument ("earliest version"); diff --git a/libbpkg/manifest.hxx b/libbpkg/manifest.hxx index 8a11a85..f487a90 100644 --- a/libbpkg/manifest.hxx +++ b/libbpkg/manifest.hxx @@ -68,13 +68,20 @@ namespace bpkg // std::invalid_argument if the passed string is not a valid version // representation. // + enum flags + { + none = 0, + fold_zero_revision = 0x01, + allow_iteration = 0x02 + }; + explicit - version (const std::string& v, bool fold_zero_revision = true) - : version (v.c_str (), fold_zero_revision) {} + version (const std::string& v, flags fl = fold_zero_revision) + : version (v.c_str (), fl) {} explicit - version (const char* v, bool fold_zero_revision = true) - : version (data_type (v, data_type::parse::full, fold_zero_revision)) + version (const char* v, flags fl = fold_zero_revision) + : version (data_type (v, data_type::parse::full, fl)) { } @@ -169,7 +176,7 @@ namespace bpkg { enum class parse {full, upstream, release}; - data_type (const char*, parse, bool fold_zero_revision); + data_type (const char*, parse, flags); // Note that there is no iteration component as it can't be present in // the string representation passed to the ctor. @@ -178,6 +185,7 @@ namespace bpkg std::string upstream; butl::optional release; butl::optional revision; + std::uint32_t iteration; std::string canonical_upstream; std::string canonical_release; }; @@ -188,7 +196,7 @@ namespace bpkg upstream (std::move (d.upstream)), release (std::move (d.release)), revision (d.revision), - iteration (0), + iteration (d.iteration), canonical_upstream (std::move (d.canonical_upstream)), canonical_release (std::move (d.canonical_release)) {} }; @@ -199,6 +207,34 @@ namespace bpkg return os << (v.empty () ? "" : v.string ()); } + inline version::flags + operator&= (version::flags& x, version::flags y) + { + return x = static_cast ( + static_cast (x) & + static_cast (y)); + } + + inline version::flags + operator|= (version::flags& x, version::flags y) + { + return x = static_cast ( + static_cast (x) | + static_cast (y)); + } + + inline version::flags + operator& (version::flags x, version::flags y) + { + return x &= y; + } + + inline version::flags + operator| (version::flags x, version::flags y) + { + return x |= y; + } + // priority // class priority @@ -1707,13 +1743,14 @@ namespace bpkg // Note: the package name is not verified. // LIBBPKG_EXPORT version - extract_package_version (const char*, bool fold_zero_revision = true); + extract_package_version (const char*, + version::flags fl = version::fold_zero_revision); inline version extract_package_version (const std::string& s, - bool fold_zero_revision = true) + version::flags fl = version::fold_zero_revision) { - return extract_package_version (s.c_str (), fold_zero_revision); + return extract_package_version (s.c_str (), fl); } } -- cgit v1.1