Skip to content

Instantly share code, notes, and snippets.

@ivanweiler
Created January 28, 2020 20:58
Show Gist options
  • Save ivanweiler/9068fd4724ee63a53efe6fdc1b1f80f5 to your computer and use it in GitHub Desktop.
Save ivanweiler/9068fd4724ee63a53efe6fdc1b1f80f5 to your computer and use it in GitHub Desktop.
<?php
/**
* Fast direct PHP port of https://github.com/github/version_sorter
* Not really tested
*/
class Strchunk
{
/**
* @var int
*/
public $offset;
/**
* @var int
*/
public $len;
}
class VersionCompare
{
/**
* @var Strchunk
*/
public $string;
/**
* @var int
*/
public $number;
/**
* VersionCompare constructor.
*/
public function __construct()
{
$this->string = new Strchunk();
}
}
class VersionNumber
{
/**
* @var string
*/
public $original;
/**
* @var int
*/
public $size;
/**
* @var int
*/
public $num_flags;
/**
* @var array
*/
public $comp = [];
}
function strchunk_cmp(string $original_a, \Strchunk $a, string $original_b, \Strchunk $b): int
{
$len = min($a->len, $b->len);
//$cmp = memcmp($original_a + $a->offset, $original_b + $b->offset, $len);
$cmp = strncmp(substr($original_a, $a->offset), substr($original_b, $b->offset), $len);
return $cmp ? $cmp : (int)($a->len - $b->len);
}
function compare_version_number(\VersionNumber $a, \VersionNumber $b): int
{
$max_n = min($a->size, $b->size);
for ($n = 0; $n < $max_n; ++$n) {
$num_a = ($a->num_flags & (1 << $n)) != 0;
$num_b = ($b->num_flags & (1 << $n)) != 0;
if ($num_a == $num_b) {
$ca = $a->comp[$n];
$cb = $b->comp[$n];
$cmp = 0;
if ($num_a) {
$cmp64 = $ca->number - $cb->number;
$cmp = (int)max(-1, min(1, $cmp64));
} else {
$cmp = strchunk_cmp(
$a->original, $ca->string,
$b->original, $cb->string
);
}
if ($cmp) return $cmp;
} else {
return $num_a ? 1 : -1;
}
}
if ($a->size < $b->size)
return ($b->num_flags & (1 << $n)) ? -1 : 1;
if ($a->size > $b->size)
return ($a->num_flags & (1 << $n)) ? 1 : -1;
return 0;
}
function parse_version_number(string $string): \VersionNumber
{
$num_flags = 0;
$comp_n = 0;
$version = new VersionNumber();
$original = $string;
$string = str_split($string);
for($offset = 0; isset($string[$offset]) && $comp_n < 64;) {
if (ctype_digit($string[$offset])) {
$number = 0;
$start = $offset;
$overflown = 0;
while (isset($string[$offset]) && ctype_digit($string[$offset])) {
if (!$overflown) {
$old_number = $number;
$number = (10 * $number) + ($string[$offset] - '0'); //??
//printf("%d", $number);
if ($number < $old_number) {
$overflown = 1;
}
}
$offset++;
}
$version->comp[$comp_n] = new VersionCompare();
if ($overflown) {
$version->comp[$comp_n]->string->offset = $start;
$version->comp[$comp_n]->string->len = $offset - $start;
} else {
$version->comp[$comp_n]->number = $number;
$num_flags |= (1 << $comp_n);
//var_dump($num_flags);
}
$comp_n++;
continue;
}
if ($string[$offset] == '-' || ctype_alpha($string[$offset])) {
$start = $offset;
if ($string[$offset] == '-') {
$offset++;
}
while (isset($string[$offset]) && ctype_alpha($string[$offset])) {
$offset++;
}
$version->comp[$comp_n] = new VersionCompare();
$version->comp[$comp_n]->string->offset = $start;
$version->comp[$comp_n]->string->len = $offset - $start;
$comp_n++;
continue;
}
$offset++;
}
$version->original = $original;
$version->num_flags = $num_flags;
$version->size = $comp_n;
return $version;
}
////////////////////////////////
$versions = [
"1.2b.005",
"1.2",
"1.002.05"
];
usort($versions, function($a, $b) {
$version1 = parse_version_number($a);
$version2 = parse_version_number($b);
//var_dump(compare_version_number($version1, $version2));
return compare_version_number($version1, $version2);
});
var_dump($versions);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment