changeset 23353:95744d6d7d3b

use hex values for numeric containers.Map keys (bug #49559) * +containers/Map.m (encode_keys): Use typecast to convert numeric keys to 32- or 64-bit unsigned integers, then convert to hex. (decode_keys): Use hex2num and then typecast to recover original key value. New test to ensure exact key encoding/decoding.
author John W. Eaton <jwe@octave.org>
date Wed, 05 Apr 2017 17:07:58 -0400
parents 778fdffc09df
children 1a2941fb8ffd
files scripts/+containers/Map.m
diffstat 1 files changed, 61 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/+containers/Map.m	Wed Apr 05 15:07:27 2017 -0400
+++ b/scripts/+containers/Map.m	Wed Apr 05 17:07:58 2017 -0400
@@ -467,17 +467,26 @@
         return;
       endif
       cell_input = iscell (keys);
-      if (! cell_input)
-        keys = { keys };
-      endif
-      if (! all (cellfun ("isclass", keys, this.KeyType)))
-        ## Convert input set to KeyType.  This is rarely necessary.
-        keys = cellfun (@(x) feval (this.KeyType, x), keys,
-                        "UniformOutput", false);
+      if (cell_input)
+        if (! all (cellfun ("isclass", keys, this.KeyType)))
+          ## Convert input set to KeyType.  This is rarely necessary.
+          keys = cellfun (@(x) feval (this.KeyType, x), keys,
+                          "UniformOutput", true);
+        else
+          keys = cell2mat (keys);
+        endif
       endif
       ## FIXME: Replace with csprintf when it becomes available.
-      keys = ostrsplit (sprintf ("%.16g\n", keys{:}), "\n");
-      keys(end) = [];
+      ## Use explicit width in format to ensure that we print all digits
+      ## even when there are leading zeros.
+      if (any (strcmp (this.KeyType, {"single", "int32", "uint32"})))
+        keytype = "uint32";
+        fmt = "%0.8X|";
+      else
+        keytype = "uint64";
+        fmt = "%0.16X|";
+      endif
+      keys = ostrsplit (sprintf (fmt, typecast (keys, keytype)), "|", true);
       if (! cell_input)
         keys = char (keys);
       endif
@@ -486,9 +495,18 @@
 
     function keys = decode_keys (this, keys)
       if (this.numeric_keys)
-        keys = str2double (char (keys));
-        if (! strcmp (this.KeyType, "double"))
-          keys = feval (this.KeyType, keys);
+        ## Since we typecast the key to uint32 or uint64 before
+        ## converting to hex, it would probably be better if hex2num
+        ## could return uint32 or uint64 directly, then we could
+        ## typecast back to other types.
+        if (any (strcmp (this.KeyType, {"single", "int32", "uint32"})))
+          keytype = "single";
+        else
+          keytype = "double";
+        endif
+        keys = hex2num (keys, keytype);
+        if (! strcmp (this.KeyType, keytype))
+          keys = typecast (keys, this.KeyType);
         endif
         keys = mat2cell (keys, ones (numel (keys), 1), 1);
       endif
@@ -621,6 +639,37 @@
 %! m.remove({"b","c"});
 %! assert (isempty (m));
 
+## Ensure that exact key values are preserved.
+%!test
+%! keytypes = {"int32", "int64", "uint32", "uint64"};
+%! for i = 1:numel (keytypes)
+%!   keytype = keytypes{i};
+%!   key = intmax (keytype);
+%!   m = containers.Map (key, pi);
+%!   assert (m.isKey (key));
+%!   assert (m.keys (), {key});
+%!   key = intmin (keytype);
+%!   m = containers.Map (key, pi);
+%!   assert (m.isKey (key));
+%!   assert (m.keys (), {key});
+%! endfor
+%! keytypes = {"double", "single"};
+%! for i = 1:numel (keytypes)
+%!   keytype = keytypes{i};
+%!   key = realmax (keytype);
+%!   m = containers.Map (key, pi);
+%!   assert (m.isKey (key));
+%!   assert (m.keys (), {key});
+%!   key = realmin (keytype);
+%!   m = containers.Map (key, pi);
+%!   assert (m.isKey (key));
+%!   assert (m.keys (), {key});
+%!   key = -realmax (keytype);
+%!   m = containers.Map (key, pi);
+%!   assert (m.isKey (key));
+%!   assert (m.keys (), {key});
+%! endfor
+
 ## Test input validation
 %!error containers.Map (1,2,3)
 %!error containers.Map (1,2,3,4,5)