mirror of
https://github.com/nvm-sh/nvm.git
synced 2026-10-11 00:00:13 +08:00
[Perf] nvm_version_greater, nvm_version_greater_than_or_equal_to: avoid awk where possible
This commit is contained in:
@@ -796,33 +796,81 @@ nvm_curl_version() {
|
||||
command curl -V | command awk '{ if ($1 == "curl") print $2 }' | command sed 's/-.*$//g'
|
||||
}
|
||||
|
||||
# Try to do some shell magic to do semver greater-than maths.
|
||||
# Fall through to the expensive test by calling `awk` if we can't be sure.
|
||||
nvm_version_greater() {
|
||||
command awk 'BEGIN {
|
||||
local NVM_LEFT
|
||||
NVM_LEFT="${1#v}"
|
||||
local NVM_RIGHT
|
||||
NVM_RIGHT="${2#v}"
|
||||
local NVM_PLAIN
|
||||
# a third, more complex, argument of `1` (a flag) compares equal versions as true; `0` or
|
||||
# no argument compares them as false.
|
||||
local NVM_OR_EQUAL
|
||||
NVM_OR_EQUAL="${3:-0}"
|
||||
NVM_PLAIN='true'
|
||||
|
||||
# `x.y.z` operands are compared in shell arithmetic, which avoids a fork,
|
||||
# except when a group is long enough to overflow it; anything else - partial
|
||||
# versions, prereleases, and malformed operands - keeps the comparison this
|
||||
# function has always made
|
||||
case "${NVM_LEFT}" in
|
||||
'' | *[!0-9.]* | *..* | .* | *. | *.*.*.*) NVM_PLAIN='false' ;;
|
||||
*.*.*) ;;
|
||||
*) NVM_PLAIN='false' ;;
|
||||
esac
|
||||
case "${NVM_RIGHT}" in
|
||||
'' | *[!0-9.]* | *..* | .* | *. | *.*.*.*) NVM_PLAIN='false' ;;
|
||||
*.*.*) ;;
|
||||
*) NVM_PLAIN='false' ;;
|
||||
esac
|
||||
|
||||
if [ "${NVM_PLAIN}" = 'true' ] && [ "${#NVM_LEFT}" -lt 19 ] && [ "${#NVM_RIGHT}" -lt 19 ]; then
|
||||
local NVM_GROUP
|
||||
local NVM_LEFT_GROUP
|
||||
local NVM_RIGHT_GROUP
|
||||
NVM_GROUP=1
|
||||
while [ "${NVM_GROUP}" -le 3 ]; do
|
||||
NVM_LEFT_GROUP="${NVM_LEFT%%.*}"
|
||||
NVM_LEFT="${NVM_LEFT#*.}"
|
||||
NVM_RIGHT_GROUP="${NVM_RIGHT%%.*}"
|
||||
NVM_RIGHT="${NVM_RIGHT#*.}"
|
||||
if [ "${NVM_LEFT_GROUP}" -lt "${NVM_RIGHT_GROUP}" ]; then
|
||||
return 1
|
||||
elif [ "${NVM_LEFT_GROUP}" -gt "${NVM_RIGHT_GROUP}" ]; then
|
||||
return 0
|
||||
fi
|
||||
NVM_GROUP=$((NVM_GROUP + 1))
|
||||
done
|
||||
if [ "${NVM_OR_EQUAL}" = 1 ]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
command awk -v or_equal="${NVM_OR_EQUAL}" 'BEGIN {
|
||||
if (ARGV[1] == "" || ARGV[2] == "") exit(1)
|
||||
split(ARGV[1], a, /\./);
|
||||
split(ARGV[2], b, /\./);
|
||||
for (i=1; i<=3; i++) {
|
||||
if (a[i] && a[i] !~ /^[0-9]+$/) exit(2);
|
||||
if (b[i] && b[i] !~ /^[0-9]+$/) { exit(0); }
|
||||
if (b[i] && b[i] !~ /^[0-9]+$/) {
|
||||
# a non-numeric right-hand group (a prerelease): the left is greater,
|
||||
# unless equality counts, in which case the comparison below decides
|
||||
if (or_equal != 1) { exit(0); }
|
||||
}
|
||||
if (a[i] < b[i]) exit(3);
|
||||
else if (a[i] > b[i]) exit(0);
|
||||
}
|
||||
if (or_equal == 1) { exit(0); }
|
||||
exit(4)
|
||||
}' "${1#v}" "${2#v}"
|
||||
}' "${NVM_LEFT}" "${NVM_RIGHT}"
|
||||
}
|
||||
|
||||
# Try to do some shell magic to do semver greater-than or equal-to maths.
|
||||
# Fall through to the expensive test by calling `awk` if we can't be sure.
|
||||
# see `nvm_version_greater`
|
||||
nvm_version_greater_than_or_equal_to() {
|
||||
command awk 'BEGIN {
|
||||
if (ARGV[1] == "" || ARGV[2] == "") exit(1)
|
||||
split(ARGV[1], a, /\./);
|
||||
split(ARGV[2], b, /\./);
|
||||
for (i=1; i<=3; i++) {
|
||||
if (a[i] && a[i] !~ /^[0-9]+$/) exit(2);
|
||||
if (a[i] < b[i]) exit(3);
|
||||
else if (a[i] > b[i]) exit(0);
|
||||
}
|
||||
exit(0)
|
||||
}' "${1#v}" "${2#v}"
|
||||
nvm_version_greater "${1-}" "${2-}" 1
|
||||
}
|
||||
|
||||
nvm_version_dir() {
|
||||
|
||||
@@ -6,6 +6,8 @@ die () { echo "$@" ; exit 1; }
|
||||
\. ../../../nvm.sh
|
||||
|
||||
nvm_version_greater 0.10.0 0.2.12 || die '"nvm_version_greater 0.10.0 0.2.12" did not return true'
|
||||
nvm_version_greater 0.20.12 0.10.0 || die '"nvm_version_greater 0.20.12 0.10.0" did not return true'
|
||||
nvm_version_greater v18.20.0 v18.19.9 || die '"nvm_version_greater v18.20.0 v18.19.9" did not return true'
|
||||
|
||||
if nvm_version_greater 0.10.0 0.20.12; then
|
||||
die '"nvm_version_greater 0.10.0 0.20.12" returned true'
|
||||
@@ -14,3 +16,40 @@ fi
|
||||
if nvm_version_greater 0.10.0 0.10.0; then
|
||||
die '"nvm_version_greater" returned false for the same two versions'
|
||||
fi
|
||||
|
||||
if nvm_version_greater v18.20.0 v18.20.0; then
|
||||
die '"nvm_version_greater v18.20.0 v18.20.0" returned true for the same two versions'
|
||||
fi
|
||||
|
||||
if nvm_version_greater v18.19.9 v18.20.0; then
|
||||
die '"nvm_version_greater v18.19.9 v18.20.0" returned true'
|
||||
fi
|
||||
|
||||
# a partial version is not a plain `x.y.z`, so it keeps the awk comparison
|
||||
nvm_version_greater 0.10 0.2.12 || die '"nvm_version_greater 0.10 0.2.12" did not return true'
|
||||
|
||||
# the comparison must not write anything to stdout (zsh's `typeset` prints a
|
||||
# local variable when it is redeclared)
|
||||
OUTPUT="$(nvm_version_greater 0.10.0 0.2.12 2>&1)"
|
||||
[ -z "${OUTPUT}" ] || die "nvm_version_greater 0.10.0 0.2.12 wrote output: '${OUTPUT}'"
|
||||
|
||||
# `nvm_version_greater` must not fork a command for a plain `x.y.z` pair:
|
||||
# mock `awk`, which the comparison used, and fail if it runs
|
||||
TEST_DIR="$(pwd)/nvm_version_greater_tmp"
|
||||
[ ! -e "${TEST_DIR}" ] && mkdir "${TEST_DIR}"
|
||||
MOCK_BIN="${TEST_DIR}/bin"
|
||||
mkdir -p "${MOCK_BIN}"
|
||||
{
|
||||
echo '#!/bin/sh'
|
||||
echo 'echo "awk was called" >&2'
|
||||
echo 'exit 1'
|
||||
} > "${MOCK_BIN}/awk"
|
||||
chmod +x "${MOCK_BIN}/awk"
|
||||
|
||||
PATH="${MOCK_BIN}:${PATH}" nvm_version_greater 0.10.0 0.2.12 \
|
||||
|| die 'nvm_version_greater 0.10.0 0.2.12 forked a command'
|
||||
|
||||
PATH="${MOCK_BIN}:${PATH}" nvm_version_greater v18.20.0 v18.19.9 \
|
||||
|| die 'nvm_version_greater v18.20.0 v18.19.9 forked a command'
|
||||
|
||||
rm -rf "${TEST_DIR}"
|
||||
|
||||
Reference in New Issue
Block a user