Skip to content

Lots of architectural changes - #48

Open
ElijahSwiftIBM wants to merge 5 commits into
devfrom
feat/json-schema
Open

ElijahSwiftIBM wants to merge 5 commits into
devfrom
feat/json-schema

Conversation

@ElijahSwiftIBM

Copy link
Copy Markdown
Collaborator

first draft of json schema build

💡 Issue Reference

Issue: #7

💻 What does this address?

Strong desire to put together a programmatic manner to consume json schema for control blocks rather than hard-coded header files.

📟 Implementation Details

Major architectural changes but largely in line with what is described in #7

📋 Is there a test case?

No new test cases yet and I haven't even run the existing test cases yet.

@ElijahSwiftIBM
ElijahSwiftIBM force-pushed the feat/json-schema branch 3 times, most recently from 53be6bb to 7dc9e6c Compare October 22, 2026 19:45
Signed-off-by: Elijah Swift <Elijah.Swift@IBM.com>
Signed-off-by: Elijah Swift <Elijah.Swift@IBM.com>
Signed-off-by: Elijah Swift <Elijah.Swift@IBM.com>
Signed-off-by: Elijah Swift <Elijah.Swift@IBM.com>
@ElijahSwiftIBM

Copy link
Copy Markdown
Collaborator Author

This is Bob's summary that looks pretty accurate

Summary: feat/json-schema vs dev

This branch changes how CBXP describes control blocks. On dev, each control block is a hand-written C++ subclass. Here, each one is a JSON mapping, validated against a JSON schema and walked by a generic traversal engine.

The diff is 102 files, about +31k/−1.9k. Most of the added lines are the JSON mappings and the vendored validator. The hand-written logic is about 2,300 lines of new C++.

