std.random: MersenneTwisterEngine
)1011101000000010100100100010100010010011010000100010111111011001
10111 |
| [01000000010100100100010100010010]
| |
+----------(rotate_bits)
|
10100100100010100010010][010000000 ---> output
uint randomPcg32(ref ulong seed)
{
const ulong x = seed;
seed = x * 0x5851F42D4C957F2D + 0x14057B7EF767814F;
uint xorshifted = cast(uint)(((x >> 18UL) ^ x) >> 27UL);
uint rot = cast(uint)(x >> 59UL);
return (xorshifted >> rot) | (xorshifted << ((-rot) & 0b11111));
}
Anybody can come up with a a complex solution. A simple one takes genius. You know it's genius when others say: "phui, anyone could have done that!" Except that nobody did.
-Walter Bright
void main()
{
string s = environmentGet("PATH");
writeln(s);
free(s.ptr);
}
void main()
{
string s = environmentGet("PATH");
scope(exit)
free(s.ptr);
writeln(s);
}
malloc
βΊ free
ITypeInfo
and IMoniker
:GetFuncDesc
βΊ ReleaseFuncDesc
GetVarDesc
βΊ ReleaseVarDesc
GetNames
βΊ SysFreeString
GetDisplayName
βΊ CoTaskMemFree
Documentation suggests IMalloc::Free
void getString(IMoniker moniker, IBindCtx ctx)
{
BSTR displayName;Β Β Β Β
moniker.GetDisplayName(ctx, null, &displayName);
writeln(displayName.fromStringz);
IMalloc allocator;
CoGetMalloc(1, &allocator);
allocator.Free(displayName);
allocator.release();
}
@live
functions offer some protection
The borrow checker makes it safe, right?
void main() @live
{
int* x = cast(int*) malloc(4);
free(x);
}
Right?
...
void main() @live
{
int* x = new int;
free(x); // No error, by design
}
void main()
{
string paths = "C:/dmd;C:/ldc2";
foreach (string path; paths.splitter(';'))
{
writeln(path);
}
}
Voldemort Types can be annoying
import std.stdio, std.path;
void main()
{
File f = File(withExtension("basilisk", ".txt"));
// Error: none of the overloads of `this` are
// callable using argument types `(Result)`
import std.array;
File g = File(withExtension("basilisk", ".txt").array);
}
char[] environmentGet(string var)
{
char[32768] buf = void;
// GetEnvironmentVariable(var, buf[]);
return buf[]; // Error
}
void main()
{
char[32768] buf;
const str = environmentGet("PATH", buf[]);
}
void environmentGet(O)(string name, ref O sink)
{
import std.range: put;
put(sink, "...");
}
void main()
{
import std.array : Appender;
Appender!string appender;
environmentGet("PATH", appender);
string result = appender.data;
}
environmentGet("PATH").splitter(';')
@nogc
Appender
@safe
Memory is automatically managed by occasionally pausing all threads and scanning for memory still in use, and freeing the rest.
Amusing story from Kent Mitchell
dmd -lowmem
)std.internal.string: tempCString
dmd.common.string: SmallBuffer
struct ScopeArray(T)
{
T[32] stackMem;
T[] big;
this(size_t length)
{
if (length > stackMem.length)
big = malloc(T.sizeof * length);
}
T[] opIndex() => big ? big[] : stackMem[];
~this() { if (big.ptr) free(big.ptr); }
}
Length must be given upfront
void main()
{
auto a = ScopeArray!char(length: 1024);
char[] path = environmentGet("PATH", a[]);
writeln(path);
// a.~this();
}
Unless...
void main()
{
auto a = Arena();
char[] path = environmentGet("PATH", &a);
writeln(path);
// a.~this();
}
struct Arena
{
ubyte[] buffer;
ArenaPage* page = null;
}
struct ArenaPage
{
ArenaPage* previous;
// variable number of bytes follow
}
void heap()
{
Arena a;
ubyte[] res = a.allocate(100); // heap allocates
}
void stack()
{
ubyte[512] buf = void;
Arena a = Arena(buf[]);
ubyte[] res = a.allocate(100); // uses stack buffer
}
ref
or pointerabstract class Allocator
{
ubyte[] allocate(size_t size, size_t alignment);
}
class Arena : Allocator;
class GcAllocator : Allocator;
class FailAllocator : Allocator;
struct Allocator
{
AllocatorBase* x;
}
struct AllocatorBase
{
immutable AllocFunc allocate;
}
alias AllocFunc = ubyte[] function(size_t size, size_t alignment, void* this_);
struct Arena
{
AllocatorBase base; // Old school struct inheritance
ubyte[] buffer;
ArenaPage* page;
}
struct Arena
{
Allocator alloc() return => Allocator(&this);
}
struct Allocator
{
AllocatorBase* base;
T[] array(T)(size_t length) =>
cast(T[]) base.allocate(T.sizeof * length);
}
void main()
{
Arena a;
char[] str = a.alloc.array!char(128);
}
string environmentGet(string name, Allocator alloc = gc)
{
return alloc.array!char(n);
}
void main()
{
string s = environmentGet("PATH");
Arena a;
string s = environmentGet("PATH", a.alloc);
}
Best of both worlds!
@safe
// Requires `-preview=dip1000`
string environmentGet(string name, return scope Allocator alloc = gc);
string global;
void main() @safe
{
global = environmentGet("PATH", gc); // Fine
Arena a;
global = environmentGet("PATH", a.alloc); // Error
}
@nogc
return scope
, but no @inout_nogc
@nogc
@nogc
should not be part of function typestruct Array(T)
{
T[] slice;
size_t capacity;
Allocator alloc;
}
void main() @safe
{
import automem.vector;
auto vec1 = vector(1, 2, 3);
int[] slice1 = vec1[];
vec1.reserve(4096);
int[] slice2 = vec1[];
}
Just don't free when growing the array
free()
calls@trusted
annotationsScopeArray
, Stack
, and NogcAppender
Array
is all you need void copying()
{
float[] data;
data ~= 3.0;
data ~= 4.0;
glBufferSubData(buffer, data);
}
void memoryMapping()
{
float[] data = glMapBufferRange(buffer, 2 * float.sizeof);
size_t i = 0;
data[i++] = 3.0;
data[i++] = 4.0;
glUnmapBuffer(buffer);
}
{
auto mapper = Mapper(buffer, 2); // Arena
scope float[] mappedBuffer = mapper[];
auto data = Array!float(storage: mappedBuffer, failAllocator);
data ~= 3.0;
data ~= 4.0;
// mapper.~this() unmaps
}
Clutter from long parameter declaration
string environmentGet(string name);
// vs
string environmentGet(string name, return scope Allocator alloc = gc);
Language feature of Jai and Odin
main :: proc()
{
context.user_index = 456
{
context.allocator = my_custom_allocator()
context.user_index = 123
supertramp() // `context` is implicitly passed
}
assert(context.user_index == 456)
}
struct Context
{
Allocator allocator;
Allocator temp_allocator;
Assertion_Failure_Proc assertion_failure_proc;
Logger logger;
Random_Generator random_generator;
void* user_ptr;
ptrdiff_t user_index;
// Internal use only
void* _internal;
}
new T[]
with allocator.array!T
Arena
/ Allocator
where needed-preview=dip1000
for @safe
@system
/@trusted
free()
is not @safe
@safe
with scope
Dennis Korpel
https://github.com/marp-team/marpit/tree/main/docs
https://github.com/marp-team/marpit/blob/main/docs/image-syntax.md
Callback needs to compute 480 samples with a strict deadline
### Image credits: - [Missile](https://commons.wikimedia.org/w/index.php?curid=120217981) by Marcus Burns, CC BY 3.0 - ----------------------------------------------------------- ### Quite Okay Formats - Dominic Szablewski - Similar to PNG / MP3 - Encoder / decoder are 400 lines <!- -_footer: https://qoiformat.org/- -> ----------------------------------------------------------- ## Partial / transitive scope ```D struct Context { string source; HashMap!(string, Declaration) symbolTable; Token[] tokens; // ... } ``` * Inference can be programmed * But how much `scope[...]` syntax do we want? ----------------------------------------------------------- ## Syntax woes * `return scope` vs. `scope return` still here * Inference by default would help * `ret&` and `retscope`? ----------------------------------------------------------- ### Scoped pointers status update * Refactored `dmd/escape.d` this year * Number of `if` statements 310 β 240 * `scope` inference still needs work * Nested functions + `scope` still broken * Want partial / transitive scope for structs ----------------------------------------------------------- ### 4. Null garbage collection  <!- -_footer: https://youtu.be/bNJhtKPugSQ?si=z-USHbQyKkhrlH9c&t=579 - -> ----------------------------------------------------------- # Unpredictable lifetimes * What if allocation depends on user input? * Pre-allocate a pool * Roll your own free-list, bitmapped block, GC... <!- -_footer: https://bitbashing.io/gc-for-systems-programmers.html- -> <!- -https://forum.dlang.org/post/tnajfjmvvyqardwhxegi@forum.dlang.org- -> ----------------------------------------------------------- ## Concatenation ```D struct Person { string name, surname; string toString() const => name ~ " " ~ surname; } void main() { char[] s = "Hello " ~ Person("Dennis", "Korpel").toString() ~ "!"; } // "Dennis " // "Dennis Korpel" // "Hello Dennis Korpel" // "Hello Dennis Korpel!" ``` ----------------------------------------------------------- ``` string environmentGet(string key, return scope Allocator alloc = gc) { //int n = GetEnvironmentVariableA(key.ptr, null, 0); char[] buf = alloc.array!char(n); //GetEnvironmentVariableA(key.ptr, buf.ptr, buf.length); return buf[0 .. n]; } ``` ----------------------------------------------------------- ### GC complexity  <!- -_footer: https://github.com/dlang/dmd/tree/master/druntime/src/core/internal/gc- -> ----------------------------------------------------------- # GC Phobia  <!- -_footer: https://forum.dlang.org/post/mailman.147.1702920162.3719.digitalmars-d-learn@puremagic.com- -> ----------------------------------------------------------- ### | Do π | Don't βΉοΈ | |--------------------------|------------------------------| | Free big chunks | Pair each malloc βΊ free | | Free at end of scope | Manually call free | | Return simple values | Be annoying on the call site | ----------------------------------------------------------- ## Context struct could help  <!--_footer: https://youtu.be/uZgbKrDEzAs?si=clQT4OAd4j66KJ8i&t=2303- -> ----------------------------------------------------------- ### std.experimental.allocator is BIG * Interface + composable building blocks * 20 000 lines of code π² * Overkill for my purposes <!- -_footer: https://dlang.org/phobos/std_experimental_allocator.html- ->