automate widetag dispatching
[sbcl.git] / src / code / seq.lisp
index d3e88e0..6636175 100644 (file)
 \f
 ;;;; SUBSEQ
 ;;;;
+
+(define-array-dispatch vector-subseq-dispatch (array start end)
+  (declare (optimize speed (safety 0)))
+  (declare (type index start end))
+  (subseq array start end))
+
 ;;;; The support routines for SUBSEQ are used by compiler transforms,
 ;;;; so we worry about dealing with END being supplied or defaulting
 ;;;; to NIL at this level.
                     (end end)
                     :check-fill-pointer t
                     :force-inline t)
-    (funcall (!find-vector-subseq-fun data) data start end)))
+    (vector-subseq-dispatch data start end)))
 
 (defun list-subseq* (sequence start end)
   (declare (type list sequence)
 (define-sequence-traverser replace
     (sequence1 sequence2 &rest args &key start1 end1 start2 end2)
   #!+sb-doc
-  "The target sequence is destructively modified by copying successive
-   elements into it from the source sequence."
+  "Destructively modifies SEQUENCE1 by copying successive elements
+into it from the SEQUENCE2.
+
+Elements are copied to the subseqeuence bounded by START1 and END1,
+from the subsequence bounded by START2 and END2. If these subsequences
+are not of the same length, then the shorter length determines how
+many elements are copied."
   (declare (truly-dynamic-extent args))
   (let* (;; KLUDGE: absent either rewriting FOO-REPLACE-FROM-BAR, or
          ;; excessively polluting DEFINE-SEQUENCE-TRAVERSER, we rebind
          first-sequence
          more-sequences))
 
-;;; KLUDGE: MAP has been rewritten substantially since the fork from
-;;; CMU CL in order to give reasonable performance, but this
-;;; implementation of MAP-INTO still has the same problems as the old
-;;; MAP code. Ideally, MAP-INTO should be rewritten to be efficient in
-;;; the same way that the corresponding cases of MAP have been
-;;; rewritten. Instead of doing it now, though, it's easier to wait
-;;; until we have DYNAMIC-EXTENT, at which time it should become
-;;; extremely easy to define a reasonably efficient MAP-INTO in terms
-;;; of (MAP NIL ..). -- WHN 20000920
+;;; Uses the machinery of (MAP NIL ...). For non-vectors we avoid
+;;; computing the length of the result sequence since we can detect
+;;; the end during mapping (if MAP even gets that far).
 (defun map-into (result-sequence function &rest sequences)
-  (let* ((fp-result
-          (and (arrayp result-sequence)
-               (array-has-fill-pointer-p result-sequence)))
-         (len (apply #'min
-                     (if fp-result
-                         (array-dimension result-sequence 0)
-                         (length result-sequence))
-                     (mapcar #'length sequences))))
-
-    (when fp-result
-      (setf (fill-pointer result-sequence) len))
-
-    (let ((really-fun (%coerce-callable-to-fun function)))
-      (dotimes (index len)
-        (setf (elt result-sequence index)
-              (apply really-fun
-                     (mapcar (lambda (seq) (elt seq index))
-                             sequences))))))
+  (declare (truly-dynamic-extent sequences))
+  (let ((really-fun (%coerce-callable-to-fun function)))
+    ;; For each result type, define a mapping function which is
+    ;; responsible for replacing RESULT-SEQUENCE elements and for
+    ;; terminating itself if the end of RESULT-SEQUENCE is reached.
+    ;;
+    ;; The mapping function is defined with the MAP-LAMBDA macrolet,
+    ;; whose syntax matches that of LAMBDA.
+    (macrolet ((map-lambda (params &body body)
+                 `(flet ((f ,params ,@body))
+                    (declare (truly-dynamic-extent #'f))
+                    ;; Note (MAP-INTO SEQ (LAMBDA () ...)) is a
+                    ;; different animal, hence the awkward flip
+                    ;; between MAP and LOOP.
+                    (if sequences
+                        (apply #'map nil #'f sequences)
+                        (loop (f))))))
+      ;; Optimize MAP-LAMBDAs since they are the inner loops. Because
+      ;; we are manually doing bounds checking with known types, turn
+      ;; off safety for vectors and lists but keep it for generic
+      ;; sequences.
+      (etypecase result-sequence
+        (vector
+         (locally (declare (optimize speed (safety 0)))
+           (with-array-data ((data result-sequence) (start) (end)
+                             ;; MAP-INTO ignores fill pointer when mapping
+                             :check-fill-pointer nil)
+             (let ((index start))
+               (declare (type index index))
+               (macrolet ((dispatch ()
+                            `(block mapping
+                               (map-lambda (&rest args)
+                                 (declare (truly-dynamic-extent args))
+                                 (when (eql index end)
+                                   (return-from mapping))
+                                 (setf (aref data index)
+                                       (apply really-fun args))
+                                 (incf index)))))
+                 (typecase data
+                   (simple-vector (dispatch))
+                   (otherwise (dispatch))))
+               (when (array-has-fill-pointer-p result-sequence)
+                 (setf (fill-pointer result-sequence) (- index start)))))))
+        (list
+         (let ((node result-sequence))
+           (declare (type list node))
+           (map-lambda (&rest args)
+             (declare (truly-dynamic-extent args) (optimize speed (safety 0)))
+             (when (null node)
+               (return-from map-into result-sequence))
+             (setf (car node) (apply really-fun args))
+             (setf node (cdr node)))))
+        (sequence
+         (multiple-value-bind (iter limit from-end)
+             (sb!sequence:make-sequence-iterator result-sequence)
+           (map-lambda (&rest args)
+             (declare (truly-dynamic-extent args) (optimize speed))
+             (when (sb!sequence:iterator-endp result-sequence
+                                              iter limit from-end)
+               (return-from map-into result-sequence))
+             (setf (sb!sequence:iterator-element result-sequence iter)
+                   (apply really-fun args))
+             (setf iter (sb!sequence:iterator-step result-sequence
+                                                   iter from-end))))))))
   result-sequence)
 \f
 ;;;; quantifiers
 (macrolet (;; shared logic for defining %FIND-POSITION and
            ;; %FIND-POSITION-IF in terms of various inlineable cases
            ;; of the expression defined in FROB and VECTOR*-FROB
-           (frobs ()
+           (frobs (&optional bit-frob)
              `(seq-dispatch sequence-arg
                (frob sequence-arg from-end)
                (with-array-data ((sequence sequence-arg :offset-var offset)
                                  (end end)
                                  :check-fill-pointer t)
                  (multiple-value-bind (f p)
-                     (macrolet ((frob2 () '(if from-end
-                                            (frob sequence t)
-                                            (frob sequence nil))))
+                     (macrolet ((frob2 () `(if from-end
+                                               (frob sequence t)
+                                               (frob sequence nil))))
                        (typecase sequence
                          #!+sb-unicode
                          ((simple-array character (*)) (frob2))
                          ((simple-array base-char (*)) (frob2))
-                         (t (vector*-frob sequence))))
+                         ,@(when bit-frob
+                             `((simple-bit-vector
+                                (if (and (eq #'identity key)
+                                         (or (eq #'eq test)
+                                             (eq #'eql test)
+                                             (eq #'equal test)))
+                                    (let ((p (%bit-position (the bit item) sequence
+                                                            from-end start end)))
+                                      (if p
+                                          (values item p)
+                                          (values nil nil)))
+                                    (vector*-frob sequence)))))
+                         (t
+                          (vector*-frob sequence))))
                    (declare (type (or index null) p))
                    (values f (and p (the index (- p offset)))))))))
   (defun %find-position (item sequence-arg from-end start end key test)
                (vector*-frob (sequence)
                  `(%find-position-vector-macro item ,sequence
                                                from-end start end key test)))
-      (frobs)))
+      (frobs t)))
   (defun %find-position-if (predicate sequence-arg from-end start end key)
     (macrolet ((frob (sequence from-end)
                  `(%find-position-if predicate ,sequence