While working on dconstruct's compiler, I needed a way to tell the serializer which parts of each struct should have their relocaction table bits set. In practice, every 64-bit chunk that contains a pointer gets a 1, while non-pointer chunks get a 0.
I ended up using a small bitmap to describe the layout of each serialized struct. Bitmaps are unsigned 64-bit integers. For example:
Here the first two u32 fields share the first 64-bit chunk, so that chunk is not a pointer. The final field is a pointer, so the bitmap expressed in binary is 0b10.
Because I always try to stay up-to-date on the newest C++ standard, there is a commented-out sketch in base.h for how C++26 static reflection could generate those masks automatically:
The same reflection sketch is also embedded below in Compiler Explorer so you can inspect and tweak it directly:
Each reflection feature in that snippet has a specific role:
consteval makes the function compile-time only, so the bitmap is generated once by the compiler. There is no point to making this function be available at runtime (as it only takes a template argument anyway) so I chose to make it consteval instead of just constexpr^^T reflects the type T into a metaobject. This is at the core of C++26 reflection: Metaobjects exist only "reflection"-time, and are not comparable to any other kind of datatype that already exists in C++.nonstatic_data_members_of(...) enumerates the nonstatic, nonfunction members of that reflected type.std::meta::access_context::current uses the current access context when reflecting members. This means we can see the same members that we would be able to if we were using them in a normal context. std::meta::access_context::unchecked() gives us access to everything, while unprivileged() only shows us public, no matter where we are calling from.define_static_array(...) materializes the reflected member list into something the compiler can iterate over at reflection-time.template for is the compile-time loop that visits each reflected member.std::meta::type_of(mem) gets the reflected type of the current member from the meta-object. The result is still a meta-object, but this time partaining to the type itself.[: ... :] turns that reflected type back into an ordinary C++ type so it can be used with sizeof.is_pointer_type(type_of(mem)) decides whether the current 8-byte block should set a relocation bit.In dconstruct, the intended usage is straightforward: compute the bitmap for the struct type once, then pass its bytes into the existing push_bytes API:
For StateScript, this produces the same mask used manually today. The low byte 0b1010'1010 describes the first eight 64-bit slots, and the next byte 0b01 describes the remaining two.
While building dconstruct, a disassembler and decompiler for compiled script files, I needed a way to understand the binary format before writing the actual parser. ImHex turned out to be the perfect tool for this. It has a built-in pattern language called hexpat that lets you overlay typed structures onto raw hex data, turning an opaque byte stream into a navigable, annotated view. This guide covers the most useful features of the language that I used during the reverse engineering process.
The binary format I was working with uses FNV-1a hashing to identify types and symbols. Rather than hardcoding magic numbers, hexpat lets you define functions that run at pattern-evaluation time. This means you can compute the hash of a string and use the result directly in enum definitions:
This function implements the FNV-1a 64-bit hash. The initial basis 0xCBF29CE484222325 and the prime 0x100000001B3 are the standard FNV-1a constants. With this, enum variants can be defined using human-readable names while the underlying values are the actual hashes the binary uses:
This makes the pattern self-documenting — you can see that TypeId::Float corresponds to the hash of the string "float", and ImHex will match it automatically when it encounters that value in the hex view.
Hexpat has a native padding[n] type for skipping over bytes you don't care about:
This works, but it has an annoying side effect: bytes consumed by padding are no longer highlighted in the hex view. When you're actively reverse engineering a format, you want to see every byte accounted for and colored — even the ones you don't understand yet. Unhighlighted bytes are invisible bytes, and invisible bytes are easy to miscount when you're trying to figure out where the next field starts.
The workaround is to use u8 _padding[n] instead. This declares the padding as an actual named array of bytes, which keeps them highlighted in the hex view:
The leading underscore is just a naming convention to signal "I know these bytes exist but I don't know what they are yet." Once I understood all the fields, I could replace some of these with their actual types. The ones that remained as _padding are genuinely unused alignment bytes.
match StatementOne of the most powerful features in hexpat is the match statement. It lets you conditionally parse different struct layouts depending on a previously read value — essentially a tagged union / sum type. The binary format I was working with has a generic "typed value" container: a type ID followed by a payload whose layout depends on that ID.
This is extremely useful for formats with polymorphic structures. Without match, you'd have to use if/else if chains, which become unwieldy with many variants. The match syntax makes the intent clear: "read the type tag, then parse the body according to that tag."
@The binary format is heavily pointer-based — structs contain absolute offsets to other structs elsewhere in the file. Hexpat's @ (placement) operator lets you declare a variable at an arbitrary file offset, which means you can follow pointer chains directly in the pattern:
The @ m_pSsLambda tells ImHex to place the array at the file offset stored in m_pSsLambda, not at the current cursor position. Combined with the if guard (to avoid following null pointers), this lets you recursively resolve an entire pointer-based data structure from a single root struct. ImHex then highlights all the referenced data, even when it's scattered across the file.
The hardest part of the reverse engineering was the symbol table. Every 8-byte chunk (u64) in the binary's data section can optionally be a pointer to another structure. Whether it is a pointer is determined by a bitfield that sits after the data section — one bit per chunk.
First, the bitfield itself is defined using hexpat's bitfield type:
Each SymbolTableByte contains both the raw byte value and a bitfield overlay at the same position (@ $ - 1 rewinds one byte). This dual view lets you inspect either the raw value or the individual bits in ImHex.
The real trick is the SymbolLinkedChunk struct, which uses the symbol table bits to conditionally interpret chunks as pointers:
This struct is placed at offset 0x0 and repeated for every 8-byte chunk in the file. For each chunk, it reads the corresponding bit from the symbol table (using calculated offsets based on $, the current cursor position). If the bit is set, the chunk is a pointer, and the pattern follows it. The [[hidden]] attribute on the raw chunk field prevents it from cluttering the pattern data view — you only see the resolved pointer targets.
This was the key insight that made the rest of the disassembler possible: once you know which u64 values are pointers and which are plain data, you can recursively resolve the entire file into a tree of typed structures.
Recently, I needed to write a class for work that had some functions that should only be called after some state had been fulfilled within the object. This, of course, can be solved in many different ways. We could just check for the condition at the beginning of every function call. But Python gives us a better solution: decorators. However, I've recently become a die-hard MyPy user, so typing my functions is mandatory, and I try to use Any unless absolutely necessary. So, I had to figure out how to correctly type decorators. Here's a small refresher:
In general, decorators are defined like this:
The decorator receives the to-be-decorated function f, does some stuff before, calls the function, and then does some stuff after. Let's annotate this correctly:
Callable type, it's part of the typing module of Python's standard library. It receives two type arguments:
[ ])None here)f takes arguments? Generally, it's solved like this when you're not considering type hints:
args[0] isn't a string, and our program tries to strip a number at runtime?
We could always assert the type via
assert isinstance(args[0], str), but that's anything but elegant, and also at runtime.
Let's consider the case in which our decorator only decorates function that take *exactly one* string, and no other arguments. We would annotate this as such:
ParamSpec, Concatenate & GenericsParamSpec is another type of the Python library. It's a generic representation of a functions parameter types. Since Python 3.12 however, you can use it through the new generic syntax: