From 5a1eaaebaddb73991426fad5b732d6722bed1116 Mon Sep 17 00:00:00 2001 From: Javier Olaechea Date: Sun, 24 Mar 2019 16:00:00 -0500 Subject: [PATCH 1/4] Refactor int Encoders Instead of Shifting bits and masking in order to extract a subset of the bits Lisp provides the function LBD, which allows us to extract a number of bits from an integer. This approach is more direct, communicates the intent better and allows the compiler to do less work. --- osc.lisp | 46 ++++++++++++++++------------------------------ 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/osc.lisp b/osc.lisp index cdce438..9749422 100644 --- a/osc.lisp +++ b/osc.lisp @@ -309,37 +309,23 @@ (elt s 3)))) i)) -(defun encode-int32 (i) - "convert an integer into a sequence of 4 bytes in network byte order." - (declare (type integer i)) - (let ((buf (make-sequence - '(vector (unsigned-byte 8)) 4))) - (macrolet ((set-byte (n) - `(setf (elt buf ,n) - (logand #xff (ash i ,(* 8 (- n 3))))))) - (set-byte 0) - (set-byte 1) - (set-byte 2) - (set-byte 3)) - buf)) +(defmacro defint-encoder (num-of-octets &optional docstring) + (let ((enc-name (intern (format nil "~:@(encode-int~)~D" (* 8 num-of-octets)))) + (buf (gensym)) + (int (gensym))) + `(defun ,enc-name (,int) + ,@(when docstring + (list docstring)) + (let ((,buf (make-array ,num-of-octets :element-type '(unsigned-byte 8)))) + ,@(loop + for n below num-of-octets + collect `(setf (aref ,buf ,n) + (ldb (byte 8 (* 8 (- (1- ,num-of-octets) ,n))) + ,int))) + ,buf)))) -(defun encode-int64 (i) - "convert an integer into a sequence of 8 bytes in network byte order." - (declare (type integer i)) - (let ((buf (make-sequence - '(vector (unsigned-byte 8)) 8))) - (macrolet ((set-byte (n) - `(setf (elt buf ,n) - (logand #xff (ash i ,(* 8 (- n 7))))))) - (set-byte 0) - (set-byte 1) - (set-byte 2) - (set-byte 3) - (set-byte 4) - (set-byte 5) - (set-byte 6) - (set-byte 7)) - buf)) +(defint-encoder 4 "Convert an integer into a sequence of 4 bytes in network byte order.") +(defint-encoder 8 "Convert an integer into a sequence of 8 bytes in network byte order.") (defun decode-uint64 (s) "8 byte -> 64 bit unsigned int" From d130e45f58498e7a12f5e942ba3ab3fd06dd007d Mon Sep 17 00:00:00 2001 From: Javier Olaechea Date: Sun, 24 Mar 2019 16:14:43 -0500 Subject: [PATCH 2/4] Reuse uint decoders when possible The int decoder does the same work as its corresponding uint decoder before encoding the integer in Two's complement. We can re-use the uint decoder instead of writing the same code in the corresponding int decoder. --- osc.lisp | 50 ++++++++++++++++++++------------------------------ 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/osc.lisp b/osc.lisp index 9749422..59da4e7 100644 --- a/osc.lisp +++ b/osc.lisp @@ -291,16 +291,6 @@ (ldb (byte 16 0) (decode-int32 s))) #-(or sbcl cmucl openmcl allegro) (error "cant decode floats using this implementation")) -(defun decode-int32 (s) - "4 byte -> 32 bit int -> two's compliment (in network byte order)" - (let ((i (+ (ash (elt s 0) 24) - (ash (elt s 1) 16) - (ash (elt s 2) 8) - (elt s 3)))) - (if (>= i #x7fffffff) - (- 0 (- #x100000000 i)) - i))) - (defun decode-uint32 (s) "4 byte -> 32 bit unsigned int" (let ((i (+ (ash (elt s 0) 24) @@ -309,6 +299,18 @@ (elt s 3)))) i)) +(defun decode-uint64 (s) + "8 byte -> 64 bit unsigned int" + (let ((i (+ (ash (elt s 0) 56) + (ash (elt s 1) 48) + (ash (elt s 2) 40) + (ash (elt s 3) 32) + (ash (elt s 4) 24) + (ash (elt s 5) 16) + (ash (elt s 6) 8) + (elt s 7)))) + i)) + (defmacro defint-encoder (num-of-octets &optional docstring) (let ((enc-name (intern (format nil "~:@(encode-int~)~D" (* 8 num-of-octets)))) (buf (gensym)) @@ -327,28 +329,16 @@ (defint-encoder 4 "Convert an integer into a sequence of 4 bytes in network byte order.") (defint-encoder 8 "Convert an integer into a sequence of 8 bytes in network byte order.") -(defun decode-uint64 (s) - "8 byte -> 64 bit unsigned int" - (let ((i (+ (ash (elt s 0) 56) - (ash (elt s 1) 48) - (ash (elt s 2) 40) - (ash (elt s 3) 32) - (ash (elt s 4) 24) - (ash (elt s 5) 16) - (ash (elt s 6) 8) - (elt s 7)))) - i)) +(defun decode-int32 (s) + "4 byte -> 32 bit int -> two's complement (in network byte order)" + (let ((i (decode-uint32 s))) + (if (>= i #x7fffffff) + (- 0 (- #x100000000 i)) + i))) (defun decode-int64 (s) - "8 byte -> 64 bit int -> two's compliment (in network byte order)" - (let ((i (+ (ash (elt s 0) 56) - (ash (elt s 1) 48) - (ash (elt s 2) 40) - (ash (elt s 3) 32) - (ash (elt s 4) 24) - (ash (elt s 5) 16) - (ash (elt s 6) 8) - (elt s 7)))) + "8 byte -> 64 bit int -> two's complement (in network byte order)" + (let ((i (decode-uint64 s))) (if (>= i #x7fffffffffffffff) (- 0 (- #x10000000000000000 i)) i))) From 9facf444f48eeb340a5f2d446a8b3f4bfad23d66 Mon Sep 17 00:00:00 2001 From: Javier Olaechea Date: Sun, 24 Mar 2019 16:43:20 -0500 Subject: [PATCH 3/4] Refactor uint Decoders Instead bit-shifting to 'place' the bits where we want them to be and adding Lisp provides the DPB function that allows us to deposit bits where we want them in an integer. --- osc.lisp | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/osc.lisp b/osc.lisp index 59da4e7..ad1847a 100644 --- a/osc.lisp +++ b/osc.lisp @@ -291,25 +291,22 @@ (ldb (byte 16 0) (decode-int32 s))) #-(or sbcl cmucl openmcl allegro) (error "cant decode floats using this implementation")) -(defun decode-uint32 (s) - "4 byte -> 32 bit unsigned int" - (let ((i (+ (ash (elt s 0) 24) - (ash (elt s 1) 16) - (ash (elt s 2) 8) - (elt s 3)))) - i)) +(defmacro defint-decoder (num-of-octets &optional docstring) + (let ((decoder-name (intern (format nil "~:@(decode-uint~)~D" (* 8 num-of-octets)))) + (seq (gensym)) + (int (gensym))) + `(defun ,decoder-name (,seq) + ,@(when docstring + (list docstring)) + (let* ((,int 0) + ,@(loop + for n below num-of-octets + collect `(,int (dpb (aref ,seq ,n) (byte 8 (* 8 (- (1- ,num-of-octets) ,n))) + ,int)))) + int)))) -(defun decode-uint64 (s) - "8 byte -> 64 bit unsigned int" - (let ((i (+ (ash (elt s 0) 56) - (ash (elt s 1) 48) - (ash (elt s 2) 40) - (ash (elt s 3) 32) - (ash (elt s 4) 24) - (ash (elt s 5) 16) - (ash (elt s 6) 8) - (elt s 7)))) - i)) +(defint-decoder 4 "4 byte -> 32 bit unsigned int") +(defint-decoder 8 "8 byte -> 64 bit unsigned int") (defmacro defint-encoder (num-of-octets &optional docstring) (let ((enc-name (intern (format nil "~:@(encode-int~)~D" (* 8 num-of-octets)))) From db5dd2b16608826f160f00b3b6e57d7a75e59acb Mon Sep 17 00:00:00 2001 From: Javier Olaechea Date: Sun, 24 Mar 2019 16:57:45 -0500 Subject: [PATCH 4/4] Use Read-time evaluation to make code more readable --- osc.lisp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osc.lisp b/osc.lisp index ad1847a..27ba54a 100644 --- a/osc.lisp +++ b/osc.lisp @@ -329,15 +329,15 @@ (defun decode-int32 (s) "4 byte -> 32 bit int -> two's complement (in network byte order)" (let ((i (decode-uint32 s))) - (if (>= i #x7fffffff) - (- 0 (- #x100000000 i)) + (if (>= i #.(1- (expt 2 31))) + (- (- #.(expt 2 32) i)) i))) (defun decode-int64 (s) "8 byte -> 64 bit int -> two's complement (in network byte order)" (let ((i (decode-uint64 s))) - (if (>= i #x7fffffffffffffff) - (- 0 (- #x10000000000000000 i)) + (if (>= i #.(1- (expt 2 63))) + (- (- #.(expt 2 64) i)) i))) ;; osc-strings are unsigned bytes, padded to a 4 byte boundary