From c4b44fb12108c0764d1fa4ef1c37f11a54379343 Mon Sep 17 00:00:00 2001
From: Piotr Szarmanski
Date: Mon, 24 Oct 2022 10:50:11 +0200
Subject: Add hash-backend and update README.

---
 README                | 17 +++++++++++++++++
 eris.asd              |  2 ++
 src/eris-decode.lisp  | 29 +++++++++++++++++++----------
 src/eris.lisp         |  1 +
 src/file-backend.lisp |  4 +++-
 src/hash-backend.lisp | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/package.lisp      | 10 +++++++++-
 7 files changed, 102 insertions(+), 12 deletions(-)
 create mode 100644 src/hash-backend.lisp

diff --git a/README b/README
index 21afb2f..606afba 100644
--- a/README
+++ b/README
@@ -41,3 +41,20 @@ function will attempt to decode an ERIS read-capability in parallel into a file
 specified by the OUTPUT-FILE string or pathspec.
 
 See the docstrings of the specific functions for more details.
+
+
+
+A high-level API is provided for convenience in backend.lisp. The concept is
+that a backend object is created, which holds information like output-function,
+fetch-function, caching details, block-size, etc. and the {en/de}coding
+functions simply take the backend as an argument.
+
+This interface consists of two generic functions: store-data, for encoding data,
+and fetch-read-capability, for retrieving the contents of a read-capability
+object.
+
+As an example, a file-based backend called file-backend is provided. It can be
+used simply by making an instance of the 'file-backend class with a :directory
+argument, which will point to the directory in which ERIS data is to be stored.
+
+There is also hash-backend, which implements a simple hash-table backend.
diff --git a/eris.asd b/eris.asd
index 0ec5c7f..1c18f7f 100644
--- a/eris.asd
+++ b/eris.asd
@@ -13,6 +13,8 @@
                          (:file "base32")
                          (:file "eris")
                          (:file "eris-decode")
+                         (:file "backend")
+                         (:file "file-backend")
                          #+unix (:file "parallel-decoder"))))
   :in-order-to ((test-op (test-op :eris/test))))
 
diff --git a/src/eris-decode.lisp b/src/eris-decode.lisp
index ba20e9d..3d93312 100644
--- a/src/eris-decode.lisp
+++ b/src/eris-decode.lisp
@@ -218,19 +218,28 @@ hash-table is used, a (copy-seq) needs to be done on the return value of
 gethash.
 
 The keyword argument CACHE-CAPACITY indicates the amount of blocks stored in the
