Creates an object mapped to a file. The object is persistent
and outlives the parent application. The file may be reopened
and the object reused, it's value persists. This might be viewed
as kind of binary serialization with the difference that access
to the variable is almost as fast as any regular memory. The
structure might be also used as memory shared between processes/threads,
however, no built-in synchronization is provided.
If the template parameter T is immutable, the created object is also
immutable. Note: shared memory is then created in read-only
mode, so even cast to mutable and attempt to modify it will cause
segmentation fault.
The Perpetual(T) exists in two forms. When T is regular value type
(including static arrays), Persistent(T) behaves like reference to
the object. In the second form, Persistent(T[]), the class behaves
as proxy to dynamic array providing slicing interface. Note, even
in this case, elements of the array must be value type, no pointers
nor references to process's memory allowed.
The values created are initialized with std.conv.emplace using
additional arguments if any. The object remains in valid state
until the file is deleted or modified.
Examples:
import std.stdio;
import std.file : remove, exists, deleteme;
// custom data examples
struct A { int x; };
class B {};
enum Color { black, red, green, blue, white };
string[] file;
foreach(i; 0..8) file~=deleteme~to!string(i);
scope(exit) foreach(f; file[]) if(exists(f)) remove(f);
/// Part 1: create mapped variables
{
// simle int variable initialized with default value
auto p0=Perpetual!int(file[0]);
assert(p0 == int.init);
p0=7;
assert(p0 == 7);
// single double value initialized in ctor
// , would throw if the file did exist.
auto p1=Perpetual!double(file[1], 2.71828);
assert(p1 == 2.71828);
p1=3.14159;
// struct, initialized in ctor
auto p2=Perpetual!A(file[2], 22);
assert(p2.x == 22);
// static array of integers, assignable
auto p3=Perpetual!(int[5])(file[3]);
assert(p3[0] == int.init);
p3=[1,3,5,7,9];
assert(p3[0] == 1);
// enum, initialized in ctor
auto p4=Perpetual!Color(file[4], Color.red);
assert(p4 == Color.red);
// character string
auto p5=Perpetual!(char[32])(file[5]);
p5="hello world";
// second order static array
auto p8=Perpetual!(char[3][5])(file[6]);
p8[]="..."; p8[1]="one"; p8[2]="two";
/// Compile time errors
// char* is reference type
static assert(!is(typeof(Perpetual!(char*)("?"))));
// B is class, reference type
static assert(!is(typeof(Perpetual!(B)("?"))));
// char* is reference type
static assert(!is(typeof(Perpetual!(char*)("?"))));
// char*[12] is reference type
static assert(!is(typeof(Perpetual!(char*[12])("?"))));
// char[string] is reference type
static assert(!is(typeof(Perpetual!(char[string])("?"))));
// char[] is reference type
static assert(!is(typeof(Perpetual!(char[][])("?"))));
// char[][3] is reference type
static assert(!is(typeof(Perpetual!(char[][3])("?"))));
}
/// destroy everything and unmap files
/// Part 2: map again and check the values are preserved
{
// Was previosly mapped as int and assigned 7
auto p0=Perpetual!int(file[0]);
assert(p0 == 7);
// int cannot be emplaced from a double
static assert(!is(typeof(Perpetual!int("?", 1.0))));
/// attempt to initialize immutable array
static assert(!is(typeof(Perpetual!(immutable(int)[])(3,file[0],34))));
/// The file was only 4 bytes long
/// , immutable storage can't be extended
assertThrown(Perpetual!(immutable(int)[])(3,file[0]));
/// This works, extend the storage and
/// init appended tail, but not existing part
auto p1=Perpetual!(int[])(3,file[0],123);
assert(p1[0] == 7);
assert(p1[1] == 123);
assert(p1.length == 3);
// Was previousli mapped as double and assigned 3.14159
auto p2=Perpetual!double(file[1]);
assert(p2 == 3.14159);
// struct with int member initialized with 22
auto p3=Perpetual!A(file[2]);
assert(p3 == A(22));
// Was mapped as int[5], remap as view only array of shorts
auto p4=Perpetual!(immutable(short[]))(file[3]);
assert(p4.length == 10);
// Assuming LSB
assert(p4[0] == 1 && p4[2] == 3 && p4[4] == 5);
// cannot modify immutable expression
static assert(!is(typeof(p4[1]=111)));
// enum, was set to Color.red
auto p5=Perpetual!Color(file[4]);
assert(p5 == Color.red);
// view only variant of char[4]
auto p6=Perpetual!string(4, file[5]);
assert(p6 == "hell");
// cannot modify immutable expression
static assert(!is(typeof(p6[0]='A')));
// slice is not mutable
static assert(!is(typeof(p6[]="1234")));
// remap second order array as plain array
auto p7=Perpetual!(const(char[]))(file[6]);
assert(p7.length == 15);
assert(p7[0..5] == "...on");
// map again as dynamic array
auto p8=Perpetual!(char[3][])(file[6]);
assert(p8.length == 5);
assert(p8[2] == "two");
// Array lengths don't match for copy: 4 != 3
//p8[0]="null";
// write-protected file
{ File(file[7],"w").write("12345678"); }
chmod(file[7].toStringz, octal!444);
// mutable array can't be mapped on read-only file
assertThrown(Perpetual!(int[])(file[7]));
// immutable array can be mapped
auto p9=Perpetual!(immutable(int)[])(file[7]);
assert(p9.length == 2);
}
- @property Element[] Ref();
- Proxy to underlying dynamic array type.
Forwards calls to opSlice(), length() etc
- const @property void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt);
- Convert to string the value, not the class itself
- const @property auto initIndex();
- Index of uninitialized part of the array
- const @property auto master();
- If whole or part of the file was created and
was initialized in constructor, return true.
For scalar types this means, the entire object was constructed.
For dynamic arrays initIndex() shows first initialized
elsement.
- this(Arg...)(string path, Arg arg);
- Universal constructor, for both scalar and array types.
Opens file and assosiates object with it.
The file is extended if required.
The object is initialized if file was created or extended.
- this(Arg...)(size_t len, string path, Arg arg) if (dynamic);
- Open file and map dynamic array to it.
Creates array of requested length, file is extended if necessary
class factory function
Examples:
Using of package scoped shared memory allocator
import std.file : remove, deleteme;
string file=deleteme~"1";
scope(exit) remove(file);
{
auto s1=shMem(file, 8);
enforce(s1.master);
enforce(s1.writeable);
enforce(s1.unplowed == 0);
enforce(s1.length == 8);
int[] i=cast(int[]) s1[];
enforce(i.length == 2);
i[0]=12;
i[1]=13;
}
{
auto s2=shMem(file, 12);
enforce(s2.master);
enforce(s2.writeable);
enforce(s2.unplowed == 8);
enforce(s2.length == 12);
int[] i=cast(int[]) s2[];
enforce(i.length == 3);
enforce(i[0] == 12);
enforce(i[1] == 13);
}
{
auto s3=shMem(file, 4, ShMem.Mode.readOnly);
enforce(!s3.master);
enforce(!s3.writeable);
enforce(s3.unplowed == 4);
enforce(s3.length == 4);
int[] i=cast(int[]) s3[];
enforce(i.length == 1);
enforce(i[0] == 12);
}
{
auto s4=shMem(file);
enforce(!s4.master);
enforce(s4.writeable);
enforce(s4.unplowed == 12);
enforce(s4.length == 12);
}
{
assertThrown(shMem(file, 24, ShMem.Mode.readOnly));
}
{
chmod(toStringz(file), octal!444);
assertThrown(shMem(file, 24));
}