Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| en:docs:win16:modules:local_heap [2026/02/24 04:50] – prokushev | en:docs:win16:modules:local_heap [2026/02/24 08:12] (current) – [References] prokushev | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ===== Win16 Local Heap Functions | + | ===== Local Heap and Atom Table ===== |
| ===== Overview ===== | ===== Overview ===== | ||
| Line 23: | Line 23: | ||
| **Important Notes:** | **Important Notes:** | ||
| - | * The field at offset 6 (pLocalHeap) is the primary way to locate the local heap structures given only the DGROUP selector. | + | |
| - | * When LocalInit() is called on a globally allocated block (non‑DGROUP), | + | |
| - | * Similarly, if InitAtomTable() is called on a global block, offset 8 points to the atom table, and offset 6 will point to the associated local heap (since atoms are stored in the local heap). | + | * When LocalInit() is called on a globally allocated block (non‑DGROUP), |
| + | * Similarly, if InitAtomTable() is called on a global block, offset 8 points to the atom table, and offset 6 will point to the associated local heap (since atoms are stored in the local heap). | ||
| ==== HeapInfo and LocalInfo ==== | ==== HeapInfo and LocalInfo ==== | ||
| Line 97: | Line 98: | ||
| Every block in the local heap is preceded by an arena (header) that contains management information. Arenas always start on a 4‑byte boundary, so the two low bits of every arena address are zero. These bits are reused as flags in the la_prev field of each arena. The two low bits of la_prev have the following meaning: | Every block in the local heap is preceded by an arena (header) that contains management information. Arenas always start on a 4‑byte boundary, so the two low bits of every arena address are zero. These bits are reused as flags in the la_prev field of each arena. The two low bits of la_prev have the following meaning: | ||
| - | * Bit 0 (least significant): | + | |
| - | * Bit 1: Set if the block is MOVEABLE; cleared if the block is FIXED (only meaningful when bit 0 is set). | + | * Bit 1: Set if the block is MOVEABLE; cleared if the block is FIXED (only meaningful when bit 0 is set). |
| Thus, to obtain the real address of the previous arena, the two low bits must be masked off. | Thus, to obtain the real address of the previous arena, the two low bits must be masked off. | ||
| Line 148: | Line 149: | ||
| ===== Heap Operations ===== | ===== Heap Operations ===== | ||
| - | * **Allocation (LocalAlloc)** walks the free list, splitting blocks if necessary, and sets up the appropriate arena. For MOVEABLE blocks, it also allocates a handle table entry. | + | |
| - | * **Compaction (LocalCompact)** coalesces adjacent free blocks and may move or discard unlocked MOVEABLE blocks. When a block is moved, its lhe_address is updated. | + | * **Compaction (LocalCompact)** coalesces adjacent free blocks and may move or discard unlocked MOVEABLE blocks. When a block is moved, its lhe_address is updated. |
| - | * **Locking (LocalLock/ | + | * **Locking (LocalLock/ |
| - | * **Discarding (LocalDiscard)** frees the memory of a MOVEABLE block but keeps the handle entry alive with the LHE_DISCARDED flag set. | + | * **Discarding (LocalDiscard)** frees the memory of a MOVEABLE block but keeps the handle entry alive with the LHE_DISCARDED flag set. |
| ===== Atom Tables ===== | ===== Atom Tables ===== | ||
| Line 161: | Line 162: | ||
| ==== Relationship with the Local Heap ==== | ==== Relationship with the Local Heap ==== | ||
| - | Physically, an atom table resides **inside** the local heap of some data segment . Therefore, before creating an atom table, the segment must be initialized as a local heap by calling `LocalInit()`. | + | Physically, an atom table resides **inside** the local heap of some data segment. Therefore, before creating an atom table, the segment must be initialized as a local heap by calling `LocalInit()`. |
| - | ==== Types of Atoms ==== | + | ==== ATOMENTRY Structure |
| - | Windows supports two fundamentally different types of atoms: **string | + | Each string |
| - | ===== Integer Atoms ===== | + | ^ Offset ^ Type ^ Field ^ Description ^ |
| + | | 00h | WORD | `next` | Next entry in the same hash bucket (0 if last). | | ||
| + | | 02h | WORD | `usage` | Reference count. | | ||
| + | | 04h | BYTE | `len` | Length of the string (1–255). | | ||
| + | | 05h | BYTE[] | `name` | ASCIIZ string (length `len` + 1). | | ||
| - | Integer atoms are a special category | + | ==== Types of Atoms ==== |
| - | + | ||
| - | * **Range**: `0x0001` to `0xBFFF` . | + | |
| - | * **Reference count**: Not applicable, as they are not allocated from the heap . | + | |
| - | * **String representation**: | + | |
| - | * **Purpose**: | + | |
| - | **Important consequence**: Functions like `GlobalAddAtom`, | + | Windows supports two fundamentally different types of atoms: |
| ===== String Atoms ===== | ===== String Atoms ===== | ||
| - | These are the " | + | String |
| - | * **Range**: `0xC000` to `0xFFFF` . | + | |
| - | * **Physical memory representation**: Each string atom is represented by an **`ATOMENTRY`** structure located | + | * **Storage**: Allocated |
| + | * **Reference count**: Yes (`usage` field). | ||
| + | | ||
| + | * **Creation**: | ||
| - | ==== Why the range 0xC000–0xFFFF? (Technical rationale) ==== | + | **Encoding: |
| - | This is a direct implementation feature of 16-bit Windows : | + | <code c> |
| - | 1. **Alignment**: | + | #define HANDLETOATOM(handle) ((ATOM)(0xc000 | ((handle) >> |
| - | 2. **Encoding**: | + | #define ATOMTOHANDLE(atom) ((HANDLE16)(atom) << |
| - | 3. **Decoding**: | + | </ |
| - | 4. **Range separation**: | + | |
| - | ==== Structure of a String Atom Entry (ATOMENTRY) | + | ===== Integer Atoms ===== |
| - | The `ATOMENTRY` structure is the exact layout of a string | + | Integer atoms are created by passing |
| - | ^ Offset ^ Type ^ Field ^ Description ^ | + | * **Range**: |
| - | | 00h | WORD | `next` | Near pointer | + | * **Storage**: |
| - | | 02h | WORD | `usage` | Reference count (how many times `AddAtom`/`GlobalAddAtom` has been called | + | * **Reference count**: Not applicable. |
| - | | 04h | BYTE | `len` | Length of the string | + | * **String representation**: |
| - | | 05h | BYTE[] | `name` | Beginning of the buffer containing the ASCIIZ string | + | * **Creation**: |
| - | In C/C++: | + | **How it works:** When a string of the form '# |
| - | ```c | + | |
| - | typedef struct atomstruct { | + | |
| - | struct atomstruct near *next; /* Next entry in collision chain */ | + | |
| - | WORD usage; | + | |
| - | BYTE len; /* Length of string | + | |
| - | BYTE name; /* Start of string | + | |
| - | } ATOMENTRY; | + | |
| - | ``` | + | |
| - | **Important**: | + | ==== MAKEINTATOM Macro ==== |
| - | ==== Structure of an Atom Table (ATOMTABLE) ==== | + | The **`MAKEINTATOM`** macro is defined as: |
| - | An atom table is a hash table implemented as an array of " | + | <code c> |
| + | #define MAKEINTATOM(i) | ||
| + | </ | ||
| - | ^ Offset ^ Type ^ Field ^ Description ^ | + | This macro casts a 16‑bit integer value to a pointer type. When this " |
| - | | 00h | WORD | `numEntries` | Size of the hash table (number of buckets). Default | + | |
| - | | 02h | ... | `hashtab` | Array of `numEntries` near pointers | + | |
| - | In simplified C: | + | * For values ≤ `0xBFFF`, the function treats it as an integer atom and returns the value directly. |
| - | ```c | + | * For values ≥ `0xC000`, the function assumes it is an encoded pointer to an `ATOMENTRY` and will dereference it (after shifting left by 2 bits) to access the atom entry. |
| - | typedef struct { | + | |
| - | WORD numEntries; | + | **Important: |
| - | | + | |
| - | } ATOMTABLE; | + | |
| - | ``` | + | |
| ==== Local vs. Global Atom Tables ==== | ==== Local vs. Global Atom Tables ==== | ||
| - | * **Local atom tables**: Bound to a specific data segment (e.g., an application' | + | |
| - | * **Global atom table**: A system-wide table accessible to all applications via `GlobalAddAtom`, | + | * **Global atom table**: A system-wide table accessible to all applications via `GlobalAddAtom`, |
| ==== Creating Custom Atom Tables (outside DGROUP) ==== | ==== Creating Custom Atom Tables (outside DGROUP) ==== | ||
| - | Since all atom operations | + | Since all atom operations work with the current segment pointed to by DS, you can create and use an atom table in any arbitrary data segment by following three steps: |
| - | 1. **Create a local heap** in the target segment using `LocalInit(Selector, | + | - |
| - | 2. **Switch the DS register** to that segment. | + | |
| - | 3. Call `InitAtomTable(size)` to initialize the atom table in the newly created heap. | + | |
| - | After that, any subsequent call to `AddAtom`, `FindAtom`, etc., will operate on the custom table if DS is temporarily set to the correct segment . | + | After that, any subsequent call to `AddAtom`, `FindAtom`, etc., will operate on the custom table if DS is temporarily set to the correct segment. |
| - | + | ||
| - | Example in assembly: | + | |
| - | ```asm | + | |
| - | ; Assume a local heap already exists in segment CUST_SEG | + | |
| - | push ds | + | |
| - | mov ax, CUST_SEG | + | |
| - | mov ds, ax | + | |
| - | push 37 ; hash table size | + | |
| - | call InitAtomTable | + | |
| - | pop cx | + | |
| - | pop ds | + | |
| - | ``` | + | |
| - | + | ||
| - | A convenience wrapper in C: | + | |
| - | ```c | + | |
| - | ATOM BasedAddAtom(WORD wSeg, LPCSTR lpString) | + | |
| - | { | + | |
| - | ATOM ret; | + | |
| - | _asm push ds | + | |
| - | _asm mov ds, wSeg | + | |
| - | ret = AddAtom(lpString); | + | |
| - | _asm pop ds | + | |
| - | return ret; | + | |
| - | } | + | |
| - | ``` | + | |
| ==== Summary of Atom Type Differences ==== | ==== Summary of Atom Type Differences ==== | ||
| - | ^ Feature ^ Integer | + | ^ Feature ^ String |
| - | | Range | `0x0001` – `0xBFFF` | `0xC000` – `0xFFFF` | | + | | Range | `0xC000` – `0xFFFF` | `0x0001` – `0xBFFF` | |
| - | | Storage | Not stored (value is the atom) | In local heap as `ATOMENTRY` | | + | | Stored in atom table | Yes, as `ATOMENTRY` |
| - | | Reference count | None | Yes (field `usage`) | | + | | Memory allocated | `ATOMENTRY` structure in local heap | None | |
| - | | String representation | `"#1234" | + | | Reference count | Yes (`usage`) |
| - | | Creation | `AddAtom(" | + | | String representation | Original string | Generated as `"#dddd" |
| - | | Examples | + | | Creation | `AddAtom(" |
| + | | Find behavior | ||
| + | | Delete behavior | Decrements refcount, frees if zero | No operation (returns 0) | | ||
| ===== Custom Local Heaps ===== | ===== Custom Local Heaps ===== | ||
| Line 289: | Line 257: | ||
| The `LocalInit()` function initializes a local heap within a specified segment. Its prototype is: | The `LocalInit()` function initializes a local heap within a specified segment. Its prototype is: | ||
| - | ```c | + | < |
| WORD LocalInit(WORD wSegment, WORD pStart, WORD pEnd); | WORD LocalInit(WORD wSegment, WORD pStart, WORD pEnd); | ||
| - | ``` | + | </ |
| - | * `wSegment` – Selector of the segment where the heap will be created. | + | |
| - | * `pStart` – Offset of the first byte of the heap area (must be paragraph‑aligned, | + | * `pStart` – Offset of the first byte of the heap area (must be paragraph‑aligned, |
| - | * `pEnd` – Offset of the last byte of the heap area (inclusive). The heap will manage memory from `pStart` to `pEnd`. | + | * `pEnd` – Offset of the last byte of the heap area (inclusive). The heap will manage memory from `pStart` to `pEnd`. |
| If successful, `LocalInit()` returns a non‑zero value. It sets up the `HeapInfo` and `LocalInfo` structures at the beginning of the heap area (starting at `pStart`) and updates the segment’s instance data at offset **06h** (`pLocalHeap`) to point to that `HeapInfo` structure. However, if the segment is not a default data segment (i.e., not DGROUP), the instance data at offset 0 must also contain a zero word to indicate that the NULL segment structure is present; otherwise, the heap may not be recognized by some routines. | If successful, `LocalInit()` returns a non‑zero value. It sets up the `HeapInfo` and `LocalInfo` structures at the beginning of the heap area (starting at `pStart`) and updates the segment’s instance data at offset **06h** (`pLocalHeap`) to point to that `HeapInfo` structure. However, if the segment is not a default data segment (i.e., not DGROUP), the instance data at offset 0 must also contain a zero word to indicate that the NULL segment structure is present; otherwise, the heap may not be recognized by some routines. | ||
| - | **Example: | + | **Example: |
| - | ```asm | + | < |
| - | ``` | + | ; 1. Allocate a 64KB global memory block |
| + | GlobalAlloc GMEM_FIXED, 0x10000 | ||
| + | mov dx, ax ; DX = selector of allocated block | ||
| + | |||
| + | ; 2. Temporarily set DS to that segment to access its instance data | ||
| + | push ds | ||
| + | push dx | ||
| + | pop ds | ||
| + | |||
| + | ; 3. Initialize the NULL segment (Instance Data) at offset 0. | ||
| + | ; The first word must be zero (wMustBeZero = 0). | ||
| + | xor ax, ax | ||
| + | mov word ptr [0], ax | ||
| + | ; The other fields (pLocalHeap, | ||
| + | |||
| + | ; 4. Define the heap area: start at offset 16 (0x0010) to preserve | ||
| + | ; the 16-byte Instance Data, end at 0xFFFF (the last byte of the segment). | ||
| + | mov bx, 16 ; pStart = 16 | ||
| + | mov cx, 0xFFFF | ||
| + | |||
| + | ; 5. Restore DS (if no longer needed) | ||
| + | pop ds | ||
| + | |||
| + | ; 6. Call LocalInit to create the heap in segment dx, from pStart to pEnd. | ||
| + | push dx | ||
| + | push bx | ||
| + | push cx | ||
| + | call LocalInit | ||
| + | |||
| + | ; After the call, the Instance Data at offset 6 in segment dx | ||
| + | ; contains a valid near pointer to the HeapInfo structure located at offset 16. | ||
| + | </ | ||
| After this call, the global block can be used with local heap functions (`LocalAlloc`, | After this call, the global block can be used with local heap functions (`LocalAlloc`, | ||
| **Important Considerations: | **Important Considerations: | ||
| - | - The heap structures themselves occupy space at the beginning of the heap area. The first block (sentinel) resides at `pStart + size of (LocalInfo)`. | + | |
| - | - The segment’s instance data (at offset 0) must be properly set up, especially the zero word at offset 0, to avoid confusion with other structures. | + | * The heap structures themselves occupy space at the beginning of the heap area. The first block (sentinel) resides at `pStart + size of (LocalInfo)`. |
| - | - Custom local heaps are not automatically enlarged if they run out of space; they are limited to the range specified in `LocalInit`. | + | |
| - | - The `HEAPSIZE` setting in the module’s .DEF file only affects the default DGROUP heap. | + | |
| + | | ||
| ==== Creating Atom Tables Outside DGROUP ==== | ==== Creating Atom Tables Outside DGROUP ==== | ||
| - | As already described | + | As already described, to create an atom table in an arbitrary memory area, you must first initialize a local heap there (as above) and then, after switching DS, call `InitAtomTable`. This technique allows fully isolated atom tables for special purposes. |
| ==== Summary of Custom Heap and Atom Table Creation ==== | ==== Summary of Custom Heap and Atom Table Creation ==== | ||
| - | - Use `LocalInit` on a segment to establish a local heap anywhere in memory. | + | * Use `LocalInit` on a segment to establish a local heap anywhere in memory. |
| - | - The segment must have a valid NULL segment structure (zero word at offset 0) for the heap to be recognized. | + | |
| - | - After `LocalInit`, | + | |
| - | - To create an atom table in a custom heap, switch DS to that segment and call `InitAtomTable`. | + | |
| - | - All subsequent atom operations must be performed with DS set appropriately (or via wrapper functions). | + | |
| - | - Custom heaps and atom tables are useful for isolating memory pools, implementing resource managers, or working with large data structures without polluting the default DGROUP. | + | |
| ===== References ===== | ===== References ===== | ||
| - | 1. Schulman, A., Maxey, D., Pietrek, M. // | + | - Schulman, A., Maxey, D., Pietrek, M. // |
| - | 2. Pietrek, M. //Windows Internals// | + | |
| - | 3. Chen, R. //The Old New Thing// (blog). Microsoft Developer Blogs. | + | |
| - | 4. Microsoft OS/2 Programmer' | + | |




