Skip to content

Commit b39eeac

Browse files
authored
Merge pull request #6 from sqids/fix-bug-leadingzero
Fix zero padding throws error
2 parents 562b13a + 273ab6d commit b39eeac

File tree

4 files changed

+122
-85
lines changed

4 files changed

+122
-85
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# CHANGELOG
22

3+
## [1.0.1] - 2024-01-28
4+
5+
### Fixed
6+
7+
- Fix encoding numbers with leading zeros throws an error. (#6)
8+
39
## [1.0.0] - 2024-01-28
410

511
### Added

src/sqids

Lines changed: 73 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
#!/usr/bin/env bash
22

3+
# -e: exit immediately if a command exits with a non-zero status
4+
# -u: treat unset variables as an error and exit immediately
5+
# -C: prevent overwriting files
6+
set -euC
7+
38
readonly DEFAULT_ALPHABET="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
49
readonly DEFAULT_BLOCKLIST=(
510
"0rgasm"
@@ -567,61 +572,63 @@ readonly DEFAULT_MIN_LENGTH=0
567572

568573
# usage: ord "A" -> 65
569574
ord() {
570-
printf -v retval "%d" "'$1"
575+
printf -v __RETURN "%d" "'$1"
571576
}
572577

573578
# usage: splitstr "abcde" -> "a b c d e"
574579
splitstr() {
575580
local s="$1"
581+
local i
576582
arr=()
577-
for ((i_splitstr = 0; i_splitstr < ${#s}; i_splitstr++)); do
578-
arr+=("${s:$i_splitstr:1}")
583+
for ((i = 0; i < ${#s}; i++)); do
584+
arr+=("${s:$i:1}")
579585
done
580586

581-
retval=${arr[*]}
587+
__RETURN=${arr[*]}
582588
}
583589

584590
# usage: joinchars "," a b c d e -> "a,b,c,d,e"
585591
joinchars() {
586592
local separator=$1
587593
local str=""
588-
for ((i_joinchars = 2; i_joinchars < $#; i_joinchars++)); do
589-
str+="${!i_joinchars}"
594+
local i
595+
for ((i = 2; i < $#; i++)); do
596+
str+="${!i}"
590597
str+="$separator"
591598
done
592599
str+="${!#}"
593600

594-
retval="$str"
601+
__RETURN="$str"
595602
}
596603

597604
# usage: lower "AbCdE" -> "abcde"
598605
lower() {
599-
retval="${1,,}"
606+
__RETURN="${1,,}"
600607
}
601608

602609
# usage: shuffle "abcdefg" -> "bcefgad"
603610
shuffle() {
604611
local alphabet="$1"
605612
splitstr "$alphabet"
606613
local chars
607-
IFS=" " read -r -a chars <<<"$retval"
614+
IFS=" " read -r -a chars <<<"$__RETURN"
608615
local len=${#alphabet}
609-
local i_shuffle=0
610-
local j_shuffle=$((len - 1))
611-
612-
while [ $j_shuffle -gt 0 ]; do
613-
614-
ord "${chars[i_shuffle]}"
615-
local i_shuffle_ord=$retval
616-
ord "${chars[j_shuffle]}"
617-
local j_shuffle_ord=$retval
618-
619-
local r_shuffle=$(((i_shuffle * j_shuffle + i_shuffle_ord + j_shuffle_ord) % len))
620-
local temp=${chars[$i_shuffle]}
621-
chars[i_shuffle]=${chars[$r_shuffle]}
622-
chars[r_shuffle]=$temp
623-
i_shuffle=$((i_shuffle + 1))
624-
j_shuffle=$((j_shuffle - 1))
616+
local i=0
617+
local j=$((len - 1))
618+
619+
while [ $j -gt 0 ]; do
620+
621+
ord "${chars[i]}"
622+
local i_ord=$__RETURN
623+
ord "${chars[j]}"
624+
local j_ord=$__RETURN
625+
626+
local r=$(((i * j + i_ord + j_ord) % len))
627+
local temp=${chars[$i]}
628+
chars[i]=${chars[$r]}
629+
chars[r]=$temp
630+
i=$((i + 1))
631+
j=$((j - 1))
625632
done
626633

627634
joinchars "" "${chars[@]}"
@@ -652,7 +659,7 @@ to_number() {
652659
local count=${#alphabet}
653660

654661
splitstr "$id"
655-
for id_char in $retval; do
662+
for id_char in $__RETURN; do
656663
local index
657664
for ((index = 0; index < count; index++)); do
658665
if [ "${alphabet:$index:1}" == "$id_char" ]; then
@@ -662,15 +669,15 @@ to_number() {
662669
result=$((result * count + index))
663670
done
664671

665-
retval=$result
672+
__RETURN=$result
666673
}
667674

668675
# usage: is_blocked_id "blockedword1 blockedword2 blockedword3" "abcde" -> false
669676
is_blocked_id() {
670677
local block_list=$1
671678
local id="$2"
672679
lower "$id"
673-
id=$retval
680+
id=$__RETURN
674681

675682
for word in $block_list; do
676683
if [ ${#word} -gt ${#id} ]; then
@@ -679,21 +686,21 @@ is_blocked_id() {
679686

680687
if [[ ${#id} -le 3 || ${#word} -le 3 ]]; then
681688
if [ "$id" == "$word" ]; then
682-
retval=true
689+
__RETURN=true
683690
return 0
684691
fi
685692
elif [[ "$word" == *[0-9]* ]]; then
686693
if [[ "$id" == "$word"* || "$id" == *"$word" ]]; then
687-
retval=true
694+
__RETURN=true
688695
return 0
689696
fi
690697
elif [[ "$id" == *"$word"* ]]; then
691-
retval=true
698+
__RETURN=true
692699
return 0
693700
fi
694701
done
695702

696-
retval=false
703+
__RETURN=false
697704
return 0
698705
}
699706

@@ -702,6 +709,7 @@ encode_numbers() {
702709
local original_alphabet_str
703710
local block_list
704711
local min_length
712+
local i
705713

706714
local OPTIND=0
707715
while getopts a:b:l: OPT; do
@@ -717,7 +725,7 @@ encode_numbers() {
717725
done
718726

719727
shuffle "$original_alphabet_str"
720-
local alphabet_str=$retval
728+
local alphabet_str=$__RETURN
721729

722730
shift $((OPTIND - 1))
723731

@@ -735,7 +743,7 @@ encode_numbers() {
735743
local v=${numbers[i]}
736744
local char=${alphabet_str:$((v % ${#alphabet_str})):1}
737745
ord "$char"
738-
local char_ord=$retval
746+
local char_ord=$__RETURN
739747
offset=$((offset + char_ord + i))
740748
done
741749

@@ -765,33 +773,33 @@ encode_numbers() {
765773
for ((i = 0; i < ${#numbers[@]}; i++)); do
766774
local num=${numbers[$i]}
767775
to_id "$num" "${alphabet_str:1}"
768-
ret+=("$retval")
776+
ret+=("$__RETURN")
769777

770778
if [[ $i -ge $((${#numbers[@]} - 1)) ]]; then
771779
continue
772780
fi
773781

774782
ret+=("${alphabet_str:0:1}")
775783
shuffle "$alphabet_str"
776-
alphabet_str=$retval
784+
alphabet_str=$__RETURN
777785
done
778786

779787
joinchars "" "${ret[@]}"
780-
local id=$retval
788+
local id=$__RETURN
781789

782790
if [ "$min_length" -gt "${#id}" ]; then
783791
id+=${alphabet_str:0:1}
784792

785793
while [ $((min_length - ${#id})) -gt 0 ]; do
786794
shuffle "$alphabet_str"
787-
alphabet_str=$retval
795+
alphabet_str=$__RETURN
788796
joinchars "" "${alphabet_str:0:$((min_length - ${#id} < ${#alphabet_str} ? min_length - ${#id} : ${#alphabet_str}))}"
789-
id+=$retval
797+
id+=$__RETURN
790798
done
791799
fi
792800

793801
is_blocked_id "$block_list" "$id"
794-
local is_blocked=$retval
802+
local is_blocked=$__RETURN
795803
if $is_blocked; then
796804
encode_numbers -a "$original_alphabet_str" -b "$block_list" -l "$min_length" "${numbers[@]}" $((increment + 1))
797805
else
@@ -823,13 +831,18 @@ encode() {
823831
if [[ -z "$*" ]]; then
824832
echo ""
825833
else
826-
encode_numbers -a "$alphabet" -b "$block_list" -l "$min_length" "$@" 0
834+
nums=()
835+
for num in "$@"; do
836+
nums+=("$((10#$num))")
837+
done
838+
encode_numbers -a "$alphabet" -b "$block_list" -l "$min_length" "${nums[@]}" 0
827839
fi
828840
}
829841

830842
# usage: decode -a $DEFAULT_ALPHABET "abcde" -> 10273415
831843
decode() {
832844
local alphabet_str
845+
local i
833846

834847
local OPTIND=0
835848
while getopts :a: OPT; do
@@ -843,7 +856,7 @@ decode() {
843856
done
844857

845858
shuffle "$alphabet_str"
846-
alphabet_str=$retval
859+
alphabet_str=$__RETURN
847860

848861
shift $((OPTIND - 1))
849862

@@ -857,10 +870,10 @@ decode() {
857870

858871
# check if any characters are not in the alphabet
859872
splitstr "$id"
860-
for c in $retval; do
873+
for c in $__RETURN; do
861874
local does_exist=false
862875
splitstr "$alphabet_str"
863-
for a in $retval; do
876+
for a in $__RETURN; do
864877
if [ "$a" == "$c" ]; then
865878
does_exist=true
866879
break
@@ -894,7 +907,7 @@ decode() {
894907
done
895908

896909
joinchars "" "${alphabet_chars_rev[@]}"
897-
alphabet_str=$retval
910+
alphabet_str=$__RETURN
898911

899912
id="${id:1}"
900913

@@ -920,18 +933,18 @@ decode() {
920933
return 0
921934
else
922935
to_number "${chunks[0]}" "${alphabet_str:1:${#alphabet_str}-1}"
923-
ret+=("$retval")
936+
ret+=("$__RETURN")
924937

925938
if [ "${#chunks[@]}" -gt 1 ]; then
926939
shuffle "$alphabet_str"
927-
alphabet_str=$retval
940+
alphabet_str=$__RETURN
928941
fi
929942
fi
930943
fi
931944

932945
if [ "${#chunks[@]}" -gt 1 ]; then
933946
joinchars "$separator" "${chunks[@]:1}"
934-
id=$retval
947+
id=$__RETURN
935948
else
936949
echo "${ret[@]}"
937950
return 0
@@ -974,6 +987,10 @@ main() {
974987
local min_length="$DEFAULT_MIN_LENGTH"
975988
local alphabet="$DEFAULT_ALPHABET"
976989
local block_list="${DEFAULT_BLOCKLIST[*]}"
990+
local flag_a=false
991+
local flag_b=false
992+
local flag_e=false
993+
local flag_d=false
977994

978995
local OPTIND=0
979996
while getopts :a:b:l:edhv OPT; do
@@ -1011,15 +1028,15 @@ main() {
10111028
exit 1
10121029
fi
10131030

1014-
if [[ $flag_e && $flag_d ]]; then
1031+
if $flag_e && $flag_d; then
10151032
echo "ERROR: option -e (encode) and -d (decode) cannot be used together" >&2
10161033
exit 1
10171034
fi
10181035

10191036
splitstr "$alphabet"
1020-
for char in $retval; do
1037+
for char in $__RETURN; do
10211038
ord "$char"
1022-
local char_ord=$retval
1039+
local char_ord=$__RETURN
10231040
if [[ $char_ord -gt 127 ]]; then
10241041
echo "ERROR: Alphabet cannot contain multibyte characters" >&2
10251042
exit 1
@@ -1045,20 +1062,20 @@ main() {
10451062
local filtered_blocklist=()
10461063
if $flag_a || $flag_b; then
10471064
lower "$alphabet"
1048-
local alphabet_lower=$retval
1065+
local alphabet_lower=$__RETURN
10491066
declare -A alphabet_lower_dict
10501067
splitstr "$alphabet_lower"
1051-
for char in $retval; do
1068+
for char in $__RETURN; do
10521069
alphabet_lower_dict[$char]=true
10531070
done
10541071

10551072
lower "$block_list"
1056-
for word_lower in $retval; do
1073+
for word_lower in $__RETURN; do
10571074
if [[ ${#word_lower} -ge 3 ]]; then
10581075
local does_exist=true
10591076
splitstr "$word_lower"
1060-
for char in $retval; do
1061-
if [[ -z "${alphabet_lower_dict[$char]}" ]]; then
1077+
for char in $__RETURN; do
1078+
if [[ -z "${alphabet_lower_dict[$char]:-false}" ]]; then
10621079
does_exist=false
10631080
break
10641081
fi

tests/test_encoding.bats

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,17 @@
103103
# run ./src/sqids -e $((1 << 63))
104104
# [ $status -eq 1 ]
105105
}
106+
107+
@test "test_zero_padding" {
108+
numbers=(0 1 2 3 4 5 6 7 8 9)
109+
ids=("bM" "Uk" "gb" "Ef" "Vq" "uw" "OI" "AX" "p6" "nJ")
110+
111+
for i in {0..9}; do
112+
run ./src/sqids -e "0${numbers[$i]}"
113+
[ $status -eq 0 ]
114+
[[ $output == "${ids[$i]}" ]]
115+
run ./src/sqids -d "${ids[$i]}"
116+
[ $status -eq 0 ]
117+
[[ "${output[@]}" == "${numbers[$i]}" ]]
118+
done
119+
}

0 commit comments

Comments
 (0)