Here’s a crisp guide to OVERLAY in RPGLE data structures—what it is, why you’d use it, and the gotchas.
What OVERLAY does
OVERLAY
makes one field or data structure share the same storage as another field/DS (at the same or a specified byte position). It’s like a C “union” or a byte-map: no copying, just different views of the same bytes.
1) Overlay subfields inside a DS (byte mapping)
**free
dcl-ds Buf qualified;
raw char(20); // 20-byte buffer
id uns(5) overlay(raw:1); // bytes 1-2 (2 bytes)
qty int(10) overlay(raw:3); // bytes 3-6 (4 bytes)
code char(3) overlay(raw:7); // bytes 7-9
price packed(7:2) overlay(raw:10); // bytes 10-13 (see packed bytes below)
flag char(1) overlay(raw:14);
end-ds;
-
overlay(raw:n)
starts at byte n ofraw
. -
Great for parsing fixed-format records, sockets, data areas, or API buffers.
Packed storage bytes = ⌊digits/2⌋ + 1 (e.g., 7,2 → 4 bytes).
2) Overlay one DS on another (union-style)
Two DS share the same starting storage. Use this to reinterpret bytes in different ways.
dcl-ds U1 qualified;
all char(8);
end-ds;
dcl-ds U2 qualified overlay(U1); // same 8 bytes as U1
n int(10); // re-interpret as a 4-byte int (first 4 bytes)
s char(4) overlay(n:1); // look at the int as 4 chars, same bytes
end-ds;
// Now U1.all, U2.n, and U2.s are different views of the same bytes
3) Overlay onto pointer-based DS (dynamic buffers)
Map a variable-length buffer without copying.
dcl-s p pointer;
// Assume p points to at least 32 bytes
dcl-ds Msg based(p) qualified;
raw char(32);
len uns(5) overlay(raw:1);
type char(2) overlay(raw:3);
data char(20) overlay(raw:5);
end-ds;
// Set pointer from receive/read, then use Msg.* subfields directly
4) Overlay within arrays (careful!)
You can overlay array storage, but it’s easy to misalign.
dcl-ds A;
raw char(30);
arr char(10) dim(3) overlay(raw:1); // arr(1)=1..10, arr(2)=11..20, arr(3)=21..30
end-ds;
Common patterns
-
Fixed record parsing: Read record into
raw
then access typed fields via overlays. -
Union/casting: Share storage across DS to reinterpret bytes (e.g., char↔int).
-
Performance: Avoids
%SUBST
copies—reads/writes are in-place.
Gotchas & tips
-
Byte math matters
-
Packed/zoned must start at proper byte boundaries (packed uses nibbles but you specify byte positions; ensure the length exactly fits).
-
-
ALIGN can move things
-
ALIGN
on a DS repositions subfields to natural boundaries. If you rely on exact byte positions or overlays, avoid ALIGN on that DS (or design with it consistently).
-
-
Endianness & representation
-
int/uns
are binary; overlays let you see their raw bytes—useful, but representation is platform-specific. Don’t serialize binary ints unless you control both ends.
-
-
No copying vs
%SUBST
-
OVERLAY
is no-copy;%SUBST
returns a substring value (copy). For big buffers, overlay is faster.
-
-
Debugging
-
Use the debugger’s hex view or
DSPPGMVAR
(or dump) to confirm byte offsets if something looks off.
-
Mini cheat sheet
-
Overlay a subfield at byte 1:
field overlay(base:1)
-
Overlay whole DS on another:
dcl-ds B overlay(A); ... end-ds;
-
Packed bytes: bytes = ⌊digits/2⌋ + 1 (e.g., 5,0 → 3 bytes; 7,2 → 4 bytes)
-
Zoned bytes: bytes = digits
-
Pointer-mapped DS:
dcl-ds X based(p); ... end-ds;
thenp = %addr(buf);