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
ini_ver->set_metadata(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

View File

@ -20,24 +20,30 @@ class Semver
public:
struct Major { const int i; Major(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) {} };
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&> prerelease = boost::none)
: ver(semver_zero())
{
ver.major = major;
ver.minor = minor;
ver.patch = patch;
semver_free(&ver);
ver.counter_size = 4;
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_prerelease(prerelease);
}
Semver(const std::string &str) : ver(semver_zero())
{
auto parsed = parse(str);
if (! parsed) {
throw Slic3r::RuntimeError(std::string("Could not parse version string: ") + str);
@ -60,13 +66,16 @@ public:
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);
}
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);
}
@ -91,16 +100,18 @@ public:
~Semver() { ::semver_free(&ver); }
// const accessors
int maj() const { return ver.major; }
int min() const { return ver.minor; }
int patch() const { return ver.patch; }
//int maj() const { return ver.counter_size > 0 ? ver.counters[0] : 0; }
//int min() const { return ver.counter_size > 1 ? ver.counters[1] : 0; }
//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* metadata() const { return ver.metadata; }
// Setters
void set_maj(int maj) { ver.major = maj; }
void set_min(int min) { ver.minor = min; }
void set_patch(int patch) { ver.patch = patch; }
//void set_maj(int maj) { if(ver.counter_size > 0) ver.counters[0] = maj; }
//void set_min(int min) { if (ver.counter_size > 1) ver.counters[1] = min; }
//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_prerelease(boost::optional<const std::string&> pre) { ver.prerelease = pre ? strdup(*pre) : nullptr; }
@ -120,25 +131,32 @@ public:
// Conversion
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.metadata != nullptr) { res += '+'; res += ver.metadata; }
return res;
}
// Arithmetics
Semver& operator+=(const Major &b) { ver.major += b.i; return *this; }
Semver& operator+=(const Minor &b) { ver.minor += b.i; return *this; }
Semver& operator+=(const Patch &b) { ver.patch += b.i; return *this; }
Semver& operator-=(const Major &b) { ver.major -= b.i; return *this; }
Semver& operator-=(const Minor &b) { ver.minor -= b.i; return *this; }
Semver& operator-=(const Patch &b) { ver.patch -= b.i; return *this; }
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 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 Patch &b) const { Semver res(*this); return res -= b; }
//Semver& operator+=(const Major &b) { set_maj(maj()+b.i); return *this; }
//Semver& operator+=(const Minor &b) { set_min(min() + b.i); return *this; }
//Semver& operator+=(const Counter& b) { set_counter(counter() + b.i); return *this; }
//Semver& operator+=(const Patch &b) { set_patch(patch() + b.i); return *this; }
//Semver& operator-=(const Major& b) { set_maj(maj() - b.i); return *this; }
//Semver& operator-=(const Minor& b) { set_min(min() - b.i); return *this; }
//Semver& operator-=(const Counter& b) { set_counter(counter() - b.i); return *this; }
//Semver& operator-=(const Patch& b) { set_patch(patch() - b.i); return *this; }
//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; }
//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
friend std::ostream& operator<<(std::ostream& os, const Semver &self) {
@ -148,9 +166,10 @@ public:
private:
semver_t 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()); }
};

View File

