convert version from string to real number for comparison

Allow 2.3.10 to be > than 2.3.9
allow 1.1.1.1 and not only 1.1.1
supermerill/SuperSlicer#2301
This commit is contained in:
supermerill 2022-02-02 18:23:09 +01:00
parent 56b2f2b483
commit 3a3627a2d9
5 changed files with 197 additions and 99 deletions

View File

@ -431,7 +431,7 @@ std::string AppConfig::load()
// Make 1.40.0 alphas compare well // Make 1.40.0 alphas compare well
ini_ver->set_metadata(boost::none); ini_ver->set_metadata(boost::none);
ini_ver->set_prerelease(boost::none); ini_ver->set_prerelease(boost::none);
m_legacy_datadir = ini_ver < Semver(1, 40, 0); m_legacy_datadir = ini_ver < Semver(1, 40, 0, 0);
} }
// Legacy conversion // Legacy conversion

View File

@ -20,24 +20,30 @@ class Semver
public: public:
struct Major { const int i; Major(int i) : i(i) {} }; struct Major { const int i; Major(int i) : i(i) {} };
struct Minor { const int i; Minor(int i) : i(i) {} }; struct Minor { const int i; Minor(int i) : i(i) {} };
struct Counter { const int i; Counter(int i) : i(i) {} }; //for SuSi
struct Patch { const int i; Patch(int i) : i(i) {} }; struct Patch { const int i; Patch(int i) : i(i) {} };
Semver() : ver(semver_zero()) {} Semver() : ver(semver_zero()) {}
Semver(int major, int minor, int patch, Semver(int major, int minor, int counter, int patch,
boost::optional<const std::string&> metadata = boost::none, boost::optional<const std::string&> metadata = boost::none,
boost::optional<const std::string&> prerelease = boost::none) boost::optional<const std::string&> prerelease = boost::none)
: ver(semver_zero()) : ver(semver_zero())
{ {
ver.major = major; semver_free(&ver);
ver.minor = minor; ver.counter_size = 4;
ver.patch = patch; ver.counters = new int[4];
ver.counters[0] = major;
ver.counters[1] = minor;
ver.counters[2] = counter;
ver.counters[3] = patch;
set_metadata(metadata); set_metadata(metadata);
set_prerelease(prerelease); set_prerelease(prerelease);
} }
Semver(const std::string &str) : ver(semver_zero()) Semver(const std::string &str) : ver(semver_zero())
{ {
auto parsed = parse(str); auto parsed = parse(str);
if (! parsed) { if (! parsed) {
throw Slic3r::RuntimeError(std::string("Could not parse version string: ") + str); throw Slic3r::RuntimeError(std::string("Could not parse version string: ") + str);
@ -60,13 +66,16 @@ public:
static const Semver inf() static const Semver inf()
{ {
static semver_t ver = { std::numeric_limits<int>::max(), std::numeric_limits<int>::max(), std::numeric_limits<int>::max(), nullptr, nullptr }; semver_t ver = { new int[4], 4, nullptr, nullptr };
for (int i = 0; i < ver.counter_size; i++)
ver.counters[i] = std::numeric_limits<int>::max();
return Semver(ver); return Semver(ver);
} }
static const Semver invalid() static const Semver invalid()
{ {
static semver_t ver = { -1, 0, 0, nullptr, nullptr }; semver_t ver = { new int[1], 1, nullptr, nullptr };
ver.counters[0] = -1;
return Semver(ver); return Semver(ver);
} }
@ -91,16 +100,18 @@ public:
~Semver() { ::semver_free(&ver); } ~Semver() { ::semver_free(&ver); }
// const accessors // const accessors
int maj() const { return ver.major; } //int maj() const { return ver.counter_size > 0 ? ver.counters[0] : 0; }
int min() const { return ver.minor; } //int min() const { return ver.counter_size > 1 ? ver.counters[1] : 0; }
int patch() const { return ver.patch; } //int counter() const { return ver.counter_size > 2 ? ver.counters[2] : 0; }
//int patch() const { return ver.counter_size > 3 ? ver.counters[3] : 0; }
const char* prerelease() const { return ver.prerelease; } const char* prerelease() const { return ver.prerelease; }
const char* metadata() const { return ver.metadata; } const char* metadata() const { return ver.metadata; }
// Setters // Setters
void set_maj(int maj) { ver.major = maj; } //void set_maj(int maj) { if(ver.counter_size > 0) ver.counters[0] = maj; }
void set_min(int min) { ver.minor = min; } //void set_min(int min) { if (ver.counter_size > 1) ver.counters[1] = min; }
void set_patch(int patch) { ver.patch = patch; } //void set_counter(int count) { if (ver.counter_size > 2) ver.counters[2] = count; }
//void set_patch(int patch) { if (ver.counter_size > 3) ver.counters[3] = patch; }
void set_metadata(boost::optional<const std::string&> meta) { ver.metadata = meta ? strdup(*meta) : nullptr; } void set_metadata(boost::optional<const std::string&> meta) { ver.metadata = meta ? strdup(*meta) : nullptr; }
void set_prerelease(boost::optional<const std::string&> pre) { ver.prerelease = pre ? strdup(*pre) : nullptr; } void set_prerelease(boost::optional<const std::string&> pre) { ver.prerelease = pre ? strdup(*pre) : nullptr; }
@ -120,25 +131,32 @@ public:
// Conversion // Conversion
std::string to_string() const { std::string to_string() const {
auto res = (boost::format("%1%.%2%.%3%") % ver.major % ver.minor % ver.patch).str(); std::string res;
for (int i = 0; i < ver.counter_size; i++) {
res += ( (i==0 ? boost::format("%1%") : boost::format(".%1%")) % ver.counters[i]).str();
}
if (ver.prerelease != nullptr) { res += '-'; res += ver.prerelease; } if (ver.prerelease != nullptr) { res += '-'; res += ver.prerelease; }
if (ver.metadata != nullptr) { res += '+'; res += ver.metadata; } if (ver.metadata != nullptr) { res += '+'; res += ver.metadata; }
return res; return res;
} }
// Arithmetics // Arithmetics
Semver& operator+=(const Major &b) { ver.major += b.i; return *this; } //Semver& operator+=(const Major &b) { set_maj(maj()+b.i); return *this; }
Semver& operator+=(const Minor &b) { ver.minor += b.i; return *this; } //Semver& operator+=(const Minor &b) { set_min(min() + b.i); return *this; }
Semver& operator+=(const Patch &b) { ver.patch += b.i; return *this; } //Semver& operator+=(const Counter& b) { set_counter(counter() + b.i); return *this; }
Semver& operator-=(const Major &b) { ver.major -= b.i; return *this; } //Semver& operator+=(const Patch &b) { set_patch(patch() + b.i); return *this; }
Semver& operator-=(const Minor &b) { ver.minor -= b.i; return *this; } //Semver& operator-=(const Major& b) { set_maj(maj() - b.i); return *this; }
Semver& operator-=(const Patch &b) { ver.patch -= b.i; return *this; } //Semver& operator-=(const Minor& b) { set_min(min() - b.i); return *this; }
Semver operator+(const Major &b) const { Semver res(*this); return res += b; } //Semver& operator-=(const Counter& b) { set_counter(counter() - b.i); return *this; }
Semver operator+(const Minor &b) const { Semver res(*this); return res += b; } //Semver& operator-=(const Patch& b) { set_patch(patch() - b.i); return *this; }
Semver operator+(const Patch &b) const { Semver res(*this); return res += b; } //Semver operator+(const Major &b) const { Semver res(*this); return res += b; }
Semver operator-(const Major &b) const { Semver res(*this); return res -= b; } //Semver operator+(const Minor &b) const { Semver res(*this); return res += b; }
Semver operator-(const Minor &b) const { Semver res(*this); return res -= b; } //Semver operator+(const Counter& b) const { Semver res(*this); return res += b; }
Semver operator-(const Patch &b) const { Semver res(*this); return res -= b; } //Semver operator+(const Patch& b) const { Semver res(*this); return res += b; }
//Semver operator-(const Major &b) const { Semver res(*this); return res -= b; }
//Semver operator-(const Minor &b) const { Semver res(*this); return res -= b; }
//Semver operator-(const Counter& b) const { Semver res(*this); return res -= b; }
//Semver operator-(const Patch& b) const { Semver res(*this); return res -= b; }
// Stream output // Stream output
friend std::ostream& operator<<(std::ostream& os, const Semver &self) { friend std::ostream& operator<<(std::ostream& os, const Semver &self) {
@ -148,9 +166,10 @@ public:
private: private:
semver_t ver; semver_t ver;
Semver(semver_t ver) : ver(ver) {} Semver(semver_t ver) : ver(ver) {}
static semver_t semver_zero() { return { 0, 0, 0, nullptr, nullptr }; } static semver_t semver_zero() { return { nullptr, 0, nullptr, nullptr }; }
static char * strdup(const std::string &str) { return ::semver_strdup(str.data()); } static char * strdup(const std::string &str) { return ::semver_strdup(str.data()); }
}; };

View File

@ -162,6 +162,40 @@ semver_parse (const char *str, semver_t *ver) {
return res; return res;
} }
/**
* count he numbezr of int
*/
int
semver_count_version(const char* str) {
size_t len;
int index, value;
char* slice, * next, * endptr;
slice = (char*)str;
index = 0;
while (slice != NULL && index++ < 4) {
next = strchr(slice, DELIMITER[0]);
if (next == NULL)
len = strlen(slice);
else
len = next - slice;
if (len > SLICE_SIZE) return -1;
/* Cast to integer and store */
value = strtol(slice, &endptr, 10);
if (endptr != next && *endptr != '\0') return -1;
/* Continue with the next slice */
if (next == NULL)
slice = NULL;
else
slice = next + 1;
}
// Major and minor versions are mandatory, patch version is not mandatory.
return index;
}
/** /**
* Parses a given string as semver expression. * Parses a given string as semver expression.
* *
@ -178,37 +212,37 @@ semver_parse_version (const char *str, semver_t *ver) {
char *slice, *next, *endptr; char *slice, *next, *endptr;
slice = (char *) str; slice = (char *) str;
index = 0; index = 0;
if (ver->counters) {
// non mandatory free(ver->counters);
ver->patch = 0; ver->counters = NULL;
while (slice != NULL && index++ < 4) {
next = strchr(slice, DELIMITER[0]);
if (next == NULL)
len = strlen(slice);
else
len = next - slice;
if (len > SLICE_SIZE) return -1;
/* Cast to integer and store */
value = strtol(slice, &endptr, 10);
if (endptr != next && *endptr != '\0') return -1;
switch (index) {
case 1: ver->major = value; break;
case 2: ver->minor = value; break;
case 3: ver->patch = value; break;
}
/* Continue with the next slice */
if (next == NULL)
slice = NULL;
else
slice = next + 1;
} }
ver->counter_size = semver_count_version(str);
if (ver->counter_size != 0) {
ver->counters = malloc(ver->counter_size * sizeof(int));
while (slice != NULL && index++ < 4) {
next = strchr(slice, DELIMITER[0]);
if (next == NULL)
len = strlen(slice);
else
len = next - slice;
if (len > SLICE_SIZE) return -1;
/* Cast to integer and store */
value = strtol(slice, &endptr, 10);
if (endptr != next && *endptr != '\0') return -1;
ver->counters[index - 1] = value;
/* Continue with the next slice */
if (next == NULL)
slice = NULL;
else
slice = next + 1;
}
}
// Major and minor versions are mandatory, patch version is not mandatory. // Major and minor versions are mandatory, patch version is not mandatory.
return (index == 2 || index == 3) ? 0 : -1; return (index >= 2) ? 0 : -1;
} }
static int static int
@ -280,12 +314,11 @@ semver_compare_prerelease (semver_t x, semver_t y) {
int int
semver_compare_version (semver_t x, semver_t y) { semver_compare_version (semver_t x, semver_t y) {
int res; int res = binary_comparison(x.counter_size, y.counter_size);
if ((res = binary_comparison(x.major, y.major)) == 0) { for (int i = 0; i < x.counter_size && i < y.counter_size; i++) {
if ((res = binary_comparison(x.minor, y.minor)) == 0) { if ((res = binary_comparison(x.counters[i], y.counters[i])) != 0)
return binary_comparison(x.patch, y.patch); return res;
}
} }
return res; return res;
@ -379,9 +412,9 @@ semver_lte (semver_t x, semver_t y) {
int int
semver_satisfies_caret (semver_t x, semver_t y) { semver_satisfies_caret (semver_t x, semver_t y) {
if (x.major == y.major) { if (x.counter_size > 0 && y.counter_size > 0 && x.counters[0] == y.counters[0]) {
if (x.major == 0) { if (x.counters[0] == 0) {
return x.minor >= y.minor; return x.counter_size > 1 && y.counter_size > 1 && x.counters[1] >= y.counters[1];
} }
return 1; return 1;
} }
@ -402,8 +435,8 @@ semver_satisfies_caret (semver_t x, semver_t y) {
int int
semver_satisfies_patch (semver_t x, semver_t y) { semver_satisfies_patch (semver_t x, semver_t y) {
return x.major == y.major return x.counter_size > 1 && y.counter_size > 1 && x.counters[0] == y.counters[0]
&& x.minor == y.minor; && x.counters[1] == y.counters[1];
} }
/** /**
@ -480,6 +513,11 @@ semver_free (semver_t *x) {
free(x->prerelease); free(x->prerelease);
x->prerelease = NULL; x->prerelease = NULL;
} }
if (x->counters) {
x->counter_size = 0;
free(x->counters);
x->counters = NULL;
}
} }
/** /**
@ -507,9 +545,9 @@ concat_char (char * str, char * x, char * sep) {
void void
semver_render (semver_t *x, char *dest) { semver_render (semver_t *x, char *dest) {
if (x->major) concat_num(dest, x->major, NULL); for (int i = 0; i < x->counter_size; ++i) {
if (x->minor) concat_num(dest, x->minor, DELIMITER); concat_num(dest, x->counters[i], i==0 ? NULL : DELIMITER);
if (x->patch) concat_num(dest, x->patch, DELIMITER); }
if (x->prerelease) concat_char(dest, x->prerelease, PR_DELIMITER); if (x->prerelease) concat_char(dest, x->prerelease, PR_DELIMITER);
if (x->metadata) concat_char(dest, x->metadata, MT_DELIMITER); if (x->metadata) concat_char(dest, x->metadata, MT_DELIMITER);
} }
@ -519,18 +557,21 @@ semver_render (semver_t *x, char *dest) {
*/ */
void void
semver_bump (semver_t *x) { semver_bump_major (semver_t *x) {
x->major++; if(x->counter_size > 0)
x->counters[0]++;
} }
void void
semver_bump_minor (semver_t *x) { semver_bump_minor (semver_t *x) {
x->minor++; if (x->counter_size > 1)
x->counters[1]++;
} }
void void
semver_bump_patch (semver_t *x) { semver_bump (semver_t *x, int idx) {
x->patch++; if (x->counter_size > idx)
x->counters[idx]++;
} }
/** /**
@ -612,9 +653,9 @@ semver_numeric (semver_t *x) {
char buf[SLICE_SIZE * 3]; char buf[SLICE_SIZE * 3];
memset(&buf, 0, SLICE_SIZE * 3); memset(&buf, 0, SLICE_SIZE * 3);
if (x->major) concat_num(buf, x->major, NULL); for (int i = 0; i < x->counter_size; ++i) {
if (x->minor) concat_num(buf, x->minor, NULL); concat_num(buf, x->counters[i], NULL);
if (x->patch) concat_num(buf, x->patch, NULL); }
num = parse_int(buf); num = parse_int(buf);
if(num == -1) return -1; if(num == -1) return -1;
@ -625,11 +666,17 @@ semver_numeric (semver_t *x) {
return num; return num;
} }
char *semver_strdup(const char *src) { char* semver_strdup(const char* src) {
if (src == NULL) return NULL; if (src == NULL) return NULL;
size_t len = strlen(src) + 1; size_t len = strlen(src) + 1;
char *res = malloc(len); char* res = malloc(len);
return res != NULL ? (char *) memcpy(res, src, len) : NULL; return res != NULL ? (char*)memcpy(res, src, len) : NULL;
}
int* semver_intdup(const int* src, int len) {
if (src == NULL) return NULL;
int* res = malloc(len * sizeof(int));
return res != NULL ? (int*)memcpy(res, src, len * sizeof(int)) : NULL;
} }
semver_t semver_t
@ -639,7 +686,10 @@ semver_copy(const semver_t *ver) {
res.metadata = strdup(ver->metadata); res.metadata = strdup(ver->metadata);
} }
if (ver->prerelease != NULL) { if (ver->prerelease != NULL) {
res.prerelease = strdup(ver->prerelease); res.prerelease = strdup(ver->prerelease);
}
if (ver->counters) {
res.counters = semver_intdup(ver->counters, ver->counter_size);
} }
return res; return res;
} }

View File

@ -21,9 +21,8 @@ extern "C" {
*/ */
typedef struct semver_version_s { typedef struct semver_version_s {
int major; int * counters;
int minor; int counter_size;
int patch;
char * metadata; char * metadata;
char * prerelease; char * prerelease;
} semver_t; } semver_t;
@ -72,7 +71,10 @@ int
semver_parse (const char *str, semver_t *ver); semver_parse (const char *str, semver_t *ver);
int int
semver_parse_version (const char *str, semver_t *ver); semver_count_version(const char* str);
int
semver_parse_version(const char* str, semver_t* ver);
void void
semver_render (semver_t *x, char *dest); semver_render (semver_t *x, char *dest);
@ -81,13 +83,13 @@ int
semver_numeric (semver_t *x); semver_numeric (semver_t *x);
void void
semver_bump (semver_t *x); semver_bump_major (semver_t *x);
void void
semver_bump_minor (semver_t *x); semver_bump_minor (semver_t *x);
void void
semver_bump_patch (semver_t *x); semver_bump (semver_t *x, int idx);
void void
semver_free (semver_t *x); semver_free (semver_t *x);

View File

@ -244,6 +244,19 @@ void PresetUpdater::priv::prune_tmps() const
} }
} }
//parse the string, if it doesn't contain a valid version string, return invalid version.
Semver get_version(const std::string &str, const std::regex &regexp) {
std::smatch match;
if (std::regex_match(str, match, regexp)) {
std::string version_cleaned = match[0];
const boost::optional<Semver> version = Semver::parse(version_cleaned);
if (version.has_value()) {
return *version;
}
}
return Semver::invalid();
}
// Get Slic3rPE version available online, save in AppConfig. // Get Slic3rPE version available online, save in AppConfig.
void PresetUpdater::priv::sync_version() const void PresetUpdater::priv::sync_version() const
{ {
@ -267,34 +280,48 @@ void PresetUpdater::priv::sync_version() const
std::stringstream json_stream(body); std::stringstream json_stream(body);
boost::property_tree::read_json(json_stream, root); boost::property_tree::read_json(json_stream, root);
bool i_am_pre = false; bool i_am_pre = false;
std::string best_pre = "1"; //at least two number, use '.' as separator. can be followed by -Az23 for prereleased and +Az42 for metadata
std::string best_release = "1"; std::regex matcher("[0-9]+\.[0-9]+(\.[0-9]+)*(-[A-Za-z0-9]+)?(\\+[A-Za-z0-9]+)?");
Semver current_version(SLIC3R_VERSION_FULL);
Semver best_pre(1,0,0,0);
Semver best_release(1, 0, 0, 0);
std::string best_pre_url; std::string best_pre_url;
std::string best_release_url; std::string best_release_url;
const std::regex reg_num("([0-9]+)");
for (auto json_version : root) { for (auto json_version : root) {
std::string tag = json_version.second.get<std::string>("tag_name"); std::string tag = json_version.second.get<std::string>("tag_name");
if (SLIC3R_VERSION_FULL == tag) for (std::regex_iterator it = std::sregex_iterator(tag.begin(), tag.end(), reg_num); it != std::sregex_iterator(); ++it) {
}
Semver tag_version = get_version(tag, matcher);
if (current_version == tag_version)
i_am_pre = json_version.second.get<bool>("prerelease"); i_am_pre = json_version.second.get<bool>("prerelease");
if (json_version.second.get<bool>("prerelease")) { if (json_version.second.get<bool>("prerelease")) {
if (best_pre < tag) { if (best_pre < tag_version) {
best_pre = tag; best_pre = tag_version;
best_pre_url = json_version.second.get<std::string>("html_url"); best_pre_url = json_version.second.get<std::string>("html_url");
} }
} else { } else {
if (best_release < tag) { if (best_release < tag_version) {
best_release = tag; best_release = tag_version;
best_release_url = json_version.second.get<std::string>("html_url"); best_release_url = json_version.second.get<std::string>("html_url");
} }
} }
} }
//if release is more recent than beta, use release anyway
if ((i_am_pre ? best_pre : best_release) <= SLIC3R_VERSION_FULL) if (best_pre < best_release) {
best_pre = best_release;
best_pre_url = best_release_url;
}
//if we're the most recent, don't do anything
if ((i_am_pre ? best_pre : best_release) <= current_version)
return; return;
BOOST_LOG_TRIVIAL(info) << format("Got %1% online version: `%2%`. Sending to GUI thread...", SLIC3R_APP_NAME, i_am_pre? best_pre:best_release); BOOST_LOG_TRIVIAL(info) << format("Got %1% online version: `%2%`. Sending to GUI thread...", SLIC3R_APP_NAME, i_am_pre ? best_pre : best_release);
wxCommandEvent* evt = new wxCommandEvent(EVT_SLIC3R_VERSION_ONLINE); wxCommandEvent* evt = new wxCommandEvent(EVT_SLIC3R_VERSION_ONLINE);
evt->SetString(i_am_pre ? best_pre : best_release); evt->SetString((i_am_pre ? best_pre : best_release).to_string());
GUI::wxGetApp().QueueEvent(evt); GUI::wxGetApp().QueueEvent(evt);
}) })
.perform_sync(); .perform_sync();