SWI-Prolog atoms 
as well as strings can represent arbitrary binary data of arbitrary 
length. This facility is attractive for storing foreign data such as 
images in an atom. An atom is a unique handle to this data and the atom 
garbage collector is able to destroy atoms that are no longer referenced 
by the Prolog engine. This property of atoms makes them attractive as a 
handle to foreign resources, such as Java atoms, Microsoft's COM 
objects, etc., providing safe combined garbage collection.
To exploit these features safely and in an organised manner, the 
SWI-Prolog foreign interface allows creating‘atoms’with 
additional type information. The type is represented by a structure 
holding C function pointers that tell Prolog how to handle releasing the 
atom, writing it, sorting it, etc. Two atoms created with different 
types can represent the same sequence of bytes. Atoms are first ordered 
on the rank number of the type and then on the result of the
compare() 
function. Rank numbers are assigned when the type is registered. This 
implies that the results of inequality comparisons between blobs of 
different types is undefined and can change if the program is run twice 
(the ordering within a blob type will not change, of course).
While the blob is alive, neither its handle nor the location of the 
contents (see PL_blob_data()) 
change. If the blob's type has the
PL_BLOB_UNIQUE feature, the content of the blob must remain 
unmodified. If the blob's type does not have the
PL_BLOB_UNIQUE feature multiple instances of this blob type 
may contain the same data. The blob handle (atom_t) 
is reclaimed only by the atom garbage collector. The blob's
content (data) is normally reclaimed when the garbage collector 
reclaims the blob. If the blob's type defines the
release() 
function, this function is called. This hook may deal with side effects 
and is responsible of releasing the data if the blob's type has the PL_BLOB_NOCOPY 
flag. The content of a
PL_BLOB_NOCOPY blob may be released before the blob itself 
can be garbage collected using PL_free_blob(). 
This immediately triggers the release() 
function. After PL_free_blob() 
has reclaimed the content, this function will not be called when the atom_t 
handle is reclaimed. An atom_t handle may be reused for a 
new atom or blob after it has been garbage collected.
If foreign code stores the atom_t handle in some 
permanent location it must make sure the handle is registered 
to prevent it from being garbage collected. If the handle is obtained 
from a
term_t object it is not registered because it is 
protected by the term_t object. This applies to e.g.,
PL_get_atom(). 
Functions that create a handle from data, such as
PL_new_atom(), 
return a registered handle to prevent the asynchronous atom garbage 
collector from reclaiming it immediately. Note that many of the API 
functions create an atom or blob handle and use this to fill a
term_t object, e.g., PL_unify_blob(), PL_unify_chars(), 
etc. In this scenario the handle is protected by the term_t 
object. Registering and unregistering atom_t handles is 
done by
PL_register_atom() 
and PL_unregister_atom().
Note that during program shutdown using PL_cleanup(), 
all atoms and blobs are reclaimed as described above. These objects 
are reclaimed regardless of their registration count. The order in which 
the atoms or blobs are reclaimed under PL_cleanup() 
is undefined. However, when these objects are reclaimed using garbage_collect_atoms/0, 
registration counts are taken into account.
The type PL_blob_t represents a structure with the 
layout displayed below. The structure contains additional fields at the 
... for internal bookkeeping as well as future extensions.
typedef struct PL_blob_t
{ uintptr_t     magic;          /* PL_BLOB_MAGIC */
  uintptr_t     flags;          /* Bitwise or of PL_BLOB_* */
  const char *  name;           /* name of the type */
  int           (*release)(atom_t a);
  int           (*compare)(atom_t a, atom_t b);
  int           (*write)(IOSTREAM *s, atom_t a, int flags);
  void          (*acquire)(atom_t a);
  int           (*save)(atom_t a, IOSTREAM *s);
  atom_t        (*load)(IOSTREAM *s);
  ...
} PL_blob_t;
For each type, exactly one such structure should be allocated and 
must not be moved because the address of the structure determines the 
blob's "type". Its first field must be initialised to PL_BLOB_MAGIC. 
If a blob type is registered from a loadable object (shared object or 
DLL) the blob type must be deregistered using PL_unregister_blob_type() 
before the object may be released.
The flags is a bitwise or of the following 
constants:
- PL_BLOB_TEXT
- If specified, the blob is assumed to contain text and is considered a 
normal Prolog atom. The (currently) two predefined blob types that 
represent atoms have this flag set. User-defined blobs may not specify 
this, even if they contain only text. Applications should not 
use the blob API to create normal text atoms or get access to the text 
represented by normal text atoms. Most applications should use
PL_get_nchars() 
and PL_unify_chars() 
to get text from Prolog terms or create Prolog terms that represent 
text.
- PL_BLOB_UNIQUE
- If specified the system ensures that the blob-handle is a unique 
reference for a blob with the given type, length and content. If this 
flag is not specified, each lookup creates a new blob. Uniqueness is 
determined by comparing the bytes in the blobs unless
PL_BLOB_NOCOPYis also specified, in which case the 
pointers are compared. Note that the lookup does not use the 
blob's compare function when testing for equality, but only tests the 
bytes; this means that terms from the recorded database or C++-style 
strings will typically not compare as equal when doing blob lookup.
- PL_BLOB_NOCOPY
- By default the content of the blob is copied. Using this flag the blob 
references the external data directly. The user must ensure the provided 
pointer is valid as long as the atom lives. If PL_BLOB_UNIQUEis also specified, uniqueness is determined by comparing the pointer 
rather than the data pointed at. UsingPL_BLOB_UNIQUE|
can 
be used to make a blob reference an arbitrary pointer where the pointer 
data may be reclaimed in the release() 
handler.
- PL_BLOB_WCHAR
- If PL_BLOB_TEXTis also set, then the text is made up ofpl_wchar_titems and the blob's lenght is the number of 
bytes (that is, the number of characters timessizeof(pl_wchar_t)). 
AsPL_BLOB_TEXT, this flag should not be set in 
user-defined blobs.
The name field represents the type name as available to 
Prolog. See also current_blob/2. 
The other fields are function pointers that must be initialised to 
proper functions or NULL to get the default behaviour of 
built-in atoms. Below are the defined member functions:
- void acquire(atom_t 
a)
- Called if a new blob of this type is created through PL_put_blob(),
PL_unify_blob(), 
or PL_new_blob(). 
Note this this call is done as part of creating the blob. The call to PL_unify_blob() 
may fail if the unification fails or cannot be completed due to a 
resource error. PL_put_blob() 
has no such error conditions. This callback is typically used to store 
the
atom_thandle into the content of the blob. Given a pointer 
to the content, we can now use PL_unify_atom() 
to bind a Prolog term with a reference to the pointed to object. If the 
content of the blob can be modified (PL_BLOB_UNIQUEis not 
present) this is the only way to get access to theatom_thandle that belongs to this blob. IfPL_BLOB_UNIQUEis provided and respected, PL_unify_blob() 
given the same pointer and length will produce the sameatom_thandle.
- int release(atom_t 
a)
- The blob (atom) a is about to be released. The
release() 
function is called when the atom is reclaimed by the atom garbage 
collector, when an explicit call to PL_free_blob() 
is made or during shutdown of Prolog. This function can retrieve the 
data of the blob using PL_blob_data(). 
If the release() 
function returns FALSE, the atom garbage collector will
not reclaim the atom. For critical resources such as file 
handles or significant memory resources, it may be desirable to have an 
explicit call to dispose (most of) the resources. For example,
close/1 
reclaims the file handle and most of the resources associated with a 
stream, leaving only a tiny bit of content to the garbage collector. See 
also setup_call_cleanup/3.
The release() 
callback is called in the context of the thread executing the atom 
garbage collect, the thread executing
PL_free_blob() 
or the thread initiating the shutdown. Normally the thread gcruns all atom and clause garbage collections. The
release() 
function may not call any of the PL_*() functions except for
PL_blob_data() 
or PL_unregister_atom() 
to unregister other atoms that are part data associated to the blob. 
Calling any of the other PL_* functions may result in deadlocks or 
crashes. The release() 
function should not call any potentially slow or blocking functions as 
this may cause serious slowdowns in the rest of the system.
 
