-
Notifications
You must be signed in to change notification settings - Fork 8
/
common-ring.lisp
177 lines (158 loc) · 6.36 KB
/
common-ring.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
;;;;; -*- mode: common-lisp; common-lisp-style: modern; coding: utf-8; -*-
;;;;;
(defpackage :ring
(:use :cl)
(:shadow :push :pop :rotate :length)
(:export :make :push :pop :rotate :length :ref :with-ring-locked))
(in-package :ring)
(defun %print-ring (obj stream depth)
(declare (ignore depth obj))
(write-string "#<Ring>" stream))
(defstruct (ring (:predicate ringp)
(:constructor internal-make-ring)
(:print-function %print-ring))
"The ring data structure: An empty ring is indicated by an negative
First value. The Bound is made (1- (- Size)) to make length work.
Things are pushed at high indices first."
(first -1 :type fixnum) ;The index of the first position used.
(bound -1 :type fixnum) ;The index after the last element.
delete-function ;The function to be called on deletion.
(vector #() :type simple-vector) ;The vector.
(lock (bt:make-lock)))
(defmacro with-ring-locked ((ring) &body body)
`(bt:with-lock-held ((ring-lock ,ring))
,@body))
(defun make (size &optional (delete-function #'identity))
"Make a ring-buffer which can hold up to Size objects. Delete-Function
is a function which is called with each object that falls off the
end."
(unless (and (typep size 'fixnum) (> size 0))
(error "Ring size, ~S is not a positive fixnum." size))
(internal-make-ring
:delete-function delete-function
:vector (make-array size)
:bound (1- (- size))))
(defun push (object ring)
"Push an object into a ring, deleting an element if necessary."
(with-ring-locked (ring)
(let ((first (ring-first ring))
(vec (ring-vector ring))
(victim 0))
(declare (simple-vector vec) (fixnum first victim))
(cond
;; If zero, wrap around to end.
((zerop first)
(setq victim (1- (cl:length vec))))
;; If empty then fix up pointers.
((minusp first)
(setf (ring-bound ring) 0)
(setq victim (1- (cl:length vec))))
(t
(setq victim (1- first))))
(when (= first (ring-bound ring))
(funcall (ring-delete-function ring) (aref vec victim))
(setf (ring-bound ring) victim))
(setf (ring-first ring) victim)
(setf (aref vec victim) object))))
(defun pop (ring)
"Pop an object from a ring and return it."
(with-ring-locked (ring)
(let* ((first (ring-first ring))
(vec (ring-vector ring))
(new (if (= first (1- (cl:length vec))) 0 (1+ first)))
(bound (ring-bound ring)))
(declare (fixnum first new bound) (simple-vector vec))
(cond
((minusp bound)
(error "Cannot pop from an empty ring."))
((= new bound)
(setf (ring-first ring) -1 (ring-bound ring) (1- (- (cl:length vec)))))
(t
(setf (ring-first ring) new)))
(shiftf (aref vec first) nil))))
(defun length (ring)
"Return as values the current and maximum size of a ring."
(with-ring-locked (ring)
(let ((diff (- (ring-bound ring) (ring-first ring)))
(max (cl:length (ring-vector ring))))
(declare (fixnum diff max))
(values (if (plusp diff) diff (+ max diff)) max))))
(defun ref (ring index)
(declare (fixnum index))
"Return the index'th element of a ring. This can be set with Setf."
(with-ring-locked (ring)
(let ((first (ring-first ring)))
(declare (fixnum first))
(cond
((and (zerop index) (not (minusp first)))
(aref (ring-vector ring) first))
(t
(let* ((diff (- (ring-bound ring) first))
(sum (+ first index))
(vec (ring-vector ring))
(max (cl:length vec)))
(declare (fixnum diff max sum) (simple-vector vec))
(when (or (>= index (if (plusp diff) diff (+ max diff)))
(minusp index))
(error "Ring index ~D out of bounds." index))
(aref vec (if (>= sum max) (- sum max) sum))))))))
(defun %set-ring-ref (ring index value)
(declare (fixnum index))
(with-ring-locked (ring)
(let* ((first (ring-first ring))
(diff (- (ring-bound ring) first))
(sum (+ first index))
(vec (ring-vector ring))
(max (cl:length vec)))
(declare (fixnum diff first max) (simple-vector vec))
(when (or (>= index (if (plusp diff) diff (+ max diff))) (minusp index))
(error "Ring index ~D out of bounds." index))
(setf (aref vec (if (>= sum max) (- sum max) sum)) value))))
(eval-when (:compile-toplevel :execute)
(defmacro 1+m (exp base)
`(if (= ,exp ,base) 0 (1+ ,exp)))
(defmacro 1-m (exp base)
`(if (zerop ,exp) ,base (1- ,exp))))
(defun rotate (ring offset)
"Rotate a ring forward, i.e. second -> first, with positive offset,
or backwards with negative offset. blt'ing elements as necessary."
(declare (fixnum offset))
(with-ring-locked (ring)
(let* ((first (ring-first ring))
(bound (ring-bound ring))
(vec (ring-vector ring))
(max (cl:length vec)))
(declare (fixnum first bound max) (simple-vector vec))
(cond
((= first bound)
(let ((new (rem (+ offset first) max)))
(declare (fixnum new))
(if (minusp new) (setq new (+ new max)))
(setf (ring-first ring) new)
(setf (ring-bound ring) new)))
((not (minusp first))
(let* ((diff (- bound first))
(1-max (1- max))
(length (if (plusp diff) diff (+ max diff)))
(off (rem offset length)))
(declare (fixnum diff length off 1-max))
(cond
((minusp offset)
(do ((dst (1-m first 1-max) (1-m dst 1-max))
(src (1-m bound 1-max) (1-m src 1-max))
(cnt off (1+ cnt)))
((zerop cnt)
(setf (ring-first ring) (1+m dst 1-max))
(setf (ring-bound ring) (1+m src 1-max)))
(declare (fixnum dst src cnt))
(shiftf (aref vec dst) (aref vec src) nil)))
(t
(do ((dst bound (1+m dst 1-max))
(src first (1+m src 1-max))
(cnt off (1- cnt)))
((zerop cnt)
(setf (ring-first ring) src)
(setf (ring-bound ring) dst))
(declare (fixnum dst src cnt))
(shiftf (aref vec dst) (aref vec src) nil)))))))))
ring)