in variables whose names end in @samp{-bindat-spec}; that kind of name
is automatically recognized as risky.
+@defmac bindat-spec &rest specs
+Creates a Bindat spec object according to the data layout
+specification @var{specs}.
+@end defmac
+
@cindex endianness
@cindex big endian
@cindex little endian
@itemx dword
@itemx long
Unsigned integer in network byte order, with length 4.
-Note: These values may be limited by Emacs's integer implementation limits.
@item u16r
@itemx u24r
@node Bindat Functions
@subsection Functions to Unpack and Pack Bytes
- In the following documentation, @var{spec} refers to a data layout
-specification, @code{bindat-raw} to a byte array, and @var{struct} to an
-alist representing unpacked field data.
+ In the following documentation, @var{spec} refers to a Bindat spec
+object as returned from @code{bindat-spec}, @code{raw} to a byte
+array, and @var{struct} to an alist representing unpacked field data.
-@defun bindat-unpack spec bindat-raw &optional bindat-idx
+@defun bindat-unpack spec raw &optional idx
@c FIXME? Again, no multibyte?
This function unpacks data from the unibyte string or byte
-array @code{bindat-raw}
+array var{raw}
according to @var{spec}. Normally, this starts unpacking at the
-beginning of the byte array, but if @var{bindat-idx} is non-@code{nil}, it
+beginning of the byte array, but if @var{idx} is non-@code{nil}, it
specifies a zero-based starting position to use instead.
The value is an alist or nested alist in which each element describes
according to @var{spec}.
@end defun
-@defun bindat-pack spec struct &optional bindat-raw bindat-idx
+@defun bindat-pack spec struct &optional raw idx
This function returns a byte array packed according to @var{spec} from
the data in the alist @var{struct}. It normally creates and fills a
-new byte array starting at the beginning. However, if @var{bindat-raw}
+new byte array starting at the beginning. However, if @var{raw}
is non-@code{nil}, it specifies a pre-allocated unibyte string or vector to
-pack into. If @var{bindat-idx} is non-@code{nil}, it specifies the starting
-offset for packing into @code{bindat-raw}.
+pack into. If @var{idx} is non-@code{nil}, it specifies the starting
+offset for packing into var{raw}.
-When pre-allocating, you should make sure @code{(length @var{bindat-raw})}
+When pre-allocating, you should make sure @code{(length @var{raw})}
meets or exceeds the total length to avoid an out-of-range error.
@end defun
It used to be enabled when Emacs is started in GUI mode but not when started
in text mode. The cursor still only actually blinks in GUI frames.
++++
+** Bindat has a new 'bindat-spec' macro to define specs, with Edebug support
** pcase
+++
;; The corresponding Lisp bindat specification looks like this:
;;
;; (setq header-bindat-spec
-;; '((dest-ip ip)
+;; (bindat-spec
+;; (dest-ip ip)
;; (src-ip ip)
;; (dest-port u16)
;; (src-port u16)))
;;
;; (setq data-bindat-spec
-;; '((type u8)
+;; (bindat-spec
+;; (type u8)
;; (opcode u8)
;; (length u16r) ;; little endian order
;; (id strz 8)
;; (align 4)))
;;
;; (setq packet-bindat-spec
-;; '((header struct header-bindat-spec)
+;; (bindat-spec
+;; (header struct header-bindat-spec)
;; (items u8)
;; (fill 3)
;; (item repeat (items)
;; is interpreted by evalling TAG_VAL and then comparing that to
;; each TAG using equal; if a match is found, the corresponding SPEC
;; is used.
-;; If TAG is a form (eval EXPR), EXPR is evalled with `tag' bound to the
+;; If TAG is a form (eval EXPR), EXPR is eval'ed with `tag' bound to the
;; value of TAG_VAL; the corresponding SPEC is used if the result is non-nil.
;; Finally, if TAG is t, the corresponding SPEC is used unconditionally.
;;
(setq field (cdr field)))
struct)
-
-;; Calculate bindat-raw length of structured data
+;;;; Calculate bindat-raw length of structured data
(defvar bindat--fixed-length-alist
'((u8 . 1) (byte . 1)
(setq bindat-idx (+ bindat-idx len))))))))
(defun bindat-length (spec struct)
- "Calculate bindat-raw length for STRUCT according to bindat SPEC."
+ "Calculate `bindat-raw' length for STRUCT according to bindat SPEC."
(let ((bindat-idx 0))
(bindat--length-group struct spec)
bindat-idx))
-;; Pack structured data into bindat-raw
+;;;; Pack structured data into bindat-raw
(defun bindat--pack-u8 (v)
(aset bindat-raw bindat-idx (logand v 255))
(bindat--pack-group struct spec)
(if raw nil bindat-raw)))
+;;;; Debugging support
+
+(def-edebug-elem-spec 'bindat-spec '(&rest bindat-item))
+
+(def-edebug-elem-spec 'bindat-item
+ '(([&optional bindat-field]
+ &or ["eval" form]
+ ["fill" bindat-len]
+ ["align" bindat-len]
+ ["struct" form] ;A reference to another bindat-spec.
+ ["union" bindat-tag-val &rest (bindat-tag bindat-spec)]
+ ["repeat" integerp bindat-spec]
+ bindat-type)))
+
+(def-edebug-elem-spec 'bindat-type
+ '(&or ("eval" form)
+ ["str" bindat-len]
+ ["strz" bindat-len]
+ ["vec" bindat-len &optional bindat-type]
+ ["bits" bindat-len]
+ symbolp))
+
+(def-edebug-elem-spec 'bindat-field
+ '(&or ("eval" form) symbolp))
+
+(def-edebug-elem-spec 'bindat-len '(&or [] "nil" bindat-arg))
+
+(def-edebug-elem-spec 'bindat-tag-val '(bindat-arg))
+
+(def-edebug-elem-spec 'bindat-tag '(&or ("eval" form) atom))
+
+(def-edebug-elem-spec 'bindat-arg
+ '(&or ("eval" form) integerp (&rest symbolp integerp)))
+
+(defmacro bindat-spec (&rest fields)
+ "Build the bindat spec described by FIELDS."
+ (declare (indent 0) (debug (bindat-spec)))
+ ;; FIXME: We should really "compile" this to a triplet of functions!
+ `',fields)
-;; Misc. format conversions
+;;;; Misc. format conversions
(defun bindat-format-vector (vect fmt sep &optional len)
"Format vector VECT using element format FMT and separator SEP.
(require 'cl-lib)
(defvar header-bindat-spec
- '((dest-ip ip)
+ (bindat-spec
+ (dest-ip ip)
(src-ip ip)
(dest-port u16)
(src-port u16)))
(defvar data-bindat-spec
- '((type u8)
+ (bindat-spec
+ (type u8)
(opcode u8)
(length u16r) ;; little endian order
(id strz 8)
(align 4)))
(defvar packet-bindat-spec
- '((header struct header-bindat-spec)
+ (bindat-spec
+ (header struct header-bindat-spec)
(items u8)
(fill 3)
(item repeat (items)
(eval '(setf (edebug-test-code-use-gv-expander (cons 'a 'b)) 3) t))
"(func"))))
+(defun edebug-tests--read (form spec)
+ (with-temp-buffer
+ (print form (current-buffer))
+ (goto-char (point-min))
+ (cl-letf ((edebug-all-forms t)
+ ((get (car form) 'edebug-form-spec) spec))
+ (edebug--read nil (current-buffer)))))
+
+(ert-deftest edebug-tests--&rest-behavior ()
+ ;; `&rest' is documented to allow the last "repetition" to be aborted early.
+ (should (edebug-tests--read '(dummy x 1 y 2 z)
+ '(&rest symbolp integerp)))
+ ;; `&rest' should notice here that the "symbolp integerp" sequence
+ ;; is not respected.
+ (should-error (edebug-tests--read '(dummy x 1 2 y)
+ '(&rest symbolp integerp))))
+
(ert-deftest edebug-tests-cl-flet ()
"Check that Edebug can instrument `cl-flet' forms without name
clashes (Bug#41853)."