Blobs that require cleanup that is slow, blocking or requires calling 
Prolog must pass the data to be cleaned to another thread. Be aware that 
if the blob uses PL_BLOB_NOCOPYthe user is responsible for 
discarding the data, otherwise the atom garbage collector will free the 
data.
 
As SWI-Prolog atom garbage collector is conservative, there 
is no guarantee that the release() 
function will ever be called. If it is important to clean up some 
resource, there should be an explicit predicate for doing that, and 
calling that predicate should be guaranteed by using setup_call_cleanup/3 
or some a process finalization hook such as at_halt/1.
 
Normally, Prolog does not clean memory during shutdown. It does so on 
an explicit call to PL_cleanup().225Or 
if the system is compiled with the cmake build type Debug. 
In such a situation, there is no guarantee of the order in which atoms 
are released; if a blob contains an atom (or another blob), those atoms 
(or blobs) may have already been released. See also
PL_blob_data().
 
- int compare(atom_t 
a, atom_t b)
- Compare the blobs a and b, both of which are of 
the type associated to this blob type. Return values are as memcmp(): < 
0 if
a is less than b, = 0 if both are 
equal, and > 0 otherwise. The default implementation is a 
bitwise comparison of the blobs’contents. This default 
implementation suffices if
PL_BLOB_UNIQUEis set and the blob follows the requirement 
that its contents do not change, although it might give an unexpected 
ordering, and the ordering may change if the blob is saved and restored 
using save_program/2.
If the compare() 
function is defined, the sort/2 
predicate uses that to determine if two blobs are equal and only keeps 
one of them. This can cause unexpected results with blobs that are 
actually different; if you cannot guarantee that the blobs all have 
unique contents, then you should incorporate the blob address (the 
system guarantees that blobs are not shifted in memory after they are 
allocated). This function should not call any PL_*() functions other 
than PL_blob_data().
 
