]> code.delx.au - gnu-emacs/blobdiff - lisp/emacs-lisp/ring.el
Emulate POSIX_SIGNALS on MS-Windows.
[gnu-emacs] / lisp / emacs-lisp / ring.el
index 390aba93da164804582b6cac39b005c96d205c69..b961d403fa084f82e1b985719e135b36a1b8eff7 100644 (file)
@@ -1,16 +1,17 @@
 ;;; ring.el --- handle rings of items
 
-;; Copyright (C) 1992 Free Software Foundation, Inc.
+;; Copyright (C) 1992, 2001, 2002, 2003, 2004, 2005,
+;;   2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
 
 ;; Maintainer: FSF
 ;; Keywords: extensions
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to
-;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
-;;; This code defines a ring data structure. A ring is a 
-;;;     (hd-index length . vector) 
-;;; list. You can insert to, remove from, and rotate a ring. When the ring
-;;; fills up, insertions cause the oldest elts to be quietly dropped.
-;;;
-;;; In ring-ref, 0 is the index of the newest element.  Higher indexes
-;;; correspond to older elements until they wrap.
-;;;
-;;; hd-index = index of the newest item on the ring.
-;;; length = number of ring items.
-;;;
-;;; These functions are used by the input history mechanism, but they can
-;;; be used for other purposes as well.
+;; This code defines a ring data structure.  A ring is a
+;;     (hd-index length . vector)
+;; list.  You can insert to, remove from, and rotate a ring.  When the ring
+;; fills up, insertions cause the oldest elts to be quietly dropped.
+;;
+;; In ring-ref, 0 is the index of the newest element.  Higher indexes
+;; correspond to older elements; when the index equals the ring length,
+;; it wraps to the newest element again.
+;;
+;; hd-index = vector index of the oldest ring item.
+;;         Newer items follow this item; at the end of the vector,
+;;         they wrap around to the start of the vector.
+;; length = number of items currently in the ring.
+;;         This never exceeds the length of the vector itself.
+;;
+;; These functions are used by the input history mechanism, but they can
+;; be used for other purposes as well.
 
 ;;; Code:
 
+;;; User Functions:
+
 ;;;###autoload
-(defun ring-p (x) 
-  "Returns t if X is a ring; nil otherwise."
+(defun ring-p (x)
+  "Return t if X is a ring; nil otherwise."
   (and (consp x) (integerp (car x))
-       (consp (cdr x)) (integerp (car (cdr x)))
-       (vectorp (cdr (cdr x)))))
+       (consp (cdr x)) (integerp (cadr x))
+       (vectorp (cddr x))))
 
 ;;;###autoload
 (defun make-ring (size)
   "Make a ring that can contain SIZE elements."
   (cons 0 (cons 0 (make-vector size nil))))
 
+(defun ring-insert-at-beginning (ring item)
+  "Add to RING the item ITEM, at the front, as the oldest item."
+  (let* ((vec (cddr ring))
+         (veclen (length vec))
+         (hd (car ring))
+         (ln (cadr ring)))
+    (setq ln (min veclen (1+ ln))
+          hd (ring-minus1 hd veclen))
+    (aset vec hd item)
+    (setcar ring hd)
+    (setcar (cdr ring) ln)))
+
 (defun ring-plus1 (index veclen)
-  "INDEX+1, with wraparound"
-  (let ((new-index (+ index 1)))
+  "Return INDEX+1, with wraparound."
+  (let ((new-index (1+ index)))
     (if (= new-index veclen) 0 new-index)))
 
 (defun ring-minus1 (index veclen)
-  "INDEX-1, with wraparound"
-  (- (if (= 0 index) veclen index) 1))
+  "Return INDEX-1, with wraparound."
+  (- (if (zerop index) veclen index) 1))
 
 (defun ring-length (ring)
-  "Number of elements in the ring."
-  (car (cdr ring)))
-
-(defun ring-empty-p (ring)
-  (= 0 (car (cdr ring))))
+  "Return the number of elements in the RING."
+  (cadr ring))
 
 (defun ring-index (index head ringlen veclen)
+  "Convert nominal ring index INDEX to an internal index.
+The internal index refers to the items ordered from newest to oldest.
+HEAD is the index of the oldest element in the ring.
+RINGLEN is the number of elements currently in the ring.
+VECLEN is the size of the vector in the ring."
   (setq index (mod index ringlen))
   (mod (1- (+ head (- ringlen index))) veclen))
 