-cache."
+cache. It may be NIL to turn off caching entirely."
   (declare (type read-capability read-capability)
            (type function fetch-function)
-           (type integer cache-capacity))
+           (type (or integer null) cache-capacity))
   (with-slots (level block-size root-reference-pair) read-capability
-    (let* ((get-block (cached-lambda (:cache-class 'lru-cache 
-                                      :capacity cache-capacity
-                                      :table (make-hash-table :size (1+ cache-capacity) :test #'equalp))
-                          (reference key &optional nonce)
-                        (let* ((block (execute-fetch-function fetch-function reference)))
-                          (unless block (error 'missing-block :reference reference))
-                          (hash-check block reference)
-                          (decrypt-block block key nonce))))
+    (let* ((get-block (if cache-capacity ;; handle caching
+                          (cached-lambda (:cache-class 'lru-cache 
+                                                       :capacity cache-capacity
+                                                       :table (make-hash-table :size (1+ cache-capacity) :test #'equalp))
+                                         (reference key nonce)
+                                         (declare (type octet-vector reference key))
+                                         (let ((block (execute-fetch-function fetch-function reference)))
+                                           (declare (type octet-vector block))
+                                           (hash-check block reference)
+                                           (decrypt-block block key nonce)))
+                        (lambda (reference key nonce)
+                          (declare (type octet-vector reference key)
+                                   (optimize (debug 3)))
+                          (let ((block (execute-fetch-function fetch-function reference)))
+                            (declare (type octet-vector block))
+                            (hash-check block reference)
+                            (decrypt-block block key nonce)))))
            (root (funcall get-block (reference root-reference-pair) (key root-reference-pair) (make-nonce level))))
       ;; "Implementations MUST verify the key appearing in the read capability
       ;; if level of encoded content is larger than 0."
diff --git a/src/eris.lisp b/src/eris.lisp
index 4693104..ed4a05e 100644
--- a/src/eris.lisp
+++ b/src/eris.lisp
@@ -81,6 +81,7 @@ object, using the bytes from the OCTETS vector from at START."
     (make-instance 'reference-pair :key key :reference reference)))
 
 (defun decrypt-block (input key &optional (nonce null-secret))
+  (declare (type octet-vector input key nonce))
   (ironclad:decrypt-in-place
    (ironclad:make-cipher :chacha :mode :stream :key key :initialization-vector nonce)
    input)
diff --git a/src/file-backend.lisp b/src/file-backend.lisp
index 2bb8693..03e3ef7 100644
--- a/src/file-backend.lisp
+++ b/src/file-backend.lisp
@@ -43,7 +43,9 @@
           (lambda (reference)
             (let* ((base32 (bytes-to-base32-unpadded reference))
                    (file (merge-pathnames directory base32)))
-              (alexandria:read-file-into-byte-vector file)))
+              (if (probe-file file)
+                  (alexandria:read-file-into-byte-vector file)
+                  (error 'missing-block :reference reference))))
           output-function
           (lambda (block reference)
             (let* ((base32 (bytes-to-base32-unpadded reference))
diff --git a/src/hash-backend.lisp b/src/hash-backend.lisp
new file mode 100644
index 0000000..91dc673
--- /dev/null
+++ b/src/hash-backend.lisp
@@ -0,0 +1,51 @@
+;; This file is part of eris-cl.
+;; Copyright (C) 2022 Piotr SzarmaƄski
+
+;; eris-cl is free software: you can redistribute it and/or modify it under the
+;; terms of the GNU Lesser General Public License as published by the Free
+;; Software Foundation, either version 3 of the License, or (at your option) any
+;; later version.
+
+;; eris-cl is distributed in the hope that it will be useful, but WITHOUT ANY
+;; WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+;; A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License along with
+;; eris-cl. If not, see <https://www.gnu.org/licenses/>.
+
+(in-package :eris)
+
+(defclass hash-backend (encoding-backend decoding-backend)
+  ((table :type hash-table)))
+
+(defmethod shared-initialize :after ((hash-backend hash-backend) slot-names
+                                     &rest initargs
+                                     &key &allow-other-keys)
+  
+  (declare (ignore initargs slot-names))
+  (let ((hash-table (make-hash-table :test #'equalp)))
+    (with-slots (table output-function fetch-function) hash-backend
+      (setf table hash-table
+            fetch-function (lambda (reference)
+                             (declare (type octet-vector reference))
+                             (copy-seq (gethash reference hash-table)))
+            output-function (lambda (block reference)
+                              (declare (type octet-vector block reference))
+                              (setf (gethash reference hash-table)
+                                    block))))))
+
+(defmethod fetch-read-capability (read-capability (backend hash-backend) &key &allow-other-keys)
+  (declare (type read-capability read-capability))
+  (with-slots (fetch-function) backend
+    (eris-decode read-capability fetch-function :cache-capacity nil)))
+
+(defmethod store-data (input (backend hash-backend) &key (secret null-secret) (block-size 1kib) &allow-other-keys)
+  (declare (type octet-vector secret))
+  (with-slots (output-function) backend
+    (eris-encode input
+                 block-size
+                 output-function
+                 :hash-output nil
+                 :secret secret)))
+
+
diff --git a/src/package.lisp b/src/package.lisp
index 873a453..0265f74 100644
--- a/src/package.lisp
+++ b/src/package.lisp
@@ -43,4 +43,12 @@
    #:padding-error
    #:version-mismatch
    #:hash-mismatch
-   #:invalid-internal-block))
+   #:invalid-internal-block
+   #:missing-block
+
+   #:store-data
+   #:fetch-read-capability
+   #:encoding-backend
+   #:decoding-backend
+   #:file-backend
+   #:hash-backend))
-- 
cgit v1.2.3