What changed at a high level

  • Control blocks are data, not code. ascb, assb, asvt, cvt, ecvt, ldax, oucb and psa are now JSON files in cbxp/mappings/. tcb.json is new and has no dev equivalent. Their per-block .cpp/.hpp files and packed structs are deleted from cbxp/control_blocks/.
  • New schema. schemas/control_block_v1.json defines the mapping format. It covers pointedToBy, storageAttributes (subpools and key), sections and fields, pointsTo, repeated/count/ignoreMask, alignBefore, the eyecatcher flag and constants.
  • Custom mappings at runtime. CBXPPATH is a colon-separated list of directories. Any *.json file in them is validated and loaded. A custom mapping with the same name as a built-in one overrides it.
  • New error codes. Codes 5–8 are NullDataPtr, BadCbxpPath, BadJsonFile and CantReachBlock. They are mirrored in python/cbxp/cbxp.py. cbxp.cpp now catches CBXPError and turns it into a return code.
  • Build changes.
    • CMake and setup.py now generate .hpp headers from the JSON files. Each header holds the JSON as a raw string macro, such as CVT_JSON, and the headers are force-included into the build.
    • nlohmann/json-schema-validator is vendored under externals/json-schema-validator.
    • nlohmann/json moved to externals/json/.
  • Tests. There is a new tests/fixtures/cbxppath/* suite (ok/err cases) run by tests/run_fixture.cmake, and tests/test.py is expanded.

New C++ architecture

The old model (dev)

Each ControlBlock subclass such as ASVT, CVT or ECVT implemented get(). It cast memory to a packed struct, built its own include and filter tables, and recursed into child control blocks directly. Include parsing, filter matching and recursion were all mixed into the ControlBlock base class.

The new model

The old class is now split into focused pieces:

flowchart TD
    API[cbxp_extract / cbxp_format] --> CBE[ControlBlockExplorer]
    CBE -->|static init once| LOAD[Load built-in JSON plus CBXPPATH<br/>validate with json_validator]
    LOAD --> CBMAP[(map name to ControlBlock)]
    CBE --> TE[TraversalEngine.run]
    TE --> CTX[TraversalContext<br/>queue, visited, filter_rejected, child_filtered]
    TE -->|drainQueue BFS| CBN[ControlBlockNode.parse]
    CBN --> IR[IncludeResolver]
    CBN --> FS[FilterSet]
    CBN -->|push PendingNode| CTX
    TE -->|resolvePlaceholders| OUT[Final nested JSON]
Loading

ControlBlock (control_block.hpp)

  • It is now a concrete, immutable description of one mapping. It is built from validated JSON and holds a field map, the eyecatcher spec, storage_attributes_t (is_common, is_protected, key), pointedToBy, includables, maxOffset and a fixed address.
  • storage_attributes_t is derived from subpool numbers via the new zos_subpools.hpp table. The engine uses it to decide whether a block can be read safely.

ControlBlockExplorer (control_block_explorer.cpp)

  • It owns the registry. getControlBlocks() is a function-local static that loads the built-in mappings and then the CBXPPATH directories. Each custom mapping is schema-validated before construction.
  • validateControlBlockReferences checks that every pointedToBy parent exists and that every repeated field names a real count field.
  • It no longer does any parsing. It builds options and hands off to the engine.

TraversalEngine (traversal_engine.cpp, 600 lines)
This is the core of the change.

  • run() validates options up front. It constructs a throwaway ControlBlockNode with a null address. That resolves includes and parses filters without touching storage, so bad --include/--filter values fail immediately.
  • Root addresses. findRootAddresses() walks the pointedToBy chain upward to a fixed anchor (PSA), then follows the pointer fields back down. It handles repeated pointers with count and ignoreMask, and de-duplicates roots. For example, ECVT is reachable from both CVT and PSA.
  • Traversal. drainQueue() is an iterative BFS over a std::queue<PendingNode> instead of recursion. For each node it checks the PSA fetch-protected range (0x7FFFF000–0x7FFFFFFF) and checkReachability, then calls ControlBlockNode::parse().
  • Reachability rules. A key≠8 fetch-protected block is refused, except PSA. A non-common-storage block is refused when no filters are given, because the result can't be guaranteed accurate. Inaccessible children become null in visited, so the output keeps the raw pointer hex.
  • Assembly. computeOwners() does a BFS to find each node's shallowest parent. resolvePlaceholders() then substitutes hex pointer placeholders with the resolved child JSON. A node is expanded only under its owner. Every other reference, including cycles, stays a pointer, which keeps assembly linear and cycle-safe.
  • Filter semantics. A child rejected by its filters rejects its parent, and rejected entries of a repeated pointer field are dropped.

ControlBlockNode (control_block_node.cpp)

  • It is deliberately thin. It parses one block at one address: it checks the eyecatcher, checks buffer length for format, reads each field into JSON and applies the node's own filters.
  • Pointer fields that are included don't recurse. They emit a hex placeholder and record a PendingNode. Those are enqueued only if this node's filters pass, so a rejected parent never loads its children.
  • withAddress() copies a validated node onto a new address, so the include and filter lists are parsed and logged once per root.
  • It never writes to ctx.visited. The engine owns that.
  • It returns a NodeParseResult {json, filter_passed, child_filter_failed}.

TraversalContext / PendingNode (traversal_context.hpp)

  • All mutable traversal state lives in one struct that is passed by reference into nodes. It holds visited, queue, filter_rejected and child_filtered.
  • visited is keyed by (address, cb_name). Its value follows a three-state protocol: false means in progress (cycle break), null means visited but empty or rejected, and an object or array means fully resolved.
  • filter_rejected lets a parent tell "rejected by filter" apart from "inaccessible".

IncludeResolver (include_resolver.cpp) and FilterSet (filter_set.cpp)

  • These are the include and filter logic pulled out of the old ControlBlock base class.
  • IncludeResolver handles *, ** and explicit paths. It caches ** expansions per control block.
  • FilterSet parses filters and matches them against a node's JSON. Filters like ascb.field=value are routed into the child's options.

cbxp_types.hpp now holds cbxp_options_t, which moved out of control_block.hpp.

@ElijahSwiftIBM
ElijahSwiftIBM marked this pull request as ready for review October 23, 2026 18:02

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant