serializable-object

2019-12-27

Provides a simple class and API for the objects serializable in a FASL file

Upstream URL

github.com/guicho271828/serializable-object

Author

Masataro Asai

License

LGPL
README

1Serializable-Object https://travis-ci.org/guicho271828/serializable-object.svg?branch=master

This library provides an abstract class for serializing CLOS objects in a FASL file.

  • It exposes serializable-object class, save generic function and load-instance function.An instance of serializable-object has a slot pathname.
  • The implementation focuses on simplicity and conformance rather than speed.
  • It carefully combines the behavior of make-load-form and the file compiler specified in CLHS.
    • This guarantees the correct behavior for storing and loading the references (e.g. cyclic reference, displaced arrays)
  • Should be thread-safe.
  • :compression t compresses the fasl file, similar to the numpy .npz format.

TODO: The current implementation stores an object into a fasl file, whose format is implementation-dependent (and also version dependent). Future work could instead use an existing CLOS object serializer.

We do not use them currently for various reasons: First, the existing serializers are incomplete and unreliable; They are not as thoroughly tested as the default serialization performed in compile-file and load, which is required by the standard and is provided by each Lisp implementation.

The strongest selling point of this library is this reliability,with the cost of being restricted to the FASL-compatible lisp versions.

TODO: This library was originally written as a MOP abstract class. However, the initial implementation with MOP was not working as intended and I decided to switch to the easier solution due to the timeframe. In the future, we will pursue the API where make-instance can take :path and other keywords which are used for loading an instance if necessary.

1.1Usage

(ql:quickload :serializable-object)
(in-package :serializable-object)


(defclass my (serializable-object)
  ((value :initarg :value :initform nil)))

(defparameter *my-instance* (make-instance 'my :value 5))

(save *my-instance* :verbose t :pathname "/tmp/tmp")
;; => Saving object #<MY {100E9DDC83}> to /tmp/tmp 
;; => Writing a magic code to /tmp/tmpAAURSO1.tmp 
;; => ; compiling file "/tmp/tmpAAURSO1.tmp" (written 12 MAR 2019 12:54:04 PM):
;; => ; compiling (INITIALIZATION-FORM)
;; => 
;; => ; wrote /tmp/tmp.fasl
;; -> ; compilation finished in 0:00:00.004
;; -> #P"/tmp/tmp.fasl"
;; -> ()

(defparameter *another-instance* (load-instance 'my :verbose t :pathname "/tmp/tmp"))
;; => ; loading #P"/tmp/tmp.fasl"
;; -> *ANOTHER-INSTANCE*

(print (slot-value *another-instance* 'value))
;; => 5

1.2API

Class SERIALIZABLE-OBJECT

Abstract class.

 Generic function (save instance &key pathname store verbose parents)
 Methods:
   (save (serializable-object))
   (save :around (serializable-object))

Save an instance to a FASL file using the value of PATHNAME slot in the instance. When PATHNAME is given as an argument,

  • the object is stored in the file specified by PATHNAME,
  • the slot value is temporarily set to this value while saving the instance,
  • and the value of PATHNAME is used to save the PATHNAME slot in the saved object.

If STORE is non-nil when PATHNAME is given, PATHNAME also overwrites the slot value in the runtime object. Otherwise the PATHNAME slot value is restored to the original value after returning from this function.

If PARENTS is non-nil (default: t), ENSURE-DIRECTORIES-EXIST is called to ensure that the path exists.

When an error occurs during the call to SAVE (e.g. nonexisting directory or permission error), the path is reverted to the original value.

How it works:

  1. SAVE generic-function stores a single line of macro (initialization-form) toa temporary file and compiles a file under the dynamic environment where aspecial variable instance is bound to the object to be stored.
  2. The file compiler expands the macro to the code that sets instance to thevalue of instance. MAKE-LOAD-FORM expands the value into a loadable form.
  3. To load the instance, LOAD-INSTANCE function sets up a dynamic binding forinstance and load the compiled file in this dynamic environment. Thecompiled code sets the newly created object (by evaluating the form producedby make-load-form) to instance. LOAD-INSTANCE retrieves this value.
Function (load-instance class &key pathname (if-does-not-exist t) verbose &allow-other-keys)

Load an instance from a file.

  • When IF-DOES-NOT-EXIST is non-nil (default), it checks the file existence.
  • When IF-DOES-NOT-EXIST is nil and the file does not exist, it calls MAKE-INSTANCE with the specified arguments.
  • When verbose is non-nil, it writes messages to the standard output.
  • It always checks if the loaded object is of the specified class.

1.3Related Work

1.3.1hu.dwim.serializer

http://quickdocs.org/hu.dwim.serializer/api

  • documentation : poor
  • tutorial : none
  • performance : unknown
  • scope / usability : all CLOS objects
    • cyclic references?
    • displaced arrays?

1.3.2trivial-hashtable-serialize

http://quickdocs.org/trivial-hashtable-serialize/

  • documentation : poor
  • tutorial : good
  • performance : unknown
  • scope / usability : hash table only

1.3.3cl-store

http://quickdocs.org/cl-store/

  • documentation : minimal
  • tutorial : none
  • performance : unknown. to a 32bit int stream
  • scope / usability : All CLOS class.
    • arrays?
    • cyclic references?
    • Exported slots can be customized, all slots by default.
    • Consideres the class slots.

1.3.4cl-marshall

http://quickdocs.org/cl-marshall/

  • documentation : minimal (source code) good (tutorial)
  • tutorial : good
  • performance : unknown. to a string that consists of a list
  • scope / usability :
    • needs to specify class-persistent-slots.
    • cyclic refernces?
    • displaced arrays?
    • Exported slots can be customized, needs to be specified for each class.
    • no consideration for class slots.

1.3.5persistent-variables

http://quickdocs.org/persistent-variables/

  • documentation :
  • tutorial :
  • performance :
  • scope / usability :
    • very specific. needs to be declared as defpvar

1.3.6userial

http://quickdocs.org/userial/

  • documentation :
  • tutorial :
  • performance :
  • scope / usability :
    • offers the versioning system
    • but heavily depends on ContextL.

1.3.7specialization-store

http://quickdocs.org/specialization-store/

  • documentation :
  • tutorial : there is an extensive tutorial, but the idea seems too complicated.
  • performance :
  • scope / usability :

1.4Dependencies

This library is at least tested on implementation listed below:
  • SBCL 1.4.12 on X86-64 Linux 4.4.0-142-generic (author's environment)

Also, it depends on the following libraries:

  • alexandria by Nikodemus Siivola <nikodemus@sb-studio.net>, and others. :Alexandria is a collection of portable public domain utilities.
  • closer-mop
  • bordeaux-threads

1.5Author, License, Copyright

Licensed under LGPL v3.

Copyright (c) 2019 Masataro Asai (guicho2.71828@gmail.com) Copyright (c) 2019 IBM Corporation

Dependencies (2)

  • alexandria
  • fiveam

Dependents (1)

  • GitHub
  • Quicklisp