branch: main
commit 073f1a794b86227b71fcdbdec4a28b06acba14b0
Author: Romain GARBAGE <romain.garb...@inria.fr>
AuthorDate: Mon Feb 24 15:38:07 2025 +0100

    utils: Add ring buffer implementation.
    
    * src/cuirass/utils.scm (<ring-buffer>): New record type.
    (ring-buffer, ring-buffer-insert, ring-buffer->list): New variables.
    * tests/utils.scm: Add tests.
    
    Signed-off-by: Ludovic Courtès <l...@gnu.org>
---
 src/cuirass/utils.scm | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 tests/utils.scm       | 17 +++++++++++++
 2 files changed, 83 insertions(+), 1 deletion(-)

diff --git a/src/cuirass/utils.scm b/src/cuirass/utils.scm
index c406a84..b06f451 100644
--- a/src/cuirass/utils.scm
+++ b/src/cuirass/utils.scm
@@ -28,6 +28,7 @@
   #:use-module (rnrs bytevectors)
   #:use-module (system foreign)
   #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-9)
   #:use-module (srfi srfi-19)
   #:use-module (srfi srfi-71)
   #:autoload   (guix build utils) (mkdir-p)
@@ -60,7 +61,15 @@
             open-unix-listening-socket
 
             atomic-box-fetch-and-increment!
-            atomic-box-fetch-and-decrement!))
+            atomic-box-fetch-and-decrement!
+
+            ring-buffer
+            ring-buffer-head
+            ring-buffer-head-length
+            ring-buffer-limit
+            ring-buffer-rear
+            ring-buffer-insert
+            ring-buffer->list))
 
 (define-exception-type &cuirass-assertion-failure &assertion-failure
   make-cuirass-assertion-failure
@@ -347,3 +356,59 @@ and store the result inside the BOX."
 (define (atomic-box-fetch-and-decrement! box)
   "Atomically decrement the value of the integer stored inside the given BOX."
   (atomic-box-fetch-and-update! box 1-))
+
+;;;
+;;; Ring buffer implementation. Copied from GNU Shepherd.
+;;;
+
+;; Helper function needed by ring-buffer->list.
+(define (at-most max-length lst)
+  "If @var{lst} is shorter than @var{max-length}, return it and the empty list;
+otherwise return its @var{max-length} first elements and its tail."
+  (let loop ((len 0)
+             (lst lst)
+             (result '()))
+    (match lst
+      (()
+       (values (reverse result) '()))
+      ((head . tail)
+       (if (>= len max-length)
+           (values (reverse result) lst)
+           (loop (+ 1 len) tail (cons head result)))))))
+
+;; The poor developer's persistent "ring buffer": it holds between N and 2N
+;; elements, but has O(1) insertion.
+(define-record-type <ring-buffer>
+  (%ring-buffer limit front-length front rear)
+  ring-buffer?
+  (limit         ring-buffer-limit)
+  (front-length  ring-buffer-front-length)
+  (front         ring-buffer-front)
+  (rear          ring-buffer-rear))
+
+(define (ring-buffer size)
+  "Return an ring buffer that can hold @var{size} elements."
+  (%ring-buffer size 0 '() '()))
+
+(define-inlinable (ring-buffer-insert element buffer)
+  "Insert @var{element} to the front of @var{buffer}.  If @var{buffer} is
+already full, its oldest element is removed."
+  (match buffer
+    (($ <ring-buffer> limit front-length front rear)
+     (if (< front-length limit)
+         (let ((front-length (+ 1 front-length)))
+           (%ring-buffer limit front-length
+                         (cons element front)
+                         (if (= limit front-length)
+                             '()
+                             rear)))
+         (%ring-buffer limit 1
+                       (list element) front)))))
+
+(define (ring-buffer->list buffer)
+  "Convert @var{buffer} into a list."
+  (match buffer
+    (($ <ring-buffer> limit front-length front rear)
+     (if (= limit front-length)
+         front
+         (append front (at-most (- limit front-length) rear))))))
diff --git a/tests/utils.scm b/tests/utils.scm
index e1ac1b8..4a3b48f 100644
--- a/tests/utils.scm
+++ b/tests/utils.scm
@@ -83,4 +83,21 @@
            (const 42)))))
    #:to 'value))
 
+(test-equal "ring-buffer->list 1 element"
+  '(el)
+  (let ((buffer (ring-buffer 5)))
+    (ring-buffer->list (ring-buffer-insert 'el buffer))))
+
+(test-equal "ring-buffer->list empty buffer"
+  '()
+  (ring-buffer->list (ring-buffer 5)))
+
+(test-equal "ring-buffer->list full"
+  '(9 8 7 6 5)
+  (ring-buffer->list (fold
+                      (lambda (e r)
+                        (pk 'r (ring-buffer-insert e r)))
+                      (ring-buffer 5)
+                      (iota 10))))
+
 (test-end)

Reply via email to