+(defun ring-empty-p (ring)
+  "Return t if RING is empty; nil otherwise."
+  (zerop (cadr ring)))
+
+(defun ring-size (ring)
+  "Return the size of RING, the maximum number of elements it can contain."
+  (length (cddr ring)))
+
+(defun ring-copy (ring)
+  "Return a copy of RING."
+  (let ((vec (cddr ring))
+       (hd  (car ring))
+       (ln  (cadr ring)))
+    (cons hd (cons ln (copy-sequence vec)))))
+
 (defun ring-insert (ring item)
-  "Insert a new item onto the ring. If the ring is full, dump the oldest
-item to make room."       
-  (let* ((vec (cdr (cdr ring))) 
-        (veclen (length vec))
-        (hd (car ring))
-        (ln (car (cdr ring))))
+  "Insert onto ring RING the item ITEM, as the newest (last) item.
+If the ring is full, dump the oldest item to make room."
+  (let* ((vec (cddr ring))
+         (veclen (length vec))
+         (hd (car ring))
+         (ln (cadr ring)))
     (prog1
-       (aset vec (mod (+ hd ln) veclen) item)
+        (aset vec (mod (+ hd ln) veclen) item)
       (if (= ln veclen)
-         (setcar ring (ring-plus1 hd veclen))
-       (setcar (cdr ring) (1+ ln))))))
+          (setcar ring (ring-plus1 hd veclen))
+        (setcar (cdr ring) (1+ ln))))))
 
 (defun ring-remove (ring &optional index)
   "Remove an item from the RING.  Return the removed item.
@@ -91,32 +126,115 @@ numeric, remove the element indexed."
   (if (ring-empty-p ring)
       (error "Ring empty")
     (let* ((hd (car ring))
-         (ln (car (cdr ring)))
-         (vec (cdr (cdr ring)))
-         (veclen (length vec))
-         (tl (mod (1- (+ hd ln)) veclen))
-         oldelt)
-      (if (null index)
-         (setq index (1- ln)))
+           (ln (cadr ring))
+           (vec (cddr ring))
+           (veclen (length vec))
+           (tl (mod (1- (+ hd ln)) veclen))
+           oldelt)
+      (when (null index)
+       (setq index (1- ln)))
       (setq index (ring-index index hd ln veclen))
       (setq oldelt (aref vec index))
       (while (/= index tl)
-       (aset vec index (aref vec (ring-plus1 index veclen)))
-       (setq index (ring-plus1 index veclen)))
+        (aset vec index (aref vec (ring-plus1 index veclen)))
+        (setq index (ring-plus1 index veclen)))
       (aset vec tl nil)
       (setcar (cdr ring) (1- ln))
       oldelt)))
 
 (defun ring-ref (ring index)
-  "Returns RING's INDEX element.
-INDEX need not be <= the ring length, the appropriate modulo operation
-will be performed.  Element 0 is the most recently inserted; higher indices
-correspond to older elements until they wrap."
+  "Return RING's INDEX element.
+INDEX = 0 is the most recently inserted; higher indices
+correspond to older elements.
+INDEX need not be <= the ring length; the appropriate modulo operation
+will be performed."
   (if (ring-empty-p ring)
-      (error "indexed empty ring")
-    (let* ((hd (car ring))  (ln (car (cdr ring)))  (vec (cdr (cdr ring))))
+      (error "Accessing an empty ring")
+    (let ((hd (car ring))
+         (ln (cadr ring))
+         (vec (cddr ring)))
       (aref vec (ring-index index hd ln (length vec))))))
 
+(defun ring-elements (ring)
+  "Return a list of the elements of RING, in order, newest first."
+  (let ((start (car ring))
+       (size (ring-size ring))
+       (vect (cddr ring))
+       lst)
+    (dotimes (var (cadr ring) lst)
+      (push (aref vect (mod (+ start var) size)) lst))))
+
+(defun ring-member (ring item)
+  "Return index of ITEM if on RING, else nil.
+Comparison is done via `equal'.  The index is 0-based."
+  (catch 'found
+    (dotimes (ind (ring-length ring) nil)
+      (when (equal item (ring-ref ring ind))
+       (throw 'found ind)))))
+
+(defun ring-next (ring item)
+  "Return the next item in the RING, after ITEM.
+Raise error if ITEM is not in the RING."
+  (let ((curr-index (ring-member ring item)))
+    (unless curr-index (error "Item is not in the ring: `%s'" item))
+    (ring-ref ring (ring-plus1 curr-index (ring-length ring)))))
+
+(defun ring-previous (ring item)
+  "Return the previous item in the RING, before ITEM.
+Raise error if ITEM is not in the RING."
+  (let ((curr-index (ring-member ring item)))
+    (unless curr-index (error "Item is not in the ring: `%s'" item))
+    (ring-ref ring (ring-minus1 curr-index (ring-length ring)))))
+
+(defun ring-insert+extend (ring item &optional grow-p)
+  "Like `ring-insert', but if GROW-P is non-nil, then enlarge ring.
+Insert onto ring RING the item ITEM, as the newest (last) item.
+If the ring is full, behavior depends on GROW-P:
+  If GROW-P is non-nil, enlarge the ring to accommodate the new item.
+  If GROW-P is nil, dump the oldest item to make room for the new."
+  (let* ((vec (cddr ring))
+        (veclen (length vec))
+        (hd (car ring))
+        (ringlen (ring-length ring)))
+    (prog1
+        (cond ((and grow-p (= ringlen veclen)) ; Full ring.  Enlarge it.
+               (setq veclen (1+ veclen))
+               (setcdr ring (cons (setq ringlen (1+ ringlen))
+                                  (setq vec (vconcat vec (vector item)))))
+               (setcar ring hd))
+              (t (aset vec (mod (+ hd ringlen) veclen) item)))
+      (if (= ringlen veclen)
+          (setcar ring (ring-plus1 hd veclen))
+        (setcar (cdr ring) (1+ ringlen))))))
+
+(defun ring-remove+insert+extend (ring item &optional grow-p)
+  "`ring-remove' ITEM from RING, then `ring-insert+extend' it.
+This ensures that there is only one ITEM on RING.
+
+If the RING is full, behavior depends on GROW-P:
+  If GROW-P is non-nil, enlarge the ring to accommodate the new ITEM.
+  If GROW-P is nil, dump the oldest item to make room for the new."
+  (let (ind)
+    (while (setq ind (ring-member ring item))
+      (ring-remove ring ind)))
+  (ring-insert+extend ring item grow-p))
+
+(defun ring-convert-sequence-to-ring (seq)
+  "Convert sequence SEQ to a ring.  Return the ring.
+If SEQ is already a ring, return it."
+  (if (ring-p seq)
+      seq
+    (let* ((size (length seq))
+           (ring (make-ring size)))
+      (dotimes (count size)
+        (when (or (ring-empty-p ring)
+                 (not (equal (ring-ref ring 0) (elt seq count))))
+         (ring-insert-at-beginning ring (elt seq count))))
+      ring)))
+
+;;; provide ourself:
+
 (provide 'ring)
 
+;; arch-tag: e707682b-ed69-47c9-b20f-cf2c68cc92d2
 ;;; ring.el ends here