stats.sh: speed up stats extraction
authorXhmikosR <redacted>
Sat, 22 Nov 2025 06:26:52 +0000 (08:26 +0200)
committerXhmikosR <redacted>
Sat, 22 Nov 2025 15:27:30 +0000 (17:27 +0200)
stats.sh

index ff4dabea0a4b41f54fef67e4be17fa9b5bca70c4..cb59af66114d71424f0fab9a36969d0d55aac516 100755 (executable)
--- a/stats.sh
+++ b/stats.sh
@@ -1,13 +1,69 @@
 #!/usr/bin/env bash
 
-# clear file
-true > stats.out
-
-for TAG_DATE in $(git tag --sort=creatordate  --format='%(refname:short),%(creatordate:short)'); do
-  # echo "$TAG_DATE"
-  split=(${TAG_DATE//,/ })
-  # echo ${split[0]}
-  entries=$(git show tags/${split[0]}:readmeData.json | jq '.base.entries')
-  if [[ -z "$entries" ]]; then continue; fi
-  echo ${split[1]},${entries} >> stats.out
+# shellcheck enable=require-variable-braces
+
+set -euo pipefail
+
+# Ensure required tools are installed
+for tool in git jq; do
+  if ! command -v "${tool}" > /dev/null 2>&1; then
+    echo "Error: ${tool} not found in PATH" >&2
+    exit 1
+  fi
+done
+
+# Clear output file
+: > stats.out
+
+# Collect "tag:readmeData.json date" pairs for all tags
+mapfile -t taglist < <(
+  git for-each-ref --sort=creatordate \
+    --format='%(refname:short) %(creatordate:short)' refs/tags |
+    awk '{print $1 ":readmeData.json " $2}'
+)
+
+# Feed all object names into one cat-file process
+exec 3< <(printf '%s\n' "${taglist[@]}" | cut -d' ' -f1 | git cat-file --batch)
+
+for line in "${taglist[@]}"; do
+  # Extract the date field by removing everything up to the first space
+  date=${line#* }
+
+  # Read one header line from fd 3
+  if ! read -r -a header_fields <&3; then
+    break
+  fi
+
+  # Skip if header is incomplete
+  if ((${#header_fields[@]} < 3)); then
+    continue
+  fi
+
+  size=${header_fields[2]}
+
+  # Validate that size is numeric
+  if [[ ! "${size}" =~ ^[0-9]+$ ]]; then
+    continue
+  fi
+
+  # Read exactly ${size} bytes of blob content
+  IFS= read -r -N "${size}" blob <&3
+
+  # Consume the newline that follows the blob
+  read -r _ <&3 || true
+
+  # Stream parse JSON only if blob is non-empty
+  if [[ -z "${blob}" ]]; then
+    continue
+  fi
+
+  # Stream parse JSON with jq in "raw input" mode to avoid subshell overhead
+  jq -nr --arg date "${date}" --argjson blob "${blob}" '
+    $blob.base.entries // empty
+    | if type=="array" then
+        .[] | "\($date),\(.)"
+      else
+        "\($date),\(.)"
+      end
+  ' >> stats.out 2> /dev/null || true
 done
git clone https://git.99rst.org/PROJECT