]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
basename: avoid duplicate strlen calls on the suffix
authorCollin Funk <collin.funk1@gmail.com>
Sat, 4 Apr 2026 19:44:14 +0000 (12:44 -0700)
committerCollin Funk <collin.funk1@gmail.com>
Sat, 4 Apr 2026 21:55:56 +0000 (14:55 -0700)
    $ ltrace -c ./src/basename-prev -s a $(seq 100000) > /dev/null
    % time     seconds  usecs/call     calls      function
    ------ ----------- ----------- --------- --------------------
     50.00   30.030316          75    400000 strlen
    [...]
    $ ltrace -c ./src/basename -s a $(seq 100000) > /dev/null
    % time     seconds  usecs/call     calls      function
    ------ ----------- ----------- --------- --------------------
     42.88   22.413953          74    300001 strlen
    [...]

* src/basename.c (remove_suffix, perform_basename): Add a length
argument for the suffix and use it instead of strlen.
(main): Calculate the suffix length. Refactor code to avoid calling
perform_basename in multiple places.

src/basename.c

index 777ee9867e99c846ba3c835db8590fe5a7b3c2d7..96bbb71c52c3a3fca86c2bd54c395a1ccf906a6d 100644 (file)
@@ -88,10 +88,10 @@ Examples:\n\
    consists entirely of SUFFIX.  */
 
 static void
-remove_suffix (char *name, char const *suffix)
+remove_suffix (char *name, char const *suffix, idx_t suffix_len)
 {
   char *np = name + strlen (name);
-  char const *sp = suffix + strlen (suffix);
+  char const *sp = suffix + suffix_len;
 
   while (np > name && sp > suffix)
     if (*--np != *--sp)
@@ -100,11 +100,13 @@ remove_suffix (char *name, char const *suffix)
     *np = '\0';
 }
 
-/* Perform the basename operation on STRING.  If SUFFIX is non-null, remove
-   the trailing SUFFIX.  Finally, output the result string.  */
+/* Perform the basename operation on STRING.  If SUFFIX has a positive
+   length of SUFFIX_LEN bytes, remove the trailing SUFFIX.
+   Finally, output the result string.  */
 
 static void
-perform_basename (char const *string, char const *suffix, bool use_nuls)
+perform_basename (char const *string, char const *suffix, idx_t suffix_len,
+                  bool use_nuls)
 {
   char *name = base_name (string);
   strip_trailing_slashes (name);
@@ -115,8 +117,9 @@ perform_basename (char const *string, char const *suffix, bool use_nuls)
      skipping suffix stripping if base_name returned an absolute path
      or a drive letter (only possible if name is a file-system
      root).  */
-  if (suffix && IS_RELATIVE_FILE_NAME (name) && ! FILE_SYSTEM_PREFIX_LEN (name))
-    remove_suffix (name, suffix);
+  if (0 < suffix_len && IS_RELATIVE_FILE_NAME (name)
+      && ! FILE_SYSTEM_PREFIX_LEN (name))
+    remove_suffix (name, suffix, suffix_len);
 
   fputs (name, stdout);
   putchar (use_nuls ? '\0' : '\n');
@@ -174,21 +177,24 @@ main (int argc, char **argv)
       usage (EXIT_FAILURE);
     }
 
-  if (!multiple_names && optind + 2 < argc)
+  if (! multiple_names)
     {
-      error (0, 0, _("extra operand %s"), quote (argv[optind + 2]));
-      usage (EXIT_FAILURE);
+      if (optind + 2 == argc)
+        suffix = argv[optind + 1];
+      else if (optind + 2 < argc)
+        {
+          error (0, 0, _("extra operand %s"), quote (argv[optind + 2]));
+          usage (EXIT_FAILURE);
+        }
     }
 
-  if (multiple_names)
-    {
-      for (; optind < argc; optind++)
-        perform_basename (argv[optind], suffix, use_nuls);
-    }
-  else
-    perform_basename (argv[optind],
-                      optind + 2 == argc ? argv[optind + 1] : NULL,
-                      use_nuls);
+  idx_t suffix_len = suffix ? strlen (suffix) : 0;
+
+  char **file = argv + optind;
+  int n_files = multiple_names ? argc - optind : 1;
+
+  for (int i = 0; i < n_files; ++i)
+    perform_basename (file[i], suffix, suffix_len, use_nuls);
 
   return EXIT_SUCCESS;
 }