aboutsummaryrefslogtreecommitdiff
path: root/butl/standard-version.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'butl/standard-version.cxx')
-rw-r--r--butl/standard-version.cxx632
1 files changed, 0 insertions, 632 deletions
diff --git a/butl/standard-version.cxx b/butl/standard-version.cxx
deleted file mode 100644
index cc91f19..0000000
--- a/butl/standard-version.cxx
+++ /dev/null
@@ -1,632 +0,0 @@
-// file : butl/standard-version.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <butl/standard-version>
-
-#include <cassert>
-#include <cstdlib> // strtoull()
-#include <cstddef> // size_t
-#include <utility> // move()
-#include <stdexcept> // invalid_argument
-
-#include <butl/utility> // alnum()
-
-using namespace std;
-
-namespace butl
-{
- // Utility functions
- //
- static uint64_t
- parse_num (const string& s, size_t& p,
- const char* m,
- uint64_t min = 0, uint64_t max = 999)
- {
- if (s[p] == '-' || s[p] == '+') // strtoull() allows these.
- throw invalid_argument (m);
-
- const char* b (s.c_str () + p);
- char* e (nullptr);
- uint64_t r (strtoull (b, &e, 10));
-
- if (b == e || r < min || r > max)
- throw invalid_argument (m);
-
- p = e - s.c_str ();
- return static_cast<uint64_t> (r);
- }
-
- static void
- check_version (uint64_t vr, bool sn, standard_version::flags fl)
- {
- bool r;
- if (vr == uint64_t (~0) && (fl & standard_version::allow_stub) != 0)
- {
- // Stub.
- //
- // Check that the snapshot flag is false.
- //
- r = !sn;
- }
- else
- {
- // Check that the version isn't too large, unless represents stub.
- //
- // AAABBBCCCDDDE
- r = vr < 10000000000000ULL;
-
- // Check that E version component is consistent with the snapshot flag.
- // Note that if the allow_earliest flag is set, then E can be 1 for the
- // snapshot flag being false, denoting the earliest pre-release of the
- // version.
- //
- if (r)
- {
- uint64_t e (vr % 10);
- if ((fl & standard_version::allow_earliest) == 0)
- r = e == (sn ? 1 : 0);
- else
- r = e == 1 || (e == 0 && !sn);
- }
-
- // Check that pre-release number is consistent with the snapshot flag.
- //
- if (r)
- {
- uint64_t ab (vr / 10 % 1000);
-
- // Note that if ab is 0, it can either mean non-pre-release version in
- // the absence of snapshot number, or 'a.0' pre-release otherwise. If
- // ab is 500, it can only mean 'b.0', that must be followed by a
- // snapshot number.
- //
- if (ab != 0)
- r = ab != 500 || sn;
- }
-
- // Check that the major, the minor and the patch versions are not
- // simultaneously zeros.
- //
- if (r)
- r = (vr / 10000) != 0;
- }
-
- if (!r)
- throw invalid_argument ("invalid project version");
- }
-
- // standard_version
- //
- standard_version::
- standard_version (const std::string& s, flags f)
- {
- auto bail = [] (const char* m) {throw invalid_argument (m);};
-
- // Pre-parse the first component to see if the version starts with epoch,
- // to keep the subsequent parsing straightforward.
- //
- bool ep (false);
- {
- char* e (nullptr);
- strtoull (s.c_str (), &e, 10);
- ep = *e == '~';
- }
-
- // Note that here and below p is less or equal n, and so s[p] is always
- // valid.
- //
- size_t p (0), n (s.size ());
-
- if (ep)
- {
- epoch = parse_num (s, p, "invalid epoch", 1, uint16_t (~0));
- ++p; // Skip '~'.
- }
-
- uint16_t ma, mi, bf, ab (0);
- bool earliest (false);
-
- ma = parse_num (s, p, "invalid major version");
-
- // The only valid version that has no epoch, contains only the major
- // version being equal to zero, that is optionally followed by the plus
- // character, is the stub version, unless forbidden.
- //
- bool stub ((f & allow_stub) != 0 && !ep && ma == 0 &&
- (p == n || s[p] == '+'));
-
- if (stub)
- version = uint64_t (~0);
- else
- {
- if (s[p] != '.')
- bail ("'.' expected after major version");
-
- mi = parse_num (s, ++p, "invalid minor version");
-
- if (s[p] != '.')
- bail ("'.' expected after minor version");
-
- bf = parse_num (s, ++p, "invalid patch version");
-
- // AAABBBCCCDDDE
- version = ma * 10000000000ULL +
- mi * 10000000ULL +
- bf * 10000ULL;
-
- if (version == 0)
- bail ("0.0.0 version");
-
- // Parse the pre-release component if present.
- //
- if (s[p] == '-')
- {
- char k (s[++p]);
-
- // If the last character in the string is dash, then this is the
- // earliest version pre-release, unless forbidden.
- //
- if (k == '\0' && (f & allow_earliest) != 0)
- earliest = true;
- else
- {
- if (k != 'a' && k != 'b')
- bail ("'a' or 'b' expected in pre-release");
-
- if (s[++p] != '.')
- bail ("'.' expected after pre-release letter");
-
- ab = parse_num (s, ++p, "invalid pre-release", 0, 499);
-
- if (k == 'b')
- ab += 500;
-
- // Parse the snapshot components if present. Note that pre-release
- // number can't be zero for the final pre-release.
- //
- if (s[p] == '.')
- parse_snapshot (s, ++p);
- else if (ab == 0 || ab == 500)
- bail ("invalid final pre-release");
- }
- }
- }
-
- if (s[p] == '+')
- {
- assert (!earliest); // Would bail out earlier (a or b expected after -).
-
- revision = parse_num (s, ++p, "invalid revision", 1, uint16_t (~0));
- }
-
- if (p != n)
- bail ("junk after version");
-
- if (ab != 0 || snapshot_sn != 0 || earliest)
- version -= 10000 - ab * 10;
-
- if (snapshot_sn != 0 || earliest)
- version += 1;
- }
-
- standard_version::
- standard_version (uint64_t v, flags f)
- : version (v)
- {
- check_version (v, false, f);
- }
-
- standard_version::
- standard_version (uint64_t v, const std::string& s, flags f)
- : version (v)
- {
- bool snapshot (!s.empty ());
- check_version (version, snapshot, f);
-
- if (snapshot)
- {
- size_t p (0);
- parse_snapshot (s, p);
-
- if (p != s.size ())
- throw invalid_argument ("junk after snapshot");
- }
- }
-
- standard_version::
- standard_version (uint16_t e,
- uint64_t v,
- const std::string& s,
- uint16_t r,
- flags f)
- : standard_version (v, s, f)
- {
- if (stub () && e != 0)
- throw invalid_argument ("epoch for stub");
-
- // Can't initialize above due to ctor delegating.
- //
- epoch = e;
- revision = r;
- }
-
- standard_version::
- standard_version (uint16_t ep,
- uint64_t vr,
- uint64_t sn,
- std::string si,
- uint16_t rv,
- flags fl)
- : epoch (ep),
- version (vr),
- snapshot_sn (sn),
- snapshot_id (move (si)),
- revision (rv)
- {
- check_version (vr, true, fl);
-
- if (stub ())
- {
- if (ep != 0)
- throw invalid_argument ("epoch for stub");
-
- if (sn != 0)
- throw invalid_argument ("snapshot for stub");
- }
-
- if (!snapshot_id.empty () && (snapshot_id.size () > 16 ||
- snapshot_sn == 0 ||
- snapshot_sn == latest_sn))
- throw invalid_argument ("invalid snapshot");
- }
-
- void standard_version::
- parse_snapshot (const std::string& s, size_t& p)
- {
- // Note that snapshot id must be empty for 'z' snapshot number.
- //
- if (s[p] == 'z')
- {
- snapshot_sn = latest_sn;
- ++p;
- return;
- }
-
- uint64_t sn (parse_num (s,
- p,
- "invalid snapshot number",
- 1, latest_sn - 1));
- std::string id;
- if (s[p] == '.')
- {
- char c;
- for (++p; alnum (c = s[p]); ++p)
- id += c;
-
- if (id.empty () || id.size () > 16)
- throw invalid_argument ("invalid snapshot id");
- }
-
- snapshot_sn = sn;
- snapshot_id = move (id);
- }
-
- string standard_version::
- string_pre_release () const
- {
- std::string r;
-
- if ((alpha () && !earliest ()) || beta ())
- {
- uint64_t ab (version / 10 % 1000);
-
- if (ab < 500)
- {
- r += "a.";
- r += to_string (ab);
- }
- else
- {
- r += "b.";
- r += to_string (ab - 500);
- }
- }
-
- return r;
- }
-
- string standard_version::
- string_version () const
- {
- if (empty ())
- return "";
-
- if (stub ())
- return "0";
-
- std::string r (to_string (major ()) + '.' + to_string (minor ()) + '.' +
- to_string (patch ()));
-
- if (alpha () || beta ())
- {
- r += '-';
- r += string_pre_release ();
-
- if (snapshot ())
- r += '.';
- }
-
- return r;
- }
-
- string standard_version::
- string_snapshot () const
- {
- std::string r;
-
- if (snapshot ())
- {
- r = snapshot_sn == latest_sn ? "z" : to_string (snapshot_sn);
-
- if (!snapshot_id.empty ())
- {
- r += '.';
- r += snapshot_id;
- }
- }
-
- return r;
- }
-
- string standard_version::
- string_project () const
- {
- std::string r (string_version ());
-
- if (snapshot ())
- r += string_snapshot (); // string_version() includes trailing dot.
-
- return r;
- }
-
- string standard_version::
- string_project_id () const
- {
- std::string r (string_version ());
-
- if (snapshot ()) // Trailing dot already in id.
- {
- r += (snapshot_sn == latest_sn ? "z" :
- snapshot_id.empty () ? to_string (snapshot_sn) :
- snapshot_id);
- }
-
- return r;
- }
-
- string standard_version::
- string () const
- {
- std::string r;
-
- if (epoch != 0)
- {
- r = to_string (epoch);
- r += '~';
- }
-
- r += string_project ();
-
- if (revision != 0)
- {
- r += '+';
- r += to_string (revision);
- }
-
- return r;
- }
-
- // standard_version_constraint
- //
- standard_version_constraint::
- standard_version_constraint (const std::string& s)
- {
- using std::string; // Not to confuse with string().
-
- auto bail = [] (const string& m) {throw invalid_argument (m);};
- const char* spaces (" \t");
-
- size_t p (0);
- char c (s[p]);
-
- if (c == '(' || c == '[') // Can be '\0'.
- {
- bool min_open = c == '(';
-
- p = s.find_first_not_of (spaces, ++p);
- if (p == string::npos)
- bail ("no min version");
-
- size_t e (s.find_first_of (spaces, p));
-
- standard_version min_version;
-
- try
- {
- min_version = standard_version (s.substr (p, e - p),
- standard_version::allow_earliest);
- }
- catch (const invalid_argument& e)
- {
- bail (string ("invalid min version: ") + e.what ());
- }
-
- p = s.find_first_not_of (spaces, e);
- if (p == string::npos)
- bail ("no max version");
-
- e = s.find_first_of (" \t])", p);
-
- standard_version max_version;
-
- try
- {
- max_version = standard_version (s.substr (p, e - p),
- standard_version::allow_earliest);
- }
- catch (const invalid_argument& e)
- {
- bail (string ("invalid max version: ") + e.what ());
- }
-
- p = s.find_first_of ("])", e);
- if (p == string::npos)
- bail ("no closing bracket");
-
- bool max_open = s[p] == ')';
-
- if (++p != s.size ())
- bail ("junk after constraint");
-
- // Verify and copy the constraint.
- //
- *this = standard_version_constraint (min_version, min_open,
- max_version, max_open);
- }
- else
- {
- enum comparison {eq, lt, gt, le, ge};
- comparison operation (eq); // Uninitialized warning.
-
- if (s.compare (0, p = 2, "==") == 0)
- operation = eq;
- else if (s.compare (0, p = 2, ">=") == 0)
- operation = ge;
- else if (s.compare (0, p = 2, "<=") == 0)
- operation = le;
- else if (s.compare (0, p = 1, ">") == 0)
- operation = gt;
- else if (s.compare (0, p = 1, "<") == 0)
- operation = lt;
- else
- bail ("invalid constraint");
-
- p = s.find_first_not_of (spaces, p);
-
- if (p == string::npos)
- bail ("no version");
-
- standard_version v;
-
- try
- {
- v = standard_version (s.substr (p),
- operation != comparison::eq
- ? standard_version::allow_earliest
- : standard_version::none);
- }
- catch (const invalid_argument& e)
- {
- bail (string ("invalid version: ") + e.what ());
- }
-
- // Verify and copy the constraint.
- //
- switch (operation)
- {
- case comparison::eq:
- *this = standard_version_constraint (v);
- break;
- case comparison::lt:
- *this = standard_version_constraint (nullopt, true, move (v), true);
- break;
- case comparison::le:
- *this = standard_version_constraint (nullopt, true, move (v), false);
- break;
- case comparison::gt:
- *this = standard_version_constraint (move (v), true, nullopt, true);
- break;
- case comparison::ge:
- *this = standard_version_constraint (move (v), false, nullopt, true);
- break;
- }
- }
- }
-
- standard_version_constraint::
- standard_version_constraint (optional<standard_version> mnv, bool mno,
- optional<standard_version> mxv, bool mxo)
- : min_version (move (mnv)),
- max_version (move (mxv)),
- min_open (mno),
- max_open (mxo)
- {
- assert (
- // Min and max versions can't both be absent.
- //
- (min_version || max_version) &&
-
- // Version should be non-empty and not a stub.
- //
- (!min_version || (!min_version->empty () && !min_version->stub ())) &&
- (!max_version || (!max_version->empty () && !max_version->stub ())) &&
-
- // Absent version endpoint (infinity) should be open.
- //
- (min_version || min_open) && (max_version || max_open));
-
- if (min_version && max_version)
- {
- if (*min_version > *max_version)
- throw invalid_argument ("min version is greater than max version");
-
- if (*min_version == *max_version)
- {
- if (min_open || max_open)
- throw invalid_argument ("equal version endpoints not closed");
-
- if (min_version->earliest ())
- throw invalid_argument ("equal version endpoints are earliest");
- }
- }
- }
-
- string standard_version_constraint::
- string () const
- {
- assert (!empty ());
-
- if (!min_version)
- return (max_open ? "< " : "<= ") + max_version->string ();
-
- if (!max_version)
- return (min_open ? "> " : ">= ") + min_version->string ();
-
- if (*min_version == *max_version)
- return "== " + min_version->string ();
-
- return (min_open ? '(' : '[') + min_version->string () + ' ' +
- max_version->string () + (max_open ? ')' : ']');
- }
-
- bool standard_version_constraint::
- satisfies (const standard_version& v) const noexcept
- {
- bool s (true);
-
- if (min_version)
- {
- int i (v.compare (*min_version));
- s = min_open ? i > 0 : i >= 0;
- }
-
- if (s && max_version)
- {
- int i (v.compare (*max_version));
- s = max_open ? i < 0 : i <= 0;
- }
-
- return s;
- }
-}