Subject: 28.0.50: Hash table printing oddities

From: Pip Cet <[email-masked]>
Date: Fri, 3 Jan 2020 12:13:28 +0000

The hash table printing code has some oddities: it will print extra
spaces sometimes, won't print as many elements as requested under all
circumstances, and behave oddly for empty hash tables when
print-length is 0.

Currently, this code will result in extra spaces in the hash table output:

(let ((h (make-hash-table)))
  (puthash 1 2 h)
  (puthash 2 3 h)
  (remhash 1 h)
  (format "%S" h))

(let ((h (make-hash-table)))
  (let ((print-length 0))
    (format "%S" h)))

In the latter case, the output actually includes "data ( ...)", though
"data ()" would be shorter and more accurate.

Also, the current code contains an oddity that would make it print
fewer than print-length hash cells in the data list:

(let ((h (make-hash-table)))
  (dotimes (i 100)
    (puthash i i h))
  (dotimes (i 99)
    (remhash i h))
  (let ((print-length 1))
    (format "%S" h)))

will produce "data ( ...)", when it should produce "data (99 99)".

Proposed patch attached.
From 29feb0fa194fb1ea9fe651428b6fbe01d218aa75 Mon Sep 17 00:00:00 2001
From: Pip Cet <[email-masked]>
Date: Fri, 3 Jan 2020 12:00:44 +0000
Subject: [PATCH] Fix hash table printing

Previously, the output would sometimes contain extra spaces or fewer
elements than were requested.
* src/print.c (print_vectorlike): Keep track of the actual number of
elements printed rather than attempting to use hash bucket indices.
---
 src/print.c | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/src/print.c b/src/print.c
index 425b0dc4ee..14b5f19d6e 100644
--- a/src/print.c
+++ b/src/print.c
@@ -1578,27 +1578,34 @@ print_vectorlike (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag,
 
 	/* Print the data here as a plist. */
 	ptrdiff_t real_size = HASH_TABLE_SIZE (h);
-	ptrdiff_t size = real_size;
+	ptrdiff_t size = h->count;
 
 	/* Don't print more elements than the specified maximum.  */
 	if (FIXNATP (Vprint_length) && XFIXNAT (Vprint_length) < size)
 	  size = XFIXNAT (Vprint_length);
 
 	printchar ('(', printcharfun);
-	for (ptrdiff_t i = 0; i < size; i++)
+	ptrdiff_t j = 0;
+	for (ptrdiff_t i = 0; i < real_size; i++)
           {
             Lisp_Object key = HASH_KEY (h, i);
 	    if (!EQ (key, Qunbound))
 	      {
-	        if (i) printchar (' ', printcharfun);
+	        if (j++) printchar (' ', printcharfun);
 	        print_object (key, printcharfun, escapeflag);
 	        printchar (' ', printcharfun);
 	        print_object (HASH_VALUE (h, i), printcharfun, escapeflag);
+		if (j == size)
+		  break;
 	      }
           }
 
-	if (size < real_size)
-	  print_c_string (" ...", printcharfun);
+	if (j < h->count)
+	  {
+	    if (j)
+	      printchar (' ', printcharfun);
+	    print_c_string ("...", printcharfun);
+	  }
 
 	print_c_string ("))", printcharfun);
       }
-- 
2.24.0