The following minimal compare function gives a stable total ordering:
 
static int
compare_my_blob(atom_t a, atom_t b)
{ const struct my_blob_data *blob_a = PL_blob_data(a, NULL, NULL);
  const struct my_blob_data *blob_b = PL_blob_data(b, NULL, NULL);
  return (blob_a < blob_b) ? -1 : (blob_a > blob_b) ? 1 : 0;
}
- int write(IOSTREAM 
*s, atom_t a, int flags)
- Write the content of the blob a to the stream s 
respecting the flags. The return value is TRUEorFALSEand does not follow the Unix convention 
of the number of bytes (where zero is possible) and negative for errors. 
Any I/O operations to
s are in the context of a PL_acquire_stream(); 
upon return, the PL_release_stream() 
handles any errors, so it is safe to not check return codes from Sfprintf(), 
etc.
In general, the output from the write() 
callback should be minimal. If you wish to output more debug 
information, it is suggested that you either add a debug option to your 
"open" predicate to output more information, or provide a "properties" 
predicate. A typical implementation is:
 
static int write_my_blob(IOSTREAM *s, atom_t symbol, int flags)
{ (void)flags; /* unused */
  Sfprintf(s, "<my_blob>(%p)", PL_blob_data(symbol, NULL, NULL));
  return TRUE;
}
The flags are a bitwise or of zero or more of the
PL_WRT_*flags that were passed in to the calling
PL_write_term() 
that called write(), 
and are defined inSWI-Prolog.h. The flags do not have thePL_WRT_NEWLINEbit set, so it is safe to call PL_write_term() 
and there is no need for writing a trailing newline. This prototype is 
available if theSWI-Stream.his included beforeSWI-Prolog.h. This function can retrieve the data of the 
blob using PL_blob_data().
 
Most blobs reference some external data identified by a pointer and 
the
write() function 
writes
<type>(address). 
If this function is not provided, write/1 
emits the content of the blob for blobs of typePL_BLOB_TEXTor a string of the format<#hex 
data>for binary blobs.
 
- int save(atom_t 
a, IOSTREAM *s)
- Write the blob to stream s, in an opaque form that is known 
only to the blob. If a “save” function is not provided (that 
is, the field is NULL), the default implementation saves 
and restores the blob as if it is an array of bytes which may contain 
null (’
 0’
) bytes.
SWI-Stream.hdefines a number of PL_qlf_put_*() 
functions that write data in a machine-independent form that can be read 
by the corresponding PL_qlf_get_*() functions.
 
If the “save” function encounters an error, it should 
call
PL_warning(), 
raise an exception (see PL_raise_exception()), 
and return FALSE.226Details 
are subject to change. Note that failure to save/restore a 
blob makes it impossible to compile a file that contains such a blob 
using qcompile/2 
as well as creating a
saved state from a program that contains such a blob 
impossible. Here, contains means that the blob appears in a 
clause or directive.
 
- atom_t load(IOSTREAM 
*s)
- Read the blob from its saved form as written by the “save” function 
of the same blob type. If this cannot be done (e.g., a stream read 
failure or a corrupted external form), the “load” function 
should call PL_warning(), 
then PL_fatal_error(), 
and return constFALSE.227Details 
are subject to change; see the “save” function. 
If a “load” function is not provided (that is, the field is NULL, 
the default implementation assumes that the blob was written by the 
default “save” - that is, as an array of bytes
SWI-Stream.hdefines a number of PL_qlf_get_*() 
functions that read data in a machine-independent form, as written by 
the by the corresponding PL_qlf_put_*() functions.
 
The atom that the “load” function returns can be created 
using
PL_new_blob(). 
- bool PL_unregister_blob_type(PL_blob_t 
*type)
- Unlink the blob type from the registered type and transform the type of 
possible living blobs to unregistered, avoiding further 
reference to the type structure, functions referred by it, as well as 
the data. This function returnsTRUEif no blobs of this 
type existed andFALSEotherwise. PL_unregister_blob_type() 
is intended for the uninstall() hook of foreign modules, avoiding 
further references to the module.
- int PL_register_blob_type(PL_blob_t 
*type)
- This function does not need to be called explicitly. It is called if 
needed when a blob is created by PL_unify_blob(), PL_put_blob(), 
or
PL_new_blob().
The blob access functions are similar to the atom accessing 
functions. Blobs being atoms, the atom functions operate on blobs and 
vice versa. For clarity and possible future compatibility issues, 
however, it is not advised to rely on this.
- bool PL_is_blob(term_t 
t, PL_blob_t **type)
- Succeeds if t refers to a blob, in which case type 
is filled with the type of the blob.
- bool PL_unify_blob(term_t 
t, void *blob, size_t len, PL_blob_t *type)
- Unify t to a blob constructed from the given data and 
associated with the given type. This performs the following steps:
 
- If the type has PL_BLOB_UNIQUEset, search 
the blob database for a blob of the same type with the same content. If 
found, unify t with the existing handle.
- If not found or PL_BLOB_UNIQUEis not set, create a new 
blob handle. IfPL_BLOB_NOCOPYis set, associate it to the 
given memory; else, copy the memory to a new area owned by the blob. 
Call the acquire() 
function of the type.
- Unify t with the existing or new handle. This succeeds if t 
is already bound to the existing blob handle. If
t is a variable, it succeeds if sufficient resources 
are available to perform the unification; if t is bound to 
something else, this fails.
 
It is possible that a blob referencing critical resources is created 
after which the unification fails. Typically these resources are 
eventually reclaimed because the new blob is not referenced and 
reclaimed by the atom garbage collector. As described with the
release() 
function, it can be desirable to reclaim the critical resources after 
the failing PL_unify_blob() 
call. 
- bool PL_put_blob(term_t 
t, void *blob, size_t len, PL_blob_t *type)
- Store the described blob in t. The return value indicates 
whether a new blob was allocated (FALSE) or the blob is a 
reference to an existing blob (TRUE). Reporting 
new/existing can be used to deal with external objects having their own 
reference counts. If the return isTRUEthis reference 
count must be incremented, and it must be decremented on blob 
destruction callback. See also
PL_put_atom_nchars().
- atom_t PL_new_blob(void 
*blob, size_t len, PL_blob_t *type)
- Create a blob from its internal opaque form. This function is intended 
for the “load” function of a blob.
- bool PL_get_blob(term_t 
t, void **blob, size_t *len, PL_blob_t **type)
- If t holds a blob or atom, get the data and type and return
TRUE. Otherwise returnFALSE. Each result 
pointer may beNULL, in which case the requested 
information is ignored.
- void * PL_blob_data(atom_t 
a, size_t *len, PL_blob_t **type)
- Get the data and type associated to a blob. This function is mainly used 
from the callback functions described in section 
12.4.10.1. Note that if the release() 
hook is called from PL_cleanup(), 
blobs are released regardless of whether or not they are referenced and 
the order in which blobs are released is undefined (the order depends on 
the ordering in the atom hash table). PL_blob_data() 
may be called safely on a blob that has already been released. If this 
happens during PL_cleanup() 
the return value is guaranteed to be NULL. During normal 
execution it may return the content of a newly allocated blob that 
reuses the released handle.
- bool PL_free_blob(atom_t 
blob)
- New in 9.1.12. This function may be used on blobs with the
PL_BLOB_NOCOPYflag set and the blob type implements the
release() 
callback. It causes the release() 
callback to be called, after which the data and size are set to 0 if the release() 
returnsTRUE. After this sequence, the release() 
for this blob is never called again. The relatedatom_thandle remains valid until it is no longer referenced and reclaimed by 
the atom garbage collector. If the blob data is accessed using e.g., PL_get_blob() 
it returnsNULLfor the data and 0 for the size.228This 
means that any predicates or callbacks that use the blob must check the 
result of PL_blob_data(). 
If the release() 
function is not called, or if it returnsFALSE,FALSEis returned.
PL_free_blob() 
may be called multiple times on the same
atom_t, provided the handle is still valid. Subsequent 
calls after a successful call have no effect and returnFALSE.
 
The blob API assumes that Prolog will take care of memory management, 
using the release(c)allback 
to handle any cleanup.
Other programming languages have their own memory management, which 
might not fit nicely with the Prolog memory management. For more details 
on blobs written with C++, see
C++ 
interface to SWI-Prolog (Version 2).