utils.lisp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. ;;; -*- Mode: Lisp; show-trailing-whitespace: t; Base: 10; indent-tabs: nil; Syntax: ANSI-Common-Lisp; Package: UTILS; -*-
  2. ;;; Copyright (c) 2013, Mark VandenBrink. All rights reserved.
  3. (in-package #:utils)
  4. (eval-when (:compile-toplevel :load-toplevel :execute)
  5. #+dbg
  6. (defvar *standard-optimize-settings* '(optimize (debug 3)))
  7. #-dbg
  8. (defvar *standard-optimize-settings* '(optimize (speed 3) (safety 0) (space 0) (debug 0)))
  9. )
  10. ;;; Taken from ASDF
  11. (defmacro dbg (tag &rest exprs)
  12. "debug macro for print-debugging:
  13. TAG is typically a constant string or keyword to identify who is printing,
  14. but can be an arbitrary expression returning a tag to be princ'ed first;
  15. if the expression returns NIL, nothing is printed.
  16. EXPRS are expressions, which when the TAG was not NIL are evaluated in order,
  17. with their source code then their return values being printed each time.
  18. The last expression is *always* evaluated and its multiple values are returned,
  19. but its source and return values are only printed if TAG was not NIL;
  20. previous expressions are not evaluated at all if TAG returned NIL.
  21. The macro expansion has relatively low overhead in space or time."
  22. (let* ((last-expr (car (last exprs)))
  23. (other-exprs (butlast exprs))
  24. (tag-var (gensym "TAG"))
  25. (thunk-var (gensym "THUNK")))
  26. `(let ((,tag-var ,tag))
  27. (flet ,(when exprs `((,thunk-var () ,last-expr)))
  28. (if ,tag-var
  29. (dbg-helper ,tag-var
  30. (list ,@(loop :for x :in other-exprs :collect
  31. `(cons ',x #'(lambda () ,x))))
  32. ',last-expr ,(if exprs `#',thunk-var nil))
  33. ,(if exprs `(,thunk-var) '(values)))))))
  34. (defun dbg-helper (tag expressions-thunks last-expression last-thunk)
  35. ;; Helper for the above debugging macro
  36. (labels
  37. ((f (stream fmt &rest args)
  38. (with-standard-io-syntax
  39. (let ((*print-readably* nil)
  40. (*package* (find-package :cl)))
  41. (apply 'format stream fmt args)
  42. (finish-output stream))))
  43. (z (stream)
  44. (f stream "~&"))
  45. (e (fmt arg)
  46. (f *error-output* fmt arg))
  47. (x (expression thunk)
  48. (e "~& ~S => " expression)
  49. (let ((results (multiple-value-list (funcall thunk))))
  50. (e "~{~S~^ ~}~%" results)
  51. (apply 'values results))))
  52. (map () #'z (list *standard-output* *error-output* *trace-output*))
  53. (e "~A~%" tag)
  54. (loop :for (expression . thunk) :in expressions-thunks
  55. :do (x expression thunk))
  56. (if last-thunk
  57. (x last-expression last-thunk)
  58. (values))))
  59. (deftype octet () '(unsigned-byte 8))
  60. (deftype octets () '(simple-array octet (*)))
  61. (defmacro make-octets (len) `(make-array ,len :element-type 'octet))
  62. (defparameter *break-on-warn-user* nil "set to T if you'd like to stop in warn-user")
  63. (defun warn-user (format-string &rest args)
  64. "Print a warning error to *ERROR-OUTPUT* and continue"
  65. (declare #.utils:*standard-optimize-settings*)
  66. (when *break-on-warn-user*
  67. (break "Breaking in WARN-USER"))
  68. (format *error-output* "~&~%WARNING:: ")
  69. (apply #'format *error-output* format-string args)
  70. (format *error-output* "~&~%"))
  71. (defparameter *max-raw-bytes-print-len* 10 "Max number of octets to print from an array")
  72. (defun printable-array (array &optional (max-len *max-raw-bytes-print-len*))
  73. "Given an array, return a string of the first *MAX-RAW-BYTES-PRINT-LEN* bytes"
  74. (declare #.utils:*standard-optimize-settings*)
  75. (let* ((len (length array))
  76. (print-len (min len max-len))
  77. (printable-array (make-array print-len :element-type 'octet :displaced-to array)))
  78. (declare (fixnum max-len len)
  79. (type (array (unsigned-byte 8) 1) array))
  80. (format nil "[~:d of ~:d bytes] <~x>" print-len len printable-array)))
  81. (declaim (inline upto-null))
  82. (defun upto-null (string)
  83. "Trim STRING to end at first NULL found"
  84. (declare #.utils:*standard-optimize-settings*)
  85. (subseq string 0 (position #\Null string)))
  86. (defun dump-data (file-name data)
  87. (declare #.utils:*standard-optimize-settings*)
  88. (with-open-file (f file-name :direction :output :if-exists :supersede :element-type '(unsigned-byte 8))
  89. (write-sequence data f)))
  90. (defmacro redirect (filename &rest body)
  91. "Temporarily set *STANDARD-OUTPUT* to FILENAME and execute BODY."
  92. `(let ((*standard-output* (open ,filename :direction :output :if-does-not-exist :create :if-exists :supersede)))
  93. ,@body
  94. (finish-output *standard-output*)))
  95. (declaim (inline get-bitmask))
  96. (defun get-bitmask (start width)
  97. "Create a bit mask that begins at bit START (31 is MSB) and is WIDTH bits wide.
  98. Example: (get-bitmask 31 11) -->> #xffe00000"
  99. (declare #.utils:*standard-optimize-settings*)
  100. (ash (- (ash 1 width) 1) (- (1+ start) width)))
  101. (defmacro get-bitfield (int start width)
  102. "Extract WIDTH bits from INT starting at START
  103. Example: (get-bitfield #xFFFBB240 31 11) -->> #x7ff.
  104. The above will expand to (ash (logand #xFFFBB240 #xFFE00000) -21) at COMPILE time."
  105. `(ash (logand ,int ,(utils::get-bitmask start width)) ,(- ( - start width -1))))
  106. ;;;;;;;;;;;;;;;;;;;; convenience macros ;;;;;;;;;;;;;;;;;;;;
  107. (defmacro with-gensyms (syms &body body)
  108. `(let ,(mapcar #'(lambda (s)
  109. `(,s (gensym)))
  110. syms)
  111. ,@body))
  112. (defun make-keyword (name)
  113. (intern (string name) :keyword))
  114. (defmacro while (test &body body)
  115. `(do ()
  116. ((not ,test))
  117. ,@body))
  118. (defmacro aif (test-form then-form &optional else-form)
  119. `(let ((it ,test-form))
  120. (if it ,then-form ,else-form)))
  121. (defmacro awhen (test-form &body body)
  122. `(aif ,test-form
  123. (progn ,@body)))
  124. ;;; In multi-thread mode, need to protect insertions into hash-table
  125. ;;; Note: CCL hash-tables are thread-safe, but some other implementations
  126. ;;; don't appear to be.
  127. ;;;
  128. ;;; Also note that when running a MT-capable Lisp, we lock even when
  129. ;;; in single-threaded mode, simply for cleaner code.
  130. (defstruct locked-hash-table lock hash-table)
  131. #+(or :ccl :sbcl :abcl)
  132. (progn
  133. (defmacro make-lock () `(bt:make-lock))
  134. (defmacro with-lock ((l) &body body)
  135. `(bt:with-lock-held (,l)
  136. ,@body)))
  137. #-(or :ccl :sbcl :abcl)
  138. (progn
  139. (defmacro make-lock () nil)
  140. (defmacro with-lock ((l) &body body)
  141. (declare (ignore l))
  142. `(progn
  143. ,@body)))
  144. (defun mk-memoize (func-name)
  145. "Takes a normal function object and returns a memoized one"
  146. (declare #.utils:*standard-optimize-settings*)
  147. (let* ((func (symbol-function func-name))
  148. (the-hash-table (make-locked-hash-table
  149. :lock (make-lock)
  150. :hash-table (make-hash-table :test 'equal))))
  151. (with-slots (lock hash-table) the-hash-table
  152. #'(lambda (arg)
  153. (multiple-value-bind (value foundp) (gethash arg hash-table)
  154. (if foundp
  155. value
  156. (with-lock (lock)
  157. (setf (gethash arg hash-table) (funcall func arg)))))))))
  158. (defmacro memoize (func-name)
  159. "Memoize function associated with FUNC-NAME. Simplified version"
  160. `(setf (symbol-function ,func-name) (utils::mk-memoize ,func-name)))
  161. (defun timings (function)
  162. (declare #.utils:*standard-optimize-settings*)
  163. (let ((real-base (get-internal-real-time)))
  164. (funcall function)
  165. (float (/ (- (get-internal-real-time) real-base) internal-time-units-per-second))))