@ -162,6 +162,40 @@ semver_parse (const char *str, semver_t *ver) {
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.
*
@ -178,37 +212,37 @@ semver_parse_version (const char *str, semver_t *ver) {
char *slice, *next, *endptr;
slice = (char *) str;
index = 0;
// non mandatory
ver->patch = 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;
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;
if (ver->counters) {
free(ver->counters);
ver->counters = NULL;
}
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.
return (index == 2 || index == 3) ? 0 : -1;
return (index >= 2) ? 0 : -1;
}
static int
@ -280,12 +314,11 @@ semver_compare_prerelease (semver_t x, semver_t y) {
int
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) {
if ((res = binary_comparison(x.minor, y.minor)) == 0) {
return binary_comparison(x.patch, y.patch);
}
for (int i = 0; i < x.counter_size && i < y.counter_size; i++) {
if ((res = binary_comparison(x.counters[i], y.counters[i])) != 0)
return res;
}
return res;
@ -379,9 +412,9 @@ semver_lte (semver_t x, semver_t y) {
int
semver_satisfies_caret (semver_t x, semver_t y) {
if (x.major == y.major) {
if (x.major == 0) {
return x.minor >= y.minor;
if (x.counter_size > 0 && y.counter_size > 0 && x.counters[0] == y.counters[0]) {
if (x.counters[0] == 0) {
return x.counter_size > 1 && y.counter_size > 1 && x.counters[1] >= y.counters[1];
}
return 1;
}
@ -402,8 +435,8 @@ semver_satisfies_caret (semver_t x, semver_t y) {
int
semver_satisfies_patch (semver_t x, semver_t y) {
return x.major == y.major
&& x.minor == y.minor;
return x.counter_size > 1 && y.counter_size > 1 && x.counters[0] == y.counters[0]
&& x.counters[1] == y.counters[1];
}
/**
@ -480,6 +513,11 @@ semver_free (semver_t *x) {
free(x->prerelease);
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
semver_render (semver_t *x, char *dest) {
if (x->major) concat_num(dest, x->major, NULL);
if (x->minor) concat_num(dest, x->minor, DELIMITER);
if (x->patch) concat_num(dest, x->patch, DELIMITER);
for (int i = 0; i < x->counter_size; ++i) {
concat_num(dest, x->counters[i], i==0 ? NULL : DELIMITER);
}
if (x->prerelease) concat_char(dest, x->prerelease, PR_DELIMITER);
if (x->metadata) concat_char(dest, x->metadata, MT_DELIMITER);
}
@ -519,18 +557,21 @@ semver_render (semver_t *x, char *dest) {
*/
void
semver_bump (semver_t *x) {
x->major++;
semver_bump_major (semver_t *x) {
if(x->counter_size > 0)
x->counters[0]++;
}
void
semver_bump_minor (semver_t *x) {
x->minor++;
if (x->counter_size > 1)
x->counters[1]++;
}
void
semver_bump_patch (semver_t *x) {
x->patch++;
semver_bump (semver_t *x, int idx) {
if (x->counter_size > idx)
x->counters[idx]++;
}
/**
@ -612,9 +653,9 @@ semver_numeric (semver_t *x) {
char buf[SLICE_SIZE * 3];
memset(&buf, 0, SLICE_SIZE * 3);
if (x->major) concat_num(buf, x->major, NULL);
if (x->minor) concat_num(buf, x->minor, NULL);
if (x->patch) concat_num(buf, x->patch, NULL);
for (int i = 0; i < x->counter_size; ++i) {
concat_num(buf, x->counters[i], NULL);
}
num = parse_int(buf);
if(num == -1) return -1;
@ -625,11 +666,17 @@ semver_numeric (semver_t *x) {
return num;
}
char *semver_strdup(const char *src) {
if (src == NULL) return NULL;
size_t len = strlen(src) + 1;
char *res = malloc(len);
return res != NULL ? (char *) memcpy(res, src, len) : NULL;
char* semver_strdup(const char* src) {
if (src == NULL) return NULL;
size_t len = strlen(src) + 1;
char* res = malloc(len);
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
@ -639,7 +686,10 @@ semver_copy(const semver_t *ver) {
res.metadata = strdup(ver->metadata);
}
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;
}

View File

@ -21,9 +21,8 @@ extern "C" {
*/
typedef struct semver_version_s {
int major;
int minor;
int patch;
int * counters;
int counter_size;
char * metadata;
char * prerelease;
} semver_t;
@ -72,7 +71,10 @@ int
semver_parse (const char *str, semver_t *ver);
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
semver_render (semver_t *x, char *dest);
@ -81,13 +83,13 @@ int
semver_numeric (semver_t *x);
void
semver_bump (semver_t *x);
semver_bump_major (semver_t *x);
void
semver_bump_minor (semver_t *x);
void
semver_bump_patch (semver_t *x);
semver_bump (semver_t *x, int idx);
void
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.
void PresetUpdater::priv::sync_version() const
{
@ -267,34 +280,48 @@ void PresetUpdater::priv::sync_version() const
std::stringstream json_stream(body);
boost::property_tree::read_json(json_stream, root);
bool i_am_pre = false;
std::string best_pre = "1";
std::string best_release = "1";
//at least two number, use '.' as separator. can be followed by -Az23 for prereleased and +Az42 for metadata
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_release_url;
const std::regex reg_num("([0-9]+)");
for (auto json_version : root) {
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");
if (json_version.second.get<bool>("prerelease")) {
if (best_pre < tag) {
best_pre = tag;
if (best_pre < tag_version) {
best_pre = tag_version;
best_pre_url = json_version.second.get<std::string>("html_url");
}
} else {
if (best_release < tag) {
best_release = tag;
if (best_release < tag_version) {
best_release = tag_version;
best_release_url = json_version.second.get<std::string>("html_url");
}
}
}
if ((i_am_pre ? best_pre : best_release) <= SLIC3R_VERSION_FULL)
//if release is more recent than beta, use release anyway
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;
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);
evt->SetString(i_am_pre ? best_pre : best_release);
evt->SetString((i_am_pre ? best_pre : best_release).to_string());
GUI::wxGetApp().QueueEvent(evt);
})
.perform_sync();