;; This newlisp "module" implements dbus marshalling
;
; The newlisp representation is a simplified form using lists for
-; structs and arrays
+; structs and arrays.
-;;================
+;; (expand-signature S)
; Expland a signature string into a nested list to correspond to the
-; newlisp list representation. Basic dbus types are basic newlisp
-; types, including strings. Arrays and structs are sublists. The
-; signature marks array sublists with an initial "a", otherwise it's a
-; struct sublist.
+; newlisp list representation.
+;; Basic dbus types are basic newlisp types, including strings. Arrays
+;; and structs are sublists; the expanded signature marks array
+;; sublists with an initial "a", otherwise it's a struct sublist.
;
; Ex: "yi" = ("y" "i")
; Ex: "y(ai)" = ("y" (("a" "i")))
; Ex: "a(yi)" = (("a" ("y" "i")))
; Ex: "yyyyuua(yv)" = ("y" "y" "y" "y" "u" "u" ("a" ("y" "v")))
(define (expand-signature S)
- (println (list 'expand-signature S))
+ (setf S (replace "{" (replace "}" (copy S) ")") "("))
(let ((STACK '()) (CUR '()) (A 0))
(dolist (X (explode S))
- ;;(println "CUR=" CUR " X=" X)
(case X
(")" (setf X CUR) (setf CUR (pop STACK)))
("(" (push CUR STACK) (setf CUR '()))
(setf CUR (chop CUR))))
(when (!= "(" X)
(push X CUR -1)))
- (if (null? CUR) '() (1 CUR) CUR (CUR 0)) ))
-
-;; Join arguments with a given byte-alignment
-(define (pad-join N)
- ;;(println (list 'pad-join AT N (args)))
- (let ((OUT ""))
- (dolist (S (args))
- (let ((PAD (dup "\000" (% (- N (% (length OUT) N)) N))))
- (extend OUT PAD S)))
- OUT))
-
-;; Pack data from DATA according to signature. The DATA is a nested
-;; list where container types are sub lists. Variant types also appear
-;; as pairs of signature and value.
-(define (pack-data SIGN DATA)
- (let ((AT 0)) (pack-data-struct (expand-signature SIGN) DATA)))
+ (if (null? CUR) '() CUR)))
-;; Align AT to an I multiple by adding nul bytes, then extend it with
-;; DATA, and increment AT
+;; Align AT to an I multiple and pad DATA with as many NUL bytes at
+;; front, then increment AT past it all.
(define (pack-align DATA (I (length DATA)))
(let ((PAD (dup "\000" (% (- I (% AT I)) I))))
- ;;(println (list 'align AT I (length PAD)))
(setf DATA (extend PAD DATA))
(inc AT (length DATA))
DATA))
-;; Advance AT to align by I
-(define (align-unpack I FMT N)
- ((list (inc AT (% (- I (% AT I)) I)) ; align to I
- (unpack FMT (AT DATA))
- (inc AT N))
- 1 0)) ; return second term's first item
-
+;; Pack data from DATA according to signature. The DATA is a nested
+;; list where container types are sub lists. Variant types also appear
+;; as pairs of signature and value.
+(define (pack-data SIGN DATA)
+ (let ((AT 0)) (pack-data-struct (expand-signature SIGN) DATA)))
;; Pack a newlisp data element according to marshalling type The
;; newlisp data is integer, double, string or list (for container and
(constant
'FMTMAP ; mapping dbus type code to byte size and newlisp code
'( ("y" 1 "b") ; BYTE (unsigned 8-bit integer)
- ("b" 1 "b") ; BOOLEAN (0 is FALSE, 1 is TRUE, else invalid)
+ ("b" 4 "lu") ; BOOLEAN (0 is FALSE, 1 is TRUE, else invalid)
("n" 2 "d") ; INT16 (signed 16-bit integer)
("q" 2 "u") ; UINT16 (unsigned 16-bit integer)
("i" 4 "ld") ; INT32 (signed 32-bit integer)
)
(define (pack-data-item ES DATA)
- ;;(println (list 'pack-data-item AT ES DATA))
(if (list? ES) (pack-data-struct ES DATA)
- (find ES "osg") (pack-data-string ES DATA)
+ (= ES "s") (pack-data-string ES DATA)
+ (= ES "o") (pack-data-string ES DATA)
+ (= ES "g") (pack-data-signature ES DATA)
(= ES "v") (apply pack-data-variant DATA)
(if (lookup ES FMTMAP) (pack-align (pack $it DATA)))))
(define (pack-data-variant ES DATA)
- ;;(println (list 'pack-data-variant AT ES DATA))
(extend (pack-align (pack "bbb" 1 (char ES) 0) 1)
(pack-data-item ES DATA)))
+;; pack types "s" and "o"
(define (pack-data-string ES DATA)
- (pack-align (pack (format "lus%db" (length DATA)) (length DATA) DATA 0) 1))
+ (pack-align (pack (format "lus%db" (length DATA)) (length DATA) DATA 0) 4))
+
+;; pack type "g"
+(define (pack-data-signature ES DATA)
+ (pack-align (pack (format "bs%db" (length DATA)) (length DATA) DATA 0) 1))
;; Pack an array. DATA elements marshalled by repeating ES, preceded
;; by the array length in bytes as aligned UINT32.
(define (pack-data-array ES DATA)
- ;;(println (list 'pack-data-array AT ES DATA))
(let ((PAD (pack-align "" 4))
(X (inc AT 4)) ; start index of array bytes
(DATA (apply extend (map (curry pack-data-item ES) DATA))))
;; Pack a struct. ES and DATA elements marshalled pairwise in order
;; following an initial8-byte alignment.
(define (pack-data-struct ES DATA)
- ;;(println (list 'pack-data-struct AT ES DATA))
- (if (= "a" (ES 0))
- (pack-data-array (ES 1) DATA)
+ (if (= "a" (ES 0)) (pack-data-array (ES 1) DATA)
(apply extend (cons (pack-align "" 8)
(map pack-data-item ES DATA)))))
;;########## unpacking
+;; Advance AT to an I multiple.
(define (align-AT I)
(inc AT (% (- I (% AT I)) I)))
+;; Advance AT to an I multiple and unpack (by newlisp format) at that
+;; position in DATA. Then advance AT further past that unpacking but
+;; return the unpacked value.
(define (unpack-align I FMT)
+ ##(println (list 'unpack-align I FMT AT (length DATA)))
(align-AT I)
(prog1 ((unpack FMT (AT DATA)) 0) (inc AT I)))
-(define (unpack-data-string ES)
- ;;(println (list 'unpack-data-string ES AT (unpack "bbbb" (AT DATA))))
- (let ((N (if (= "g" ES) (unpack-align 1 "b") (unpack-align 4 "lu"))))
- (prog1 (unpack (string "s" N) (AT DATA)) (inc AT (+ 1 N)))))
-
+;; Unpack a string or object path. The format is "lu" (UINT32) with
+;; the string length, then "s%db" with that string length and followed
+;; by a NUL byte.
+(define (unpack-data-string ES (N (unpack-align 4 "lu")))
+ (prog1 ((unpack (string "s" N) (AT DATA)) 0) (inc AT (+ 1 N))))
+
+;; Unpack a signature string. The format is "b" (BYTE) with the string
+;; length, then "s%db" with that string length and followed by a NUL
+;; byte. I.e. the same as unpack-data-string but with the string
+;; length in a BYTE rather than an UINT32.
+(define (unpack-data-signature ES)
+ (unpack-data-string ES (unpack-align 1 "b")))
+
+;; Unpack a variant item. This consists of "bbb" where the middle
+;; character is the type character for the data, preceded by a 1 byte
+;; and followed by a NUL byte. The subsequent data is unpacked
+;; according to that type character.
(define (unpack-data-variant)
- ;;(println (unpack "bbbb" (AT DATA)))
- (let ((ES (char ((unpack "bbb" (AT DATA)) 1))))
- (inc AT 3)
- (unpack-data-item ES)))
+ (unpack-data-item ((expand-signature (unpack-data-signature "g")) 0)))
-;; Unpack the ES item from (AT DATA) and increment AT
+;; Unpack the ES type item from (AT DATA), optionally with
+;; pre-alignment, and increment AT past the padding and item.
(define (unpack-data-item ES)
- ;;(println (list 'unpack-data-item ES AT (unpack "b" (AT DATA))))
- ;;(when (= ES "\000") (println (history true)))
+ ##(println (list 'unpack-data-item ES AT (length DATA)))
(if (list? ES) (unpack-data-struct ES)
- (find ES "+gosg") (unpack-data-string ES)
+ (= ES "s") (unpack-data-string ES)
+ (= ES "o") (unpack-data-string ES)
+ (= ES "g") (unpack-data-signature ES)
(= ES "v") (unpack-data-variant)
(if (assoc ES FMTMAP) (unpack-align ($it -2) ($it -1)))))
;; Unpack array with ES elements. The array begins with an UINT32
-;; field telling how many bytes to unpack
+;; field telling how many bytes to unpack, followed by the array
+;; elements.
(define (unpack-data-array ES)
- ;;(println (list 'unpack-data-array ES))
(let ((A AT) (N (+ (unpack-align 4 "u") AT)) (OUT '()))
+ (when (and (list? ES) (!= "a" (ES 0))) (align-AT 8))
(while (< AT N)
- ;;(println (list 'item AT N))
+ ##(println "---next " (list AT N))
(push (unpack-data-item ES) OUT -1))
OUT))
;; Unpack a structure or array with ES fields.
(define (unpack-data-struct ES)
+ ##(println (list 'unpack-data-struct ES AT))
(if (= "a" (ES 0)) (unpack-data-array (ES 1))
(begin (align-AT 8) (map unpack-data-item ES))))
;; a pair (unpacked pos) of unpacked data and how much data is
;; consumed.
(define (unpack-data SIGN DATA (AT 0))
- ;;(println (list 'unpack-data SIGN "DATA length " (length DATA)))
- ;;(map println (explode (unpack (dup "b" (length (AT DATA))) (AT DATA)) 20))
+ ##(println (format "*** unpack-data %s %d %d" SIGN (length DATA) AT))
(list (unpack-data-item (expand-signature SIGN)) AT))
+;; Unpack all dbus messages in the given DATA block. Each message
+;; consists of head and body. The head has signature "yyyyuua(yv)"
+;; where the array is an alist of key-value pairs, optionally
+;; including the 'SIGNATURE key with the signature for the body; if
+;; omitted, then the body is empty.
+;;
+;; The function returns the header list of key-value pairs optionally
+;; extended with the pair ("" body).
+(define (unpack-messages DATA)
+ (let ((AT 0) (OUT '()) (M nil) (D nil) (S nil))
+ (while (and (< (+ AT 7) (length DATA))
+ (setf M (unpack-data "yyyyuua(yv)" DATA AT)))
+ (setf AT (M 1))
+ ##(println "message head " (M 0))
+ ##(println (list 'remains AT (length DATA)))
+ (setf M (M 0 -1)) ; Drop all but the headers then map to symbol keys
+ (dotimes (i (length M))
+ (setf (M i 0) (MESSAGE-HEADERS (M i 0) 0)))
+ ##(println "mapped headers " M)
+ ;; Add the body, if any, keyed by "".
+ (setf S (if (lookup 'SIGNATURE M) $it ""))
+ ##(println (list 'sign S))
+ (when (and (!= S "") (setf D (unpack-data S DATA AT)))
+ (setf AT (D 1))
+ (extend M (list (list "" (D 0)))))
+ ;; Collate message and move to the next portion in DATA
+ (push M OUT -1)
+ ##(println (list 'ending AT (length DATA)))
+ ;;(align-AT 4)
+ ##(println (list 'aligned AT (length DATA)))
+ (setf DATA (AT DATA))
+ (setf AT 0)
+ )
+ OUT ))
+
+"lsp-dbus-marshal.lsp"