Common Lisp Package: MANARDB

README:

manardb is a portable (across Lisps on Linux) memory mapped database for Common Lisp. It frees one from the garbage collector but stays within MOP.

See its homepage at MSI

Testing

> (asdf:operate 'asdf:load-op 'manardb-test) > (manardb.test:test-all-manardb) #<test-run: 24 tests, 3528843 assertions, 0 failures (0 expected) in 30.306 sec>

Credits

By MSI <http://www.msi.co.jp>

Thanks to Pascal Costanza for MOP conformance and other advice.

This project has found bugs in all Lisp implementations tested (Allegro, Lispworks, SBCL, and ClozureCL). Prize goes to SBCL for correctness as the bug (slot-value didn't work) was already fixed in a newer version.

FUNCTION

Public

CLEAN-MMAP-DIR (&OPTIONAL (DIR *MMAP-BASE-PATHNAME*))

Unsafely remove all temporary directories from failed transactions that were not cleaned up because the transactor crashed. [Not tested or used.]

CLOSE-ALL-MMAPS

Closes the datastore, unmapping and closing all files. Afterwards, a new datastore can be opened in a different locaiton.

COUNT-ALL-INSTANCES (CLASS)

Return a count of the number of instances of the class denoted by CLASS and any subclasses of it.

GC (ROOT-OBJECTS-SEQUENCE &KEY VERBOSE (COLLECT-AND-COMPACT T))

Do a full and precise garbage collection over all objects in the memory mapped system. If COLLECT-AND-COMPACT is true, then unused objeccts are removed. Uses at least two pointers of Lisp memory per object and more if objects are densely referenced. See REWRITE-GC for a sloppier alternative that does not need so much memory.

LISP-OBJECT-TO-MPTR (OBJ)

Stores the object OBJ in the memory mapped datastore and returns the mptr referring to it

LIST-TO-MARRAY (LIST)

Converts a Lisp list to a memory-mapped array object; nil is converted to nil

MAKE-MARRAY (LENGTH &KEY (INITIAL-ELEMENT NIL INITIAL-ELEMENT-P) (INITIAL-CONTENTS NIL INITIAL-CONTENTS-P) (MARRAY-CLASS 'MARRAY))

Create a new marray (memory-mapped array) structure in the datastore, similarly to make-array.

MAKE-MM-FIXED-STRING (LENGTH &KEY VALUE)

Create a fixed length string object of size LENGTH; stores into it the string in VALUE if given. A fixed length string allows string objects to be modified in the datastore without allocating more space.

MARRAY-REF (MARRAY I)

Like aref, but for memory mapped arrays

MARRAY-TO-LIST (MARRAY)

Converts a memory mapped array to a Lisp list; nil is converted to nil

MEQ (A B)

True iff either (eq a b) or A and B are both datastore objects representing the same object in the datastore.

MM-FIXED-STRING-VALUE (MFS)

The string stored in the fixed length string MFS. If the string was cropped, then append ... to the stored value. Can be set with setf. If the new value is too long then it will be silently cropped.

MPTR (OBJ)

If OBJ is already an integer, return it. If it is a memory mapped object, return the MPTR corresponding to it. Otherwise, raise an error.

MPTR-TO-LISP-OBJECT (MPTR)

Deference the object at location MPTR in the memory mapped datastore and create a Lisp representation of it.

OPEN-ALL-MMAPS

Maps the datastore into memory.

RETRIEVE-ALL-INSTANCES (CLASS)

Returns a list of all instances of CLASS.

REWRITE-GC (ROOT-OBJECTS-SEQUENCE &KEY PROGRESS VERBOSE SHARED-CLASSES (BASE-SHARED-CLASSES '(MM-SYMBOL)))

An alternative, sloppier GC algorithm with a space complexity that is not proportional to the size of the datastore. Creates a new datastore by copying each element of ROOT-OBJECTS-SEQUENCE as if it were entirely self contained except for any shared objects in SHARED-CLASSES. Cannot handle pointers to the inside of arrays at all; they will be recreated pointing to fresh objects. Note that arrays pointing to complex objects (or any user defined classes) are stored as arrays of mptrs, with each mptr pointing to the actual object; it is fine to have pointers to these objects, because the actual objects are not stored in the array.

USE-MMAP-DIR (DIR &KEY (IF-DOES-NOT-EXIST CREATE))

Set the memory mapped datastore to map files inside DIR.

WIPE-ALL-MMAPS

Delete all objects from all classes.

Undocumented

SETFMARRAY-REF (NEW MARRAY I)

SETFMM-FIXED-STRING-VALUE (STRING MFS)

Private

CLAUSE-FOR-IN-MARRAY-1 (&KEY ((FOR VAR)) ((IN-MARRAY SEQ)) ((FROM FROM)) ((UPFROM UPFROM)) ((DOWNFROM DOWNFROM)) ((TO TO)) ((DOWNTO DOWNTO)) ((ABOVE ABOVE)) ((BELOW BELOW)) ((BY BY) 1) ((WITH-INDEX WITH-INDEX)) GENERATE)

Elements of an marray

CLAUSE-FOR-INDEX-OF-MARRAY-2 (&KEY ((FOR VAR)) ((INDEX-OF-MARRAY SEQ)) ((FROM FROM)) ((UPFROM UPFROM)) ((DOWNFROM DOWNFROM)) ((TO TO)) ((DOWNTO DOWNTO)) ((ABOVE ABOVE)) ((BELOW BELOW)) ((BY BY) 1) ((WITH-INDEX WITH-INDEX)) GENERATE)

Indices of marray

METACLASS-ALLOCATOR-FORM (CLASS)

Returns a lambda-form that allocates a new object, and sets all memory mapped slots to their default values unless they are going to be overridden by the initargs

MTAGMAP-CLASS (INSTANCE)

@arg[extid]{A @class{extid}} @return[sytemid]{puri:uri or nil} Returns the System ID part of this External ID.

MTAGMAP-ELEM-LEN (INSTANCE)

@arg[extid]{A @class{extid}} @return[sytemid]{puri:uri or nil} Returns the System ID part of this External ID.

MTAGMAP-FD (INSTANCE)

@arg[extid]{A @class{extid}} @return[sytemid]{puri:uri or nil} Returns the System ID part of this External ID.

MTAGMAP-INSTANTIATOR (INSTANCE)

@arg[extid]{A @class{extid}} @return[sytemid]{puri:uri or nil} Returns the System ID part of this External ID.

MTAGMAP-LAYOUT (INSTANCE)

@arg[extid]{A @class{extid}} @return[sytemid]{puri:uri or nil} Returns the System ID part of this External ID.

MTAGMAP-LEN (INSTANCE)

@arg[extid]{A @class{extid}} @return[sytemid]{puri:uri or nil} Returns the System ID part of this External ID.

MTAGMAP-PTR (INSTANCE)

@arg[extid]{A @class{extid}} @return[sytemid]{puri:uri or nil} Returns the System ID part of this External ID.

MTAGMAP-WALKER (INSTANCE)

@arg[extid]{A @class{extid}} @return[sytemid]{puri:uri or nil} Returns the System ID part of this External ID.

SHRINK-ALL-MMAPS

Truncate all mmaps to smallest size (rounded up to the nearest page) which can contain all their data.

Undocumented

ALWAYS-TRUE (&REST ARGS)

ASSERT-CLASS-SLOT-LAYOUT (CLASS LAYOUT &KEY (FINALIZE T))

BOX-ARRAY (OBJECT)

BOX-CONS (CONS)

BOX-OBJECT (OBJECT)

BOX-STRING (STRING)

BOX-SYMBOL (OBJECT)

BOX-SYMBOL-MISS (OBJECT)

BOXED-BYTE-ARRAY-BOXER (ARRAY)

BOXED-DOUBLE-FLOAT-ARRAY-BOXER (ARRAY)

BOXED-SIGNED-ARRAY-BOXER (ARRAY)

BOXED-SINGLE-FLOAT-ARRAY-BOXER (ARRAY)

BOXED-UNSIGNED-ARRAY-BOXER (ARRAY)

BUILD-VERSION (&OPTIONAL (COUNTER 0))

CHECK-ALLOCATE-OKAY

CHECK-MMAP-TRUNCATE-OKAY

CHECK-SCHEMA (&OPTIONAL (DIR *MMAP-PATHNAME-DEFAULTS*))

CLEAR-CACHES

CLEAR-CACHES-HARD

COPY-ALL-MMAPS (FROM-DIR TO-DIR)

COPY-MTAGMAP (INSTANCE)

ENSURE-FINALIZE-INHERITANCE (CLASS)

ENSURE-INHERITS-FROM-MM-OBJECT (CLASS NEXT-METHOD ALL-KEYS)

FD-FILE-LENGTH (FD)

FINALIZE-ALL-MMAPS

FORCE-CLASS (CLASS-SPECIFER)

FORCE-MPTR (OBJ)

FORCE-TAG (OBJ)

GC-CALC-NEW-OFFSETS (MTAGMAP TABLE)

GC-COMPACT (OFFSETS-TABLE)

GC-REWRITE-POINTERS-AND-COMPACT (REFS-TABLE)

GENERAL-BOX-ARRAY (ARRAY)

GENERATE-BOXED-NUMERIC-TYPE (NAME &KEY TYPE)

GENERATE-BOXER (TYPES)

INSTANTIATE-DEFAULT-MM-OBJECT (MPTR)

INTERNAL-BOX-STRING (ARRAY)

LAYOUT-COMPATIBLE-P (A B)

MAINDIR

MAKE-MPTR (TAG INDEX)

MAKE-MTAGMAP (&KEY ((FD DUM96) -1) ((PTR DUM97) (NULL-POINTER)) ((LEN DUM98) 0) ((CLASS DUM99) NIL) ((LAYOUT DUM100) NIL) ((INSTANTIATOR DUM101) NIL) ((WALKER DUM102) NIL) ((ELEM-LEN DUM103) 0))

METACLASS-DEFAULT-INSTANTIATOR-FORM (CLASS)

METACLASS-DEFAULT-WALKER-FORM (CLASS)

MM-EFFECTIVE-SLOT-DEFINITION-LAMBDA-FORMS (SLOTD)

MM-EFFECTIVE-SLOT-DEFINITION-SETUP (SLOTD)

MM-FIXED-STRING-STORE (MFS STRING)

MM-FIXED-STRING-UNCROPPER (STRING ORIGINAL-LENGTH)

MM-METACLASS-ALLOC (CLASS &OPTIONAL (AMOUNT 1))

MM-METACLASS-CUSTOM-FUNCTION (CLASS SLOT &OPTIONAL (DEFAULT-SLOT (LET ((*PACKAGE* #<PACKAGE "MANARDB">)) (SYMBOLICATE 'DEFAULT- SLOT))))

MM-METACLASS-FILENAME (CLASS)

MM-METACLASS-INITIALIZE-ALLOC (CLASS INSTANCE INITARGS)

MM-METACLASS-PATHNAME (CLASS)

MM-METACLASS-SCHEMA (CLASS)

MM-METACLASS-SLOT-LAYOUT (CLASS)

MM-OBJECT-POINTER (MM-OBJECT)

MM-SLOT-OFFSET (CLASS SLOTNAME)

MM-SUBCLASSES (CLASS)

MPOINTER (MTAG MINDEX)

MPTR-INDEX (MPTR)

MPTR-POINTER (MPTR)

MPTR-TAG (MPTR)

MTAGMAP (MTAG)

SETFMTAGMAP (VAL MTAG)

MTAGMAP-ALLOC (MTAGMAP BYTES)

MTAGMAP-BYTE (MTAGMAP INDEX)

SETFMTAGMAP-BYTE (VAL MTAGMAP INDEX)

MTAGMAP-CHECK (M)

MTAGMAP-CHECK-INVERT (MTAGMAP)

MTAGMAP-CHECK-READ (MTAGMAP)

MTAGMAP-CHECK-WRITE (MTAGMAP)

SETFMTAGMAP-CLASS (NEW-VALUE INSTANCE)

MTAGMAP-CLOSE (MTAGMAP)

MTAGMAP-CLOSED-P (MTAGMAP)

MTAGMAP-COUNT (MTAGMAP)

MTAGMAP-DEFAULT-FILENAME (MTAGMAP)

MTAGMAP-DETACH (MTAGMAP)

SETFMTAGMAP-ELEM-LEN (NEW-VALUE INSTANCE)

MTAGMAP-ELEM-POS (MTAGMAP INDEX)

MTAGMAP-ELEM-POS-TO-INDEX (MTAGMAP POS)

MTAGMAP-EXTEND-ALLOC (MTAGMAP BYTES)

SETFMTAGMAP-FD (NEW-VALUE INSTANCE)

MTAGMAP-FILE-LENGTH (MTAGMAP)

MTAGMAP-FINALIZE (M)

MTAGMAP-FIRST-INDEX (MTAGMAP)

SETFMTAGMAP-INSTANTIATOR (NEW-VALUE INSTANCE)

MTAGMAP-LAST-INDEX (MTAGMAP)

SETFMTAGMAP-LAYOUT (NEW-VALUE INSTANCE)

SETFMTAGMAP-LEN (NEW-VALUE INSTANCE)

MTAGMAP-OPEN (MTAGMAP &KEY (FILE (MTAGMAP-DEFAULT-FILENAME MTAGMAP)) (MIN-BYTES 0) (SHARING *MMAP-SHARING*) (PROTECTION *MMAP-PROTECTION*) (FINALIZE T))

MTAGMAP-P (OBJECT)

SETFMTAGMAP-PTR (NEW-VALUE INSTANCE)

MTAGMAP-RESIZE (MTAGMAP NEW-LEN)

MTAGMAP-SCHEMA (MTAGMAP)

MTAGMAP-SHRINK (MTAGMAP)

SETFMTAGMAP-WALKER (NEW-VALUE INSTANCE)

MTAGMAP-WORD (MTAGMAP WINDEX)

SETFMTAGMAP-WORD (VAL MTAGMAP WINDEX)

NEXT-AVAILABLE-TAG

PATHNAME-TO-SCHEMA (DIRNAME)

PATHNAME-TO-SPECIAL-FILE (DIRNAME FILENAME)

PTR (OBJECT)

READ-SCHEMA (FILENAME)

REPLACE-ALL-MMAPS (FROM-DIR TO-DIR VERSION)

REWRITE-GC-CLEANUP (NEW-MTAGMAPS NEW-FILES)

REWRITE-GC-COPY-ONE-ROOT (MPTR SHARED-TABLES NEW-MTAGMAPS)

REWRITE-GC-WALK (ROOT-OBJECTS-SEQUENCE SHARED-TABLES NEW-MTAGMAPS &KEY PROGRESS)

ROUND-UP-TO-PAGESIZE (BYTES)

SCHEMA

SCHEMA-SUPERSET-P (STORED-SCHEMA OUR-SUPPORTED-SCHEMA)

SETUP-DEFAULT-METACLASS-FUNCTIONS (CLASS)

SETUP-MTAGMAP-FOR-METACLASS (CLASS)

SINGLE-EXPRESSION-FILE (FILENAME)

SETFSINGLE-EXPRESSION-FILE (VALUE FILENAME)

SLOT-DEFINITION-INITFORM-MM-ZEROP (SLOTD)

SLOT-DEFINITION-MM-BOXING (SLOTD)

SLOT-DEFINITION-MM-READ-CONVERTOR (SLOTD)

SLOT-DEFINITION-MM-READ-FORM (SLOTD RAW-ACCESS-FORM)

SLOT-DEFINITION-MM-TYPE (SLOTD)

SLOT-DEFINITION-MM-WRITE-CONVERTOR (SLOTD)

SLOT-DEFINITION-MM-WRITE-FORM (SLOTD RAW-WRITE-FORM NEW-VAL-SYM)

SPECIALIZED-CLASS-ARRAY-BOXER-NAME (CLASSNAME)

STORED-CFFI-TYPE (TYPE)

STORED-TYPE-SIZE (TYPE)

STR (&REST ARGS)

TAG-GENERAL-UNBOX-ARRAY (TAG INDEX)

TMPDIR

TRANSACT (&KEY BODY ON-RESTART MESSAGE)

TREE-TO-ATOMS-OR-STRINGS (TREE)

UNBOX-ARRAY (INDEX)

UNBOX-ARRAY-AS-LIST (INDEX)

UNBOX-ARRAY-INTERNAL (ELEM-TAG ELEM-INDEX LEN)

UNBOX-ARRAY-INTERNAL-GENERAL (ELEM-TAG ELEM-INDEX LEN)

UNBOX-BOX (INDEX)

UNBOX-BOXED-BYTE (INDEX)

UNBOX-BOXED-DOUBLE-FLOAT (INDEX)

UNBOX-BOXED-SIGNED (INDEX)

UNBOX-BOXED-SINGLE-FLOAT (INDEX)

UNBOX-BOXED-UNSIGNED (INDEX)

UNBOX-CONS (INDEX)

UNBOX-STRING (INDEX)

UNBOX-SYMBOL (INDEX)

VERSION-EQUALP (A B)

WALK-ARRAY (MPTR FUNC)

WRITE-SCHEMA (FILENAME)

MACRO

Public

DEFMMCLASS (NAME DIRECT-SUPERS DIRECT-SLOTS &REST OPTIONS)

Define a memory mapped class, like defclass. Automatically adds :metaclass mm-metaclass to options, if it is not present, finalizes the class immediately, and puts in an assertion that the class layout in the loaded datastore is compatible.

DIRECT-SLOT-NUMERIC-MAREF (CLASS OBJECT SLOT ELEMENT-TYPE INDEX)

Access element INDEX of an array of ELEMENT-TYPE that is stored in slot SLOT of OBJECT, which is an instance of class CLASS, without instantiating the array into the memory of the host Lisp implementation.

DOCLASS ((VAR CLASS-SPECIFIER &KEY FRESH-INSTANCES REVERSE) &BODY BODY)

For each object in the memory-mapped datastore of class denoted by CLASS-SPECIFIER (evaluated), lexically bind VAR to a Lisp object representing that object around BODY and execute it. FRESH-INSTANCES (generalized boolean, not evaluated), if true means means that a fresh Lisp object will be created for each datastore object -- by default a single Lisp object is instantiated and it is modified destructively to point to each object in the class. REVERSE (generalized boolean, not evaluated), if true means that objects will be iterated in order from newest to oldest. If false (default), they are iterated from oldest to newest. Also see dosubclasses.

DOSUBCLASSES ((VAR CLASS-SPECIFIER &REST OPTIONS) &BODY BODY)

For the class itself and each subclass of the class denoted by CLASS-SPECIFIER (evaluated) run doclass.

WITH-CACHED-SLOTS (SLOTS INSTANCE &BODY BODY)

Like with-slots, but each slot is only read from the datastore once. It is written to the datastore immediately after every write, and the cached version becomes the value written (not the value as serialised and deserialised). This is an optimization to stop repeatedly instantiating slots into Lisp memory. Note that it also useful because it preserves non-persistent slots of objects stored in SLOTS of INSTANCE over their lexical scope.

WITH-OBJECT-CACHE ((NAME &KEY (TEST ''EQUAL)) &BODY BODY)

Lexically bind a function with NAME for BODY that, when passed an object, will either instantiate a new memory mapped object for it, or if the object is equal under TEST to a previous object passed to NAME, will return the same memory mapped object.

WITH-TRANSACTION ((&KEY MESSAGE ON-RESTART) &BODY BODY)

Copy the datastore to a temporary directory; open the datastore from this temporary directory and execute BODY. If, at the end of BODY, the main datastore has had another transaction on it, then run ON-RESTART and restart the procedure. Otherwise atomically replace the main datastore. Should be safe, as it uses lockfiles. The initial copy will retry if a transaction occurs while it is being performed. It is slow copying the datastore. (A better copy than alexandria:copy-file should be easy to implement.) The proposed reflink(2) system call would make a radically more efficient implementation possible.

Private

Undocumented

CASSERT (TEST-FORM &OPTIONAL PLACES STRING &REST ARGS)

CHECK-CLASS-SLOT-LAYOUT (CLASSNAME &OPTIONAL (LAYOUT (MM-METACLASS-SLOT-LAYOUT (FIND-CLASS CLASSNAME))))

D (MACHINE-POINTER &OPTIONAL (INDEX 0) (TYPE '(UNSIGNED-BYTE 8)))

DEFINE-BOX-ARRAY (ARRAY-BOXER-NAME BOX-CLASS LISP-TYPE &KEY CONVERTOR (ARRAY-CLASS 'MM-ARRAY))

DEFINE-BOXED-NUMERIC-TYPES (&REST TYPESPECS)

DEFINE-LISP-OBJECT-TO-MPTR

DEFUN-SPEEDY (NAME LAMBDA-LIST &BODY BODY &ENVIRONMENT ENV)

DIR-REPLACEMENT-TARGET (PATHNAME)

DIR-SCHEMA (PATHNAME)

DIR-VERSION (PATHNAME)

DIRECT-SLOT-MPTR (CLASS OBJECT SLOT)

DW (MACHINE-POINTER &OPTIONAL (INDEX 0))

MM-INSTANTIATOR-FOR-TAG (MTAG)

MTAGMAP-NEXT (MTAGMAP)

PROP-FOR-MM-SYMBOL (SYM)

SET-DIRECT-SLOT-MPTR (CLASS OBJECT SLOT NEW-VALUE)

SET-DIRECT-SLOT-NUMERIC-MAREF (CLASS OBJECT SLOT ELEMENT-TYPE INDEX NEW-VALUE)

WITH-CONSTANT-TAG-FOR-CLASS ((TAGSYM CLASSNAME) &BODY BODY)

WITH-POINTER-SLOTS (SLOTNAMES (OBJECT-POINTER CLASSNAME) &BODY BODY)

WITH-RAW-SLOT ((SLOTNAME CLASSNAME &KEY (ACCESSOR-NAME SLOTNAME)) OBJECT-POINTER &BODY BODY &ENVIRONMENT ENV)

GENERIC-FUNCTION

Public

LISP-OBJECT-TO-MPTR-IMPL (OBJECT)

Override this generic function to give an user-defined class an alternative serialisation in the memory mapped datastore. Return the mptr pointing to this serialisation. Note that the serialisation for builtin types are inlined and cannot be affected.

SLOT-ACCESSOR

Public

Undocumented

MARRAY-LENGTH (OBJECT)

Private

Undocumented

%PTR (OBJECT)

SETF%PTR (NEW-VALUE OBJECT)

MARRAY-BASE (OBJECT)

MM-INSTANTIATOR (OBJECT)

SETFMM-INSTANTIATOR (NEW-VALUE OBJECT)

MM-METACLASS-LEN (OBJECT)

SETFMM-METACLASS-LEN (NEW-VALUE OBJECT)

MM-METACLASS-MTAGMAP (OBJECT)

SETFMM-METACLASS-MTAGMAP (NEW-VALUE OBJECT)

MM-METACLASS-TAG (OBJECT)

MM-SLOT-DEFINITION-OFFSET (OBJECT)

SLOT-DEFINITION-MEMORY-MAPPED (SLOTD)

SLOT-DEFINITION-MMAP-POINTER-P (SLOTD)

SETFSLOT-DEFINITION-MMAP-POINTER-P (NEW-VALUE OBJECT)

SLOT-DEFINITION-READER-FUNCTION (OBJECT)

SETFSLOT-DEFINITION-READER-FUNCTION (NEW-VALUE OBJECT)

SLOT-DEFINITION-WRITER-FUNCTION (OBJECT)

SETFSLOT-DEFINITION-WRITER-FUNCTION (NEW-VALUE OBJECT)

VARIABLE

Public

*MMAP-MAY-ALLOCATE*

If this is not true, and an attempt is made to extend a memory mapped region, an error will be signalled.

Private

Undocumented

*MM-FIXED-STRING-UNCROPPER*

*MMAP-PROTECTION*

*MMAP-SHARING*

*MOP-HACK-EFFECTIVE-SLOT-DEFINITION-CLASS*

*MTAGMAPS*

*STORED-SYMBOLS*

*TRANSACTION-COPY-FAIL-RESTART-SLEEP*

CLASS

Public

MARRAY

The base representation of a memory-mapped vector.

MM-METACLASS

Metaclass for memory mapped objects.

MM-OBJECT

Base class for all memory mapped objects.

Private

Undocumented

BOXED-BYTE

BOXED-DOUBLE-FLOAT

BOXED-SIGNED

BOXED-SINGLE-FLOAT

BOXED-UNSIGNED

MM-ARRAY

MM-ARRAY-AS-LIST

MM-BOX

MM-CONS

MM-DIRECT-SLOT-DEFINITION

MM-EFFECTIVE-SLOT-DEFINITION

MM-FIXED-STRING

MM-SLOT-DEFINITION

MM-STRING

MM-SYMBOL

MTAGMAP (MTAG)

CONSTANT

Private

Undocumented

+MINDEX-BITS+

+MPTR-BITS+

+MTAG-BITS+

+MTAGS+

+WORD-LENGTH+