-;;;; array accessors
-
-;;; FIXME: This was commented out in sbcl-0.6.9.21 since it was
-;;; causing a problem in a CHAR form in HEXSTR. It's still important
-;;; to be able to inline this, so something along these lines
-;;; will probably be back, but it might be different in detail, e.g.
-;;; (DECLAIM (MAYBE-INLINE %WITH-ARRAY-DATA)).
-#|
-;;; Handle the 1-dimensional case of %WITH-ARRAY-DATA specially. It's
-;;; important to do this efficiently if we want people to be able to
-;;; use vectors with fill pointers anywhere near inner loops, and
-;;; hence it's important to do this efficiently if we want people to
-;;; be able to use sequence functions anywhere near inner loops.
-(deftransform %with-array-data ((array start end)
- (vector index (or index null))
- *
- :important t
- :node node
- :policy (> speed space))
- "avoid full call to %WITH-ARRAY-DATA at runtime"
- (let* ((element-ctype (extract-upgraded-element-type array))
- (element-type-specifier (type-specifier element-ctype))
- (simple-array-type `(simple-array ,element-type-specifier 1)))
- (declare (type ctype element-ctype))
- `(let* (;; FIXME: Instead of doing this hairy expression for SIZE,
- ;; it should just be (ARRAY-DIMENSION ARRAY 0), and there
- ;; should be a DEFTRANSFORM for ARRAY-DIMENSION which
- ;; expands that way.
- (size (if (array-header-p array)
- (%array-dimension array 0)
- (length (the ,simple-array-type array))))
- (end (if end
- (if (or ,(policy node (= safety 0))
- (<= (the index end) size))
- end
- (vector-data-start-out-of-range))
- size)))
- (declare (type index end))
- (unless (or ,(policy node (= safety 0))
- (<= start end))
- (vector-data-end-out-of-range))
- (do (;; cumulative displacement
- (d 0 (truly-the index (+ d (%array-displacement array))))
- ;; eventually becomes bare data vector
- (v array (%array-data-vector v)))
- ((not (array-header-p v))
- (values (the ,simple-array-type v)
- (truly-the index (+ d start))
- (truly-the index (+ d end))
- (the index d)))
- (declare (type index d))))))
-(defun vector-data-start-out-of-range ()
- (error "The start of vector data was out of range."))
-(defun vector-data-end-out-of-range ()
- (error "The end of vector data was out of range."))
-|#
-
+;;;; WITH-ARRAY-DATA
+
+;;; This checks to see whether the array is simple and the start and
+;;; end are in bounds. If so, it proceeds with those values.
+;;; Otherwise, it calls %WITH-ARRAY-DATA. Note that %WITH-ARRAY-DATA
+;;; may be further optimized.
+;;;
+;;; Given any ARRAY, bind DATA-VAR to the array's data vector and
+;;; START-VAR and END-VAR to the start and end of the designated
+;;; portion of the data vector. SVALUE and EVALUE are any start and
+;;; end specified to the original operation, and are factored into the
+;;; bindings of START-VAR and END-VAR. OFFSET-VAR is the cumulative
+;;; offset of all displacements encountered, and does not include
+;;; SVALUE.
+;;;
+;;; When FORCE-INLINE is set, the underlying %WITH-ARRAY-DATA form is
+;;; forced to be inline, overriding the ordinary judgment of the
+;;; %WITH-ARRAY-DATA DEFTRANSFORMs. Ordinarily the DEFTRANSFORMs are
+;;; fairly picky about their arguments, figuring that if you haven't
+;;; bothered to get all your ducks in a row, you probably don't care
+;;; that much about speed anyway! But in some cases it makes sense to
+;;; do type testing inside %WITH-ARRAY-DATA instead of outside, and
+;;; the DEFTRANSFORM can't tell that that's going on, so it can make
+;;; sense to use FORCE-INLINE option in that case.
+(def!macro with-array-data (((data-var array &key offset-var)
+ (start-var &optional (svalue 0))
+ (end-var &optional (evalue nil))
+ &key force-inline check-fill-pointer)
+ &body forms
+ &environment env)
+ (once-only ((n-array array)
+ (n-svalue `(the index ,svalue))
+ (n-evalue `(the (or index null) ,evalue)))
+ (let ((check-bounds (policy env (plusp insert-array-bounds-checks))))
+ `(multiple-value-bind (,data-var
+ ,start-var
+ ,end-var
+ ,@(when offset-var `(,offset-var)))
+ (if (not (array-header-p ,n-array))
+ (let ((,n-array ,n-array))
+ (declare (type (simple-array * (*)) ,n-array))
+ ,(once-only ((n-len (if check-fill-pointer
+ `(length ,n-array)
+ `(array-total-size ,n-array)))
+ (n-end `(or ,n-evalue ,n-len)))
+ (if check-bounds
+ `(if (<= 0 ,n-svalue ,n-end ,n-len)
+ (values ,n-array ,n-svalue ,n-end 0)
+ ,(if check-fill-pointer
+ `(sequence-bounding-indices-bad-error ,n-array ,n-svalue ,n-evalue)
+ `(array-bounding-indices-bad-error ,n-array ,n-svalue ,n-evalue)))
+ `(values ,n-array ,n-svalue ,n-end 0))))
+ ,(if force-inline
+ `(%with-array-data-macro ,n-array ,n-svalue ,n-evalue
+ :check-bounds ,check-bounds
+ :check-fill-pointer ,check-fill-pointer)
+ (if check-fill-pointer
+ `(%with-array-data/fp ,n-array ,n-svalue ,n-evalue)
+ `(%with-array-data ,n-array ,n-svalue ,n-evalue))))
+ ,@forms))))
+
+;;; This is the fundamental definition of %WITH-ARRAY-DATA, for use in
+;;; DEFTRANSFORMs and DEFUNs.
+(def!macro %with-array-data-macro (array
+ start
+ end
+ &key
+ (element-type '*)
+ check-bounds
+ check-fill-pointer)
+ (with-unique-names (size defaulted-end data cumulative-offset)
+ `(let* ((,size ,(if check-fill-pointer
+ `(length ,array)
+ `(array-total-size ,array)))
+ (,defaulted-end (or ,end ,size)))
+ ,@(when check-bounds
+ `((unless (<= ,start ,defaulted-end ,size)
+ ,(if check-fill-pointer
+ `(sequence-bounding-indices-bad-error ,array ,start ,end)
+ `(array-bounding-indices-bad-error ,array ,start ,end)))))
+ (do ((,data ,array (%array-data-vector ,data))
+ (,cumulative-offset 0
+ (+ ,cumulative-offset
+ (%array-displacement ,data))))
+ ((not (array-header-p ,data))
+ (values (the (simple-array ,element-type 1) ,data)
+ (the index (+ ,cumulative-offset ,start))
+ (the index (+ ,cumulative-offset ,defaulted-end))
+ (the index ,cumulative-offset)))
+ (declare (type index ,cumulative-offset))))))
+
+(defun transform-%with-array-data/muble (array node check-fill-pointer)
+ (let ((element-type (upgraded-element-type-specifier-or-give-up array))
+ (type (lvar-type array))
+ (check-bounds (policy node (plusp insert-array-bounds-checks))))
+ (if (and (array-type-p type)
+ (not (array-type-complexp type))
+ (listp (array-type-dimensions type))
+ (not (null (cdr (array-type-dimensions type)))))
+ ;; If it's a simple multidimensional array, then just return
+ ;; its data vector directly rather than going through
+ ;; %WITH-ARRAY-DATA-MACRO. SBCL doesn't generally generate
+ ;; code that would use this currently, but we have encouraged
+ ;; users to use WITH-ARRAY-DATA and we may use it ourselves at
+ ;; some point in the future for optimized libraries or
+ ;; similar.
+ (if check-bounds
+ `(let* ((data (truly-the (simple-array ,element-type (*))
+ (%array-data-vector array)))
+ (len (length data))
+ (real-end (or end len)))
+ (unless (<= 0 start data-end lend)
+ (sequence-bounding-indices-bad-error array start end))
+ (values data 0 real-end 0))
+ `(let ((data (truly-the (simple-array ,element-type (*))
+ (%array-data-vector array))))
+ (values data 0 (or end (length data)) 0)))
+ `(%with-array-data-macro array start end
+ :check-fill-pointer ,check-fill-pointer
+ :check-bounds ,check-bounds
+ :element-type ,element-type))))
+
+;; It might very well be reasonable to allow general ARRAY here, I
+;; just haven't tried to understand the performance issues involved.
+;; -- WHN, and also CSR 2002-05-26