Skip to content

Instantly share code, notes, and snippets.

@sequix
Last active September 6, 2023 03:06
Show Gist options
  • Save sequix/c5da2e76bcebd654003437a323cb114f to your computer and use it in GitHub Desktop.
Save sequix/c5da2e76bcebd654003437a323cb114f to your computer and use it in GitHub Desktop.
machine-independent cpuset operations
#!/bin/bash
set -euo pipefail
help() {
printf "cpuset.sh version: 3.1\n"
printf "usage:\n"
printf "cpuset.sh union 0-1 2 3-4,5 => 0-5\n"
printf "cpuset.sh intersect 0-9 4-6 4-5 => 4-5\n"
printf "cpuset.sh minus 0-9 4-5 => 0-3,6-9\n"
printf "cpuset.sh shrink 0,1,2,3 => 0-3\n"
printf 'cpuset.sh expand 0-3 => 0\\n1\\n2\\n3\\n\n'
printf "cpuset.sh tomask 0-15 => ffff\n"
printf "cpuset.sh todigit ffff => 0-15\n"
}
expand() {
local i=0
local c=""
local lc=""
local acc=""
local lacc=""
local src="$1"
local rst
declare -a rst
for (( i=0; i<${#src}; i++ )); do
c="${src:$i:1}"
case "$c" in
[0-9])
acc=$((acc*10+$c))
;;
-)
if ! <<<"$lc" grep -q "[0-9]"; then
echo "invalid hyphen in set $src"
exit 1
fi
lacc="$acc"
acc=""
;;
,)
if [ -n "$lacc" ]; then
rst+=($(seq "$lacc" "$acc"))
elif [ -n "$acc" ]; then
rst+=("$acc")
fi
acc=""
lacc=""
;;
*)
echo "invalid character '$c' in set $src"
exit 1
;;
esac
lc="$c"
done
if [ -n "$lacc" ]; then
rst+=($(seq "$lacc" "$acc"))
elif [ -n "$acc" ]; then
rst+=("$acc")
fi
<<<"${rst[@]}" tr ' ' '\n' | sort -nu
}
expand_to_file() {
local src="$1"
local filename=""
filename=$(mktemp)
expand "$src" >"$filename"
echo "$filename"
}
shrink_internal() {
local cpus=("$@")
local n=${#cpus[@]}
if [ $n -eq 0 ]; then
return
elif [ $n -eq 1 ]; then
echo "${cpus[0]}"
return
fi
local i=0
local j=1
local ci="${cpus[0]}"
local cj=""
local cj1=""
local rst=""
for ((j=1; j < $n; j++)); do
cj="${cpus[$j]}"
cj1="${cpus[$((j-1))]}"
if [ "$cj" -ne "$((cj1+1))" ]; then
if [ "$j" -eq "$((i+1))" ]; then
rst="${rst}${ci},"
else
rst="${rst}${ci}-${cj1},"
fi
i="$j"
ci="${cj}"
fi
done
if [ "$j" -eq "$((i+1))" ]; then
rst="${rst}${ci}"
else
rst="${rst}${ci}-${cpus[$((j-1))]}"
fi
echo $rst
}
union() {
local files
declare -a files
for cpus; do
files+=($(expand_to_file "$cpus"))
done
shrink_internal $(sort -nu "${files[@]}")
rm -f "${files[@]}"
}
intersect() {
local files
declare -a files
for cpus; do
files+=($(expand_to_file "$cpus"))
done
shrink_internal $(sort -n "${files[@]}" | uniq -c | awk "\$1==${#files[@]} {print \$2}")
rm -f "${files[@]}"
}
minus() {
local a="$1"
local b="$2"
local af=""
local bf=""
af=$(expand_to_file "$a")
bf=$(expand_to_file "$b")
shrink_internal $(sort -n "$af" "$bf" "$bf" | uniq -u)
rm -f "$af" "$bf"
}
tomask() {
local cpus
cpus=($(expand "$1"))
local n=${#cpus[@]}
if [ $n -eq 0 ]; then
echo 0
return
fi
local c=0
local ni=0
local nc="${cpus[0]}"
local max="${cpus[$((n-1))]}"
local rst=""
local rst_c=0
for ((c=0; c<=max; c+=4)); do
rst_c=0
while [ "$((c+3))" -ge "$nc" ]; do
rst_c=$(( rst_c | (1<<(nc-c)) ))
ni=$((ni+1))
if [ "$ni" -ge "$n" ]; then
break
fi
nc=${cpus[$ni]}
done
rst="$(printf "%x" ${rst_c})${rst}"
done
echo "$rst"
}
todigit() {
local c=""
local src="$1"
local i=${#src}
local base=0
local rst
declare -a rst
for ((i=i-1; i>=0; i--)); do
c=${src:$i:1}
case "$c" in
0)
true
;;
1)
rst+=($((base+0)))
;;
2)
rst+=($((base+1)))
;;
3)
rst+=($((base+0)))
rst+=($((base+1)))
;;
4)
rst+=($((base+2)))
;;
5)
rst+=($((base+0)))
rst+=($((base+2)))
;;
6)
rst+=($((base+1)))
rst+=($((base+2)))
;;
7)
rst+=($((base+0)))
rst+=($((base+1)))
rst+=($((base+2)))
;;
8)
rst+=($((base+3)))
;;
9)
rst+=($((base+0)))
rst+=($((base+3)))
;;
a|A)
rst+=($((base+1)))
rst+=($((base+3)))
;;
b|B)
rst+=($((base+0)))
rst+=($((base+1)))
rst+=($((base+3)))
;;
c|C)
rst+=($((base+2)))
rst+=($((base+3)))
;;
d|D)
rst+=($((base+0)))
rst+=($((base+2)))
rst+=($((base+3)))
;;
e|E)
rst+=($((base+1)))
rst+=($((base+2)))
rst+=($((base+3)))
;;
f|F)
rst+=($((base+0)))
rst+=($((base+1)))
rst+=($((base+2)))
rst+=($((base+3)))
;;
*)
echo "invalid mask '$src'"
exit 1
;;
esac
base=$((base+4))
done
shrink_internal "${rst[@]}"
}
main() {
if [ $# -eq 0 ]; then
help
exit 0
fi
case "$1" in
union)
shift 1
if [ $# -eq 0 ]; then
echo "at least 1 argument for union action"
exit 1
fi
union "$@"
;;
intersect)
shift 1
if [ $# -eq 0 ]; then
echo "at least 1 argument for intersect action"
exit 1
fi
intersect "$@"
;;
minus)
if [ $# -ne 3 ]; then
echo "expected exact 2 arguments for minus action"
exit 1
fi
minus "$2" "$3"
;;
shrink)
if [ $# -ne 2 ]; then
echo "expected exact 1 argument for shrink action"
exit 1
fi
union "$2"
;;
expand)
if [ $# -ne 2 ]; then
echo "expected exact 1 argument for expand action"
exit 1
fi
expand "$2"
;;
tomask)
if [ $# -ne 2 ]; then
echo "expected exact 1 argument for tomask action"
exit 1
fi
tomask "$2"
;;
todigit)
if [ $# -ne 2 ]; then
echo "expected exact 1 argument for todigit action"
exit 1
fi
todigit "$2"
;;
*)
echo "invalid action '$1'"
help
exit 1
;;
esac
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment