Subject: bug#58669: 28.2; bindat: str and strz not operating on vectors

From: Nacho Barrientos <[email-masked]>
Date: Thu, 20 Oct 2022 14:18:13 +0200

Hi,

These two snippets:

  (let* ((type (bindat-type
                 (data strz)))
         (data [#x4e #x6f #x6e #x65 #x00])
         (unpacked (bindat-unpack type data)))
    (bindat-get-field unpacked 'data))

  (let* ((type (bindat-type
                 (data str 4)))
         (data [#x4e #x6f #x6e #x65])
         (unpacked (bindat-unpack type data)))
    (bindat-get-field unpacked 'data))

Both fail similarly:

Debugger entered--Lisp error: (wrong-type-argument listp [78 111 110 101])
  apply(unibyte-string [78 111 110 101])
  (if (stringp s) s (apply #'unibyte-string s))
  (let ((i 0) s) (while (and (if len (< i len) t) (/= (aref bindat-raw (+ bindat-idx i)) 0)) (setq i (1+ i))) (setq s (substring bindat-raw bindat-idx (+ bindat-idx i))) (setq bindat-idx (+ bindat-idx (or len (1+ i)))) (if (stringp s) s (apply #'unibyte-string s)))
  bindat--unpack-strz(nil)
  [...]

Debugger entered--Lisp error: (wrong-type-argument listp [78 111 110 101])
  apply(unibyte-string [78 111 110 101])
  (if (stringp s) s (apply #'unibyte-string s))
  (let ((s (substring bindat-raw bindat-idx (+ bindat-idx len)))) (setq bindat-idx (+ bindat-idx len)) (if (stringp s) s (apply #'unibyte-string s)))
  bindat--unpack-str(4)
  [...]

With the attached patch both return the string "None" as expected (well,
as I'd expect, *grins*). The diff also adds some extra tests.

I'm running 28.2 but I've declared both functions as they're in the
current master and the patch is on top of the current master, too.

It's the first time that I look into this package so I'm not sure this
is the way it's meant to work but similar operations work fine on
vectors of bytes, for instance:

  (let* ((type (bindat-type
                 (first uint 16)
                 (second uint 16)))
         (data [#x00 #xff #xff #x00])
         (unpacked (bindat-unpack type data)))
    (list (bindat-get-field unpacked 'first)
          (bindat-get-field unpacked 'second)
          (bindat-length type unpacked)))

(which returns (255 65280 4) as expected)

Please simply ignore this if I'm talking rubbish :)

Hope this helps.

>From a8831aadf57f39862560b37d507be2a3bcb99aa2 Mon Sep 17 00:00:00 2001
From: Nacho Barrientos <[email-masked]>
Date: Thu, 20 Oct 2022 14:16:43 +0200
Subject: [PATCH] bindat (src, strz): operate on vectors too

---
 lisp/emacs-lisp/bindat.el            |  4 ++--
 test/lisp/emacs-lisp/bindat-tests.el | 19 ++++++++++++++++++-
 2 files changed, 20 insertions(+), 3 deletions(-)

diff --git a/lisp/emacs-lisp/bindat.el b/lisp/emacs-lisp/bindat.el
index 0ecac3d52a..83188b369e 100644
--- a/lisp/emacs-lisp/bindat.el
+++ b/lisp/emacs-lisp/bindat.el
@@ -163,7 +163,7 @@ bindat--unpack-str
   (let ((s (substring bindat-raw bindat-idx (+ bindat-idx len))))
     (setq bindat-idx (+ bindat-idx len))
     (if (stringp s) s
-      (apply #'unibyte-string s))))
+      (apply #'unibyte-string (append s nil)))))
 
 (defun bindat--unpack-strz (&optional len)
   (let ((i 0) s)
@@ -172,7 +172,7 @@ bindat--unpack-strz
     (setq s (substring bindat-raw bindat-idx (+ bindat-idx i)))
     (setq bindat-idx (+ bindat-idx (or len (1+ i))))
     (if (stringp s) s
-      (apply #'unibyte-string s))))
+      (apply #'unibyte-string (append s nil)))))
 
 (defun bindat--unpack-bits (len)
   (let ((bits nil) (bnum (1- (* 8 len))) j m)
diff --git a/test/lisp/emacs-lisp/bindat-tests.el b/test/lisp/emacs-lisp/bindat-tests.el
index 0c03c51e2e..2abf714852 100644
--- a/test/lisp/emacs-lisp/bindat-tests.el
+++ b/test/lisp/emacs-lisp/bindat-tests.el
@@ -252,7 +252,24 @@ bindat-test--str-strz-multibyte
     (should (equal (bindat-unpack spec "abc\0") "abc"))
     ;; Missing null terminator.
     (should-error (bindat-unpack spec ""))
-    (should-error (bindat-unpack spec "a"))))
+    (should-error (bindat-unpack spec "a")))
+
+  (ert-deftest bindat-test--strz-array-unpack ()
+    (should (equal (bindat-unpack spec [#x61 #x62 #x63 #x00]) "abc"))))
+
+(let ((spec (bindat-type str 3)))
+  (ert-deftest bindat-test--str-simple-array-unpack ()
+    (should (equal (bindat-unpack spec [#x61 #x62 #x63]) "abc"))))
+
+(let ((spec (bindat-type
+              (first u8)
+              (string str 3)
+              (last uint 16))))
+  (ert-deftest bindat-test--str-combined-array-unpack ()
+    (let ((unpacked (bindat-unpack spec [#xff #x63 #x62 #x61 #xff #xff])))
+      (should (equal (bindat-get-field unpacked 'string) "cba"))
+      (should (equal (bindat-get-field unpacked 'first) (- (expt 2 8) 1)))
+      (should (equal (bindat-get-field unpacked 'last) (- (expt 2 16) 1))))))
 
 (let ((spec '((x strz 2))))
   (ert-deftest bindat-test--strz-legacy-fixedlen-len ()
-- 
2.38.0

In GNU Emacs 28.2 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.34, cairo version 1.17.6)
 of 2022-09-12 built on frederik
Windowing system distributor 'The X.Org Foundation', version 11.0.12101004
System Description: Arch Linux

Configured using:
 'configure --with-x-toolkit=gtk3 --with-native-compilation
 --sysconfdir=/etc --prefix=/usr --libexecdir=/usr/lib
 --localstatedir=/var --with-cairo --with-harfbuzz --with-libsystemd
 --with-modules 'CFLAGS=-march=x86-64 -mtune=generic -O2 -pipe -fno-plt
 -fexceptions -Wp,-D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security
 -fstack-clash-protection -fcf-protection -g
 -ffile-prefix-map=/build/emacs/src=/usr/src/debug -flto=auto'
 'LDFLAGS=-Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now -flto=auto''

Configured features:
ACL CAIRO DBUS FREETYPE GIF GLIB GMP GNUTLS GPM GSETTINGS HARFBUZZ JPEG
JSON LCMS2 LIBOTF LIBSYSTEMD LIBXML2 M17N_FLT MODULES NATIVE_COMP NOTIFY
INOTIFY PDUMPER PNG RSVG SECCOMP SOUND THREADS TIFF TOOLKIT_SCROLL_BARS
X11 XDBE XIM XPM GTK3 ZLIB

Important settings:
  value of $LANG: en_US.UTF-8
  locale-coding-system: utf-8-unix

Major mode: ELisp/l

Minor modes in effect:
  erc-list-mode: t
  erc-menu-mode: t
  erc-ring-mode: t
  erc-pcomplete-mode: t
  erc-netsplit-mode: t
  global-undo-tree-mode: t
  undo-tree-mode: t
  bug-reference-prog-mode: t
  recentf-mode: t
  goto-address-prog-mode: t
  erc-services-mode: t
  erc-autojoin-mode: t
  erc-networks-mode: t
  erc-track-mode: t
  erc-track-minor-mode: t
  erc-match-mode: t
  erc-spelling-mode: t
  erc-hl-nicks-mode: t
  erc-button-mode: t
  erc-fill-mode: t
  erc-stamp-mode: t
  erc-irccontrols-mode: t
  erc-noncommands-mode: t
  erc-move-to-prompt-mode: t
  erc-readonly-mode: t
  flyspell-mode: t
  display-time-mode: t
  engine-mode: t
  mu4e-column-faces-mode: t
  beginend-global-mode: t
  beginend-prog-mode: t
  outline-minor-mode: t
  global-git-commit-mode: t
  magit-auto-revert-mode: t
  auto-revert-mode: t
  editorconfig-mode: t
  hexl-follow-ascii: t
  csv-field-index-mode: t
  doom-modeline-mode: t
  yas-global-mode: t
  yas-minor-mode: t
  all-the-icons-ivy-rich-mode: t
  ivy-rich-project-root-cache-mode: t
  ivy-rich-mode: t
  ivy-posframe-mode: t
  ivy-mode: t
  shell-dirtrack-mode: t
  whole-line-or-region-global-mode: t
  whole-line-or-region-local-mode: t
  delete-selection-mode: t
  global-whitespace-mode: t
  override-global-mode: t
  global-eldoc-mode: t
  eldoc-mode: t
  show-paren-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  blink-cursor-mode: t
  window-divider-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t
  line-number-mode: t
  auto-fill-function: yas--auto-fill
  transient-mark-mode: t
  hs-minor-mode: t

Load-path shadows:
/home/nacho/.emacs.d/elpa/transient-20220918.2101/transient hides /usr/share/emacs/28.2/lisp/transient

Features:
(shadow emacsbug tramp-archive tramp-gvfs zeroconf ox-org calc-misc
calculator locate dired-subtree dired-hacks-utils view gnus-icalendar
icalendar pcmpl-unix erc-list erc-menu erc-ring erc-pcomplete
erc-netsplit puppet-mode align org-archive dictionary
dictionary-connection inf-ruby calc-alg calc-ext misearch multi-isearch
two-column goto-chg undo-tree queue expand-region yaml-mode-expansions
subword-mode-expansions text-mode-expansions cc-mode-expansions
the-org-mode-expansions ruby-mode-expansions python-mode-expansions
js-mode-expansions web-mode-expansions er-basic-expansions
expand-region-core expand-region-custom cl-print display-line-numbers
magit-patch magit-subtree magit-gitignore magit-ediff ediff ediff-merg
ediff-mult ediff-wind ediff-diff ediff-help ediff-init ediff-util
forge-list forge-commands forge-semi forge-bitbucket buck forge-gogs
gogs forge-gitea gtea forge-gitlab glab forge-github ghub-graphql treepy
gsexp ghub forge-notify forge-revnote forge-pullreq forge-issue
forge-topic yaml forge-post markdown-mode forge-repo forge forge-core
forge-db closql emacsql-sqlite emacsql emacsql-compiler dired-aux
mailalias descr-text projectile rg pcase rg-info-hack rg-menu rg-ibuffer
rg-result wgrep-rg wgrep rg-history rg-header ibuf-ext ibuffer
ibuffer-loaddefs grep cus-edit cus-start calc-menu calc calc-loaddefs
rect calc-macs crux gnutls network-stream url-http url-gw nsm url-auth
url-cache secrets qp man nroff-mode shr-color smiley gnus-cite
mm-archive mail-extr bug-reference avy magit-extras org-duration cal-iso
tabify mwim elfeed-link timezone cal-persia cal-french cal-move
tramp-cache recentf tree-widget info-colors counsel swiper em-unix
em-term term ehelp em-script em-prompt em-hist em-pred em-glob em-cmpl
em-basic em-banner em-alias goto-addr erc-services erc-join erc-networks
erc-track erc-match erc-spelling erc-hl-nicks erc-button erc-fill
erc-stamp erc-goodies erc erc-backend erc-loaddefs cern-ldap cap-words
superword subword ldap net-utils quail flyspell ispell ol-eww ol-rmail
ol-mhe ol-irc ol-info ol-gnus nnselect gnus-search eieio-opt speedbar
ezimage dframe ol-docview doc-view jka-compr image-mode exif ol-bibtex
ol-bbdb ol-w3m ol-doi org-link-doi appt diary-lib diary-loaddefs
notifications ox-gfm ox-md ox-odt rng-loc rng-uri rng-parse rng-match
rng-dt rng-util rng-pttrn nxml-parse nxml-ns nxml-enc xmltok nxml-util
ox-latex ox-icalendar org-agenda ox-html table ox-ascii ox-publish ox
org-element org-tree-slide org-timer org-clock org-capture org-refile
org-protocol ielm bluetooth desktop-environment time exwm-systemtray
xcb-systemtray xcb-xembed exwm exwm-input xcb-keysyms xcb-xkb
exwm-manage exwm-floating xcb-cursor xcb-render exwm-layout
exwm-workspace exwm-core xcb-ewmh xcb-icccm xcb xcb-xproto xcb-types
xcb-debug engine-mode eww xdg mm-url pipewire pipewire-lib pipewire-cli
pipewire-access eradio elfeed-show elfeed-search vc-mtn vc-hg vc-git
vc-bzr vc-src vc-sccs vc-svn vc-cvs vc-rcs vc vc-dispatcher
editorconfig-core editorconfig-core-handle editorconfig-fnmatch
elfeed-csv elfeed elfeed-curl elfeed-log elfeed-db elfeed-lib avl-tree
url-queue xml-query mu4e-column-faces inline mu4e mu4e-org mu4e-main
mu4e-view gnus-art mm-uu mml2015 mm-view mml-smime smime dig gnus-sum
gnus-group gnus-undo gnus-start gnus-dbus dbus gnus-cloud nnimap nnmail
mail-source utf7 netrc nnoo gnus-spec gnus-int gnus-range gnus-win gnus
nnheader wid-edit mu4e-headers mu4e-compose mu4e-draft mu4e-actions
smtpmail sendmail mu4e-search mu4e-lists mu4e-bookmarks mu4e-mark
mu4e-message shr kinsoku svg xml flow-fill mule-util hl-line
mu4e-contacts mu4e-update mu4e-folders mu4e-server mu4e-context
mu4e-vars mu4e-helpers mu4e-config ido helpful cc-langs trace edebug
help-fns radix-tree elisp-refs beginend git-modes gitignore-mode
gitconfig-mode gitattributes-mode git-link orgit org ob ob-tangle ob-ref
ob-lob ob-table ob-exp org-macro org-footnote org-src ob-comint
org-pcomplete org-list org-faces org-entities outline-minor-faces
noutline outline org-version ob-emacs-lisp ob-core ob-eval org-table
oc-basic bibtex ol org-keys oc org-compat org-macs cal-menu calendar
cal-loaddefs magit-bookmark magit-submodule magit-obsolete magit-blame
magit-stash magit-reflog magit-bisect magit-push magit-pull magit-fetch
magit-clone magit-remote magit-commit magit-sequence magit-notes
magit-worktree magit-tag magit-merge magit-branch magit-reset
magit-files magit-refs magit-status magit magit-repos magit-apply
magit-wip magit-log magit-diff smerge-mode diff diff-mode git-commit
log-edit message rmc puny rfc822 mml mml-sec epa derived epg rfc6068
epg-config gnus-util rmail rmail-loaddefs mm-decode mm-bodies mm-encode
mailabbrev gmm-utils pcvs-util add-log magit-core magit-autorevert
magit-margin magit-transient magit-process with-editor magit-mode
transient magit-git magit-base magit-section crm compat-27 compat-26
eshell-prompt-extras em-dirs esh-var esh-mode em-ls eshell-bookmark
bookmark eshell powerthesaurus request mailheader autorevert filenotify
mail-utils dom virtualenvwrapper gitignore-templates editorconfig
nhexl-mode hexl sqlformat reformatter jq-mode csv-mode sort web-mode
disp-table sh-script executable systemd conf-mode archive-rpm bindat
archive-cpio arc-mode archive-mode rpm-spec-mode yaml-mode json-mode
json-snatcher js cc-mode cc-fonts cc-guess cc-menus cc-styles cc-align
go-dlv gud go-mode find-file ffap etags fileloop generator xref
rspec-mode python-mode info-look advice org-loaddefs which-func hideshow
hippie-exp flymake-proc flymake project thingatpt ert pp ewoc debug
backtrace compile cc-cmds cc-engine cc-vars cc-defs ruby-mode smie
flycheck-package package-lint let-alist imenu finder lisp-mnt mail-parse
rfc2231 rfc2047 rfc2045 mm-util ietf-drums mail-prsvr flycheck find-func
rainbow-mode xterm-color doom-modeline doom-modeline-segments
doom-modeline-env doom-modeline-core shrink-path f f-shortdoc shortdoc
text-property-search dash compat compat-macs doom-themes-ext-visual-bell
face-remap doom-tokyo-night-theme doom-themes doom-themes-base
yasnippet-snippets yasnippet amx comp comp-cstr warnings s
all-the-icons-ivy-rich ivy-rich ivy-posframe posframe all-the-icons-ivy
ivy ivy-faces ivy-overlay colir color all-the-icons all-the-icons-faces
data-material data-weathericons data-octicons data-fileicons
data-faicons data-alltheicons disk-usage dired dired-loaddefs pinentry
tramp-cmds em-tramp esh-cmd esh-ext esh-opt esh-module esh-groups
esh-proc esh-io esh-arg esh-util tramp-sh tramp tramp-loaddefs trampver
tramp-integration files-x tramp-compat shell pcomplete comint ansi-color
ring parse-time iso8601 time-date ls-lisp format-spec
whole-line-or-region delsel cus-load whitespace cl-extra help-mode
use-package use-package-ensure use-package-delight use-package-diminish
use-package-bind-key bind-key easy-mmode use-package-core finder-inf
edmacro kmacro avoid server rx info package browse-url url url-proxy
url-privacy url-expand url-methods url-history url-cookie url-domsuf
url-util mailcap url-handlers url-parse auth-source cl-seq eieio
eieio-core cl-macs eieio-loaddefs password-cache json subr-x map
url-vars seq byte-opt gv bytecomp byte-compile cconv cl-loaddefs cl-lib
iso-transl tooltip eldoc paren electric uniquify ediff-hook vc-hooks
lisp-float-type elisp-mode mwheel term/x-win x-win term/common-win x-dnd
tool-bar dnd fontset image regexp-opt fringe tabulated-list replace
newcomment text-mode lisp-mode prog-mode register page tab-bar menu-bar
rfn-eshadow isearch easymenu timer select scroll-bar mouse jit-lock
font-lock syntax font-core term/tty-colors frame minibuffer cl-generic
cham georgian utf-8-lang misc-lang vietnamese tibetan thai tai-viet lao
korean japanese eucjp-ms cp51932 hebrew greek romanian slovak czech
european ethiopic indian cyrillic chinese composite emoji-zwj charscript
charprop case-table epa-hook jka-cmpr-hook help simple abbrev obarray
cl-preloaded nadvice button loaddefs faces cus-face macroexp files
window text-properties overlay sha1 md5 base64 format env code-pages
mule custom widget hashtable-print-readable backquote threads dbusbind
inotify lcms2 dynamic-setting system-font-setting font-render-setting
cairo move-toolbar gtk x-toolkit x multi-tty make-network-process
native-compile emacs)

Memory information:
((conses 16 2785060 340167)
 (symbols 48 120088 102)
 (strings 32 626461 40628)
 (string-bytes 1 22822154)
 (vectors 16 228385)
 (vector-slots 8 5214189 734689)
 (floats 8 6603 3963)
 (intervals 56 145534 13846)
 (buffers 992 195))

--
 bye
 Nacho
 http://cern.ch/nacho