Skip to content

Commit 11f172d

Browse files
authored
Merge pull request #45 from fvutils/mballance/ncdb-ucis-compliance
Improve UCIS compliance for the new NCDB database
2 parents d54718f + 48317a2 commit 11f172d

19 files changed

Lines changed: 1017 additions & 214 deletions

doc/source/reference/formats/ncdb-format.rst

Lines changed: 281 additions & 53 deletions
Large diffs are not rendered by default.

src/ucis/mem/mem_cover_index.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,21 @@ def setCoverFlags(self, flags: int):
4242
"""Set cover flags."""
4343
if self.data:
4444
self.data.flags = flags
45-
46-
45+
46+
def setAttribute(self, key: str, value: str):
47+
"""Set a user-defined attribute on this coveritem."""
48+
if not hasattr(self, '_attributes'):
49+
self._attributes = {}
50+
self._attributes[key] = value
51+
52+
def getAttribute(self, key: str):
53+
"""Get a user-defined attribute by key."""
54+
if not hasattr(self, '_attributes'):
55+
return None
56+
return self._attributes.get(key)
57+
58+
def getAttributes(self):
59+
"""Get all user-defined attributes as a dict."""
60+
if not hasattr(self, '_attributes'):
61+
return {}
62+
return dict(self._attributes)

src/ucis/mem/mem_cover_index_iterator.py

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,17 @@ class MemCoverIndexIterator(object):
1111

1212
def __init__(self, coveritems : List[MemCoverIndex], mask : CoverTypeT):
1313
self.coveritems = coveritems
14-
self.mask = mask
14+
self.mask = int(mask)
1515
self.idx = 0
16-
16+
1717
def __iter__(self):
1818
return self
19-
19+
2020
def __next__(self):
21-
next = None
22-
23-
while self.idx < len(self.coveritems) and next is None:
21+
mask = self.mask
22+
while self.idx < len(self.coveritems):
2423
n = self.coveritems[self.idx]
25-
26-
if (n.data.type & self.mask) != 0:
27-
next = n
2824
self.idx += 1
29-
30-
if next is None:
31-
raise StopIteration
32-
33-
return next
25+
if (int(n.data.type) & mask) != 0:
26+
return n
27+
raise StopIteration

src/ucis/mem/mem_history_node.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,5 +253,21 @@ def setStringProperty(self, coverindex: int, property, value: str):
253253
}
254254
if property in _map:
255255
setattr(self, _map[property], value)
256-
257-
256+
257+
def setAttribute(self, key: str, value: str):
258+
"""Set a user-defined attribute on this history node."""
259+
if not hasattr(self, '_attributes'):
260+
self._attributes = {}
261+
self._attributes[key] = value
262+
263+
def getAttribute(self, key: str):
264+
"""Get a user-defined attribute by key."""
265+
if not hasattr(self, '_attributes'):
266+
return None
267+
return self._attributes.get(key)
268+
269+
def getAttributes(self):
270+
"""Get all user-defined attributes as a dict."""
271+
if not hasattr(self, '_attributes'):
272+
return {}
273+
return dict(self._attributes)

src/ucis/mem/mem_instance_scope.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,16 @@ def createScope(self,
5050
source : SourceT,
5151
type : ScopeTypeT,
5252
flags : FlagsT) -> 'Scope':
53-
if (type & ScopeTypeT.COVERGROUP) != 0:
53+
itype = int(type)
54+
if (itype & int(ScopeTypeT.COVERGROUP)) != 0:
5455
ret = MemCovergroup(self, name, srcinfo, weight, source)
55-
elif (type & ScopeTypeT.BLOCK) != 0:
56+
elif (itype & int(ScopeTypeT.BLOCK)) != 0:
5657
ret = MemBlockScope(self, name, srcinfo, weight, source, flags)
57-
elif (type & ScopeTypeT.BRANCH) != 0:
58+
elif (itype & int(ScopeTypeT.BRANCH)) != 0:
5859
ret = MemBranchScope(self, name, srcinfo, weight, source, flags)
59-
elif (type & ScopeTypeT.TOGGLE) != 0:
60+
elif (itype & int(ScopeTypeT.TOGGLE)) != 0:
6061
ret = MemToggleScope(self, name, srcinfo, weight, source, flags)
61-
elif (type & ScopeTypeT.FSM) != 0:
62+
elif (itype & int(ScopeTypeT.FSM)) != 0:
6263
from ucis.mem.mem_fsm_scope import MemFSMScope
6364
ret = MemFSMScope(self, name, srcinfo, weight, source, flags)
6465
else:
@@ -96,4 +97,4 @@ def createToggle(self,
9697
def getIthCoverItem(self, i)->CoverItem:
9798
return self.m_cover_item_l[i]
9899

99-
100+

src/ucis/mem/mem_scope_iterator.py

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,19 @@ class MemScopeIterator(object):
1111
def __init__(self, nodes : List['MemScope'], mask):
1212
self.nodes = nodes
1313
self.idx = 0
14-
self.mask = mask
15-
14+
self.mask = int(mask)
15+
1616
def __iter__(self):
1717
return self
18-
18+
1919
def __next__(self):
2020
next = None
21+
mask = self.mask
2122

22-
while next is None and self.idx < len(self.nodes):
23+
while self.idx < len(self.nodes):
2324
n = self.nodes[self.idx]
24-
# TODO: qualify mask
25-
if (n.getScopeType() & self.mask) != 0:
26-
next = n
27-
2825
self.idx += 1
29-
30-
if next is not None:
31-
break
26+
if (int(n.getScopeType()) & mask) != 0:
27+
return n
3228

33-
if next is None:
34-
raise StopIteration
35-
return next
29+
raise StopIteration

src/ucis/ncdb/_accel/_ncdb_accel.o

-4.27 KB
Binary file not shown.

src/ucis/ncdb/attrs.py

Lines changed: 118 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,100 @@
11
"""
2-
attrs.json — user-defined attribute serialization.
2+
attrs.bin — user-defined attribute serialization.
33
4-
Format: JSON object
4+
Format v1 (legacy): JSON object
55
{"version": 1, "entries": [{"idx": <int>, "attrs": {<key>: <val>}}, ...]}
66
7-
Only scopes that have at least one attribute are included (sparse).
7+
Format v2 (current): JSON object with sections for scopes, coveritems,
8+
history nodes, and global attrs.
9+
{"version": 2,
10+
"scopes": [{"idx": <int>, "attrs": {<key>: <val>}}, ...],
11+
"coveritems": [{"scope_idx": <int>, "ci_idx": <int>, "attrs": {...}}, ...],
12+
"history": [{"idx": <int>, "attrs": {...}}, ...],
13+
"global": {<key>: <val>}}
814
"""
915

1016
import json
1117

1218
from .dfs_util import dfs_scope_list
19+
from ucis.history_node_kind import HistoryNodeKind
1320

14-
_VERSION = 1
21+
_VERSION = 2
22+
_COVER_ALL = 0xFFFFFFFF
1523

1624

1725
class AttrsWriter:
18-
"""Serialize user-defined scope attributes to attrs.json bytes."""
26+
"""Serialize user-defined attributes to attrs.bin bytes."""
1927

2028
def serialize(self, db) -> bytes:
2129
scopes = dfs_scope_list(db)
22-
entries = []
30+
scope_entries = []
2331
for idx, scope in enumerate(scopes):
2432
if not hasattr(scope, 'getAttributes'):
2533
continue
2634
attrs = scope.getAttributes()
2735
if attrs:
28-
entries.append({"idx": idx, "attrs": attrs})
29-
payload = {"version": _VERSION, "entries": entries}
36+
scope_entries.append({"idx": idx, "attrs": attrs})
37+
38+
ci_entries = []
39+
for idx, scope in enumerate(scopes):
40+
try:
41+
items = list(scope.coverItems(_COVER_ALL))
42+
except Exception:
43+
continue
44+
for ci_idx, ci in enumerate(items):
45+
if not hasattr(ci, 'getAttributes'):
46+
continue
47+
attrs = ci.getAttributes()
48+
if attrs:
49+
ci_entries.append({
50+
"scope_idx": idx, "ci_idx": ci_idx, "attrs": attrs
51+
})
52+
53+
hist_entries = []
54+
for kind in (HistoryNodeKind.TEST, HistoryNodeKind.MERGE):
55+
try:
56+
nodes = list(db.historyNodes(kind))
57+
except Exception:
58+
continue
59+
for hi, node in enumerate(nodes):
60+
if not hasattr(node, 'getAttributes'):
61+
continue
62+
attrs = node.getAttributes()
63+
if attrs:
64+
hist_entries.append({
65+
"idx": hi, "kind": kind.name, "attrs": attrs
66+
})
67+
68+
global_attrs = {}
69+
if hasattr(db, 'getAttributes'):
70+
global_attrs = db.getAttributes()
71+
72+
payload = {
73+
"version": _VERSION,
74+
"scopes": scope_entries,
75+
"coveritems": ci_entries,
76+
"history": hist_entries,
77+
"global": global_attrs,
78+
}
3079
return json.dumps(payload, separators=(',', ':')).encode()
3180

3281

3382
class AttrsReader:
34-
"""Deserialize attrs.json bytes and apply attributes to scope tree."""
83+
"""Deserialize attrs.bin bytes and apply attributes."""
3584

3685
def deserialize(self, data: bytes, db) -> None:
3786
if not data:
3887
return
3988
payload = json.loads(data.decode())
40-
if payload.get("version") != _VERSION:
41-
raise ValueError(f"Unsupported attrs.json version: {payload.get('version')}")
89+
version = payload.get("version", 1)
90+
91+
if version == 1:
92+
self._deserialize_v1(payload, db)
93+
elif version == 2:
94+
self._deserialize_v2(payload, db)
95+
96+
def _deserialize_v1(self, payload, db):
97+
"""Legacy v1: scope attrs only."""
4298
entries = payload.get("entries", [])
4399
if not entries:
44100
return
@@ -50,3 +106,54 @@ def deserialize(self, data: bytes, db) -> None:
50106
for key, val in entry.get("attrs", {}).items():
51107
if hasattr(scope, 'setAttribute'):
52108
scope.setAttribute(key, val)
109+
110+
def _deserialize_v2(self, payload, db):
111+
"""V2: scopes + coveritems + history + global."""
112+
scopes = dfs_scope_list(db)
113+
114+
for entry in payload.get("scopes", []):
115+
idx = entry["idx"]
116+
if idx < len(scopes):
117+
scope = scopes[idx]
118+
for key, val in entry.get("attrs", {}).items():
119+
if hasattr(scope, 'setAttribute'):
120+
scope.setAttribute(key, val)
121+
122+
for entry in payload.get("coveritems", []):
123+
scope_idx = entry["scope_idx"]
124+
ci_idx = entry["ci_idx"]
125+
if scope_idx < len(scopes):
126+
scope = scopes[scope_idx]
127+
try:
128+
items = list(scope.coverItems(_COVER_ALL))
129+
if ci_idx < len(items):
130+
ci = items[ci_idx]
131+
for key, val in entry.get("attrs", {}).items():
132+
if hasattr(ci, 'setAttribute'):
133+
ci.setAttribute(key, val)
134+
except Exception:
135+
pass
136+
137+
hist_nodes = {}
138+
for kind in (HistoryNodeKind.TEST, HistoryNodeKind.MERGE):
139+
try:
140+
hist_nodes[kind.name] = list(db.historyNodes(kind))
141+
except Exception:
142+
pass
143+
for entry in payload.get("history", []):
144+
kind_name = entry.get("kind", "TEST")
145+
idx = entry["idx"]
146+
nodes = hist_nodes.get(kind_name, [])
147+
if idx < len(nodes):
148+
node = nodes[idx]
149+
for key, val in entry.get("attrs", {}).items():
150+
if hasattr(node, 'setAttribute'):
151+
node.setAttribute(key, val)
152+
153+
for key, val in payload.get("global", {}).items():
154+
if hasattr(db, 'setAttribute'):
155+
db.setAttribute(key, val)
156+
157+
def apply(self, db, data: bytes) -> None:
158+
"""Alias for deserialize (matches other readers' API)."""
159+
self.deserialize(data, db)

src/ucis/ncdb/constants.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
PRESENCE_WEIGHT = 0x04 # has non-default weight (≠1)
5050
PRESENCE_AT_LEAST = 0x08 # coveritem at_least override at scope level
5151
PRESENCE_CVG_OPTS = 0x10 # has covergroup options
52+
PRESENCE_GOAL = 0x20 # has non-default scope goal (≠-1)
53+
PRESENCE_SOURCE_TYPE = 0x40 # has explicit SourceT enum
5254

5355
# ── counts.bin encoding modes ─────────────────────────────────────────────
5456

@@ -65,21 +67,21 @@
6567
# Used by reader to reconstruct coveritem objects without per-item storage.
6668

6769
COVER_TYPE_DEFAULTS: dict = {
68-
CoverTypeT.TOGGLEBIN: (0, 0, 1),
69-
CoverTypeT.STMTBIN: (0, 0, 1),
70-
CoverTypeT.BRANCHBIN: (0, 0, 1),
71-
CoverTypeT.CONDBIN: (0, 0, 1),
72-
CoverTypeT.EXPRBIN: (0, 0, 1),
73-
CoverTypeT.FSMBIN: (0, 0, 1),
74-
CoverTypeT.CVGBIN: (0, 1, 1),
75-
CoverTypeT.DEFAULTBIN: (0, 0, 1),
76-
CoverTypeT.IGNOREBIN: (0, 0, 1),
77-
CoverTypeT.ILLEGALBIN: (0, 0, 1),
78-
CoverTypeT.BLOCKBIN: (0, 0, 1),
79-
CoverTypeT.COVERBIN: (0, 0, 1),
80-
CoverTypeT.ASSERTBIN: (0, 0, 1),
81-
CoverTypeT.PASSBIN: (0, 0, 1),
82-
CoverTypeT.FAILBIN: (0, 0, 1),
70+
CoverTypeT.TOGGLEBIN: (0x01, 0, 1),
71+
CoverTypeT.STMTBIN: (0x01, 0, 1),
72+
CoverTypeT.BRANCHBIN: (0x01, 0, 1),
73+
CoverTypeT.CONDBIN: (0x01, 0, 1),
74+
CoverTypeT.EXPRBIN: (0x01, 0, 1),
75+
CoverTypeT.FSMBIN: (0x01, 0, 1),
76+
CoverTypeT.CVGBIN: (0x19, 1, 1),
77+
CoverTypeT.DEFAULTBIN: (0x01, 0, 1),
78+
CoverTypeT.IGNOREBIN: (0x01, 0, 1),
79+
CoverTypeT.ILLEGALBIN: (0x01, 0, 1),
80+
CoverTypeT.BLOCKBIN: (0x01, 0, 1),
81+
CoverTypeT.COVERBIN: (0x01, 0, 1),
82+
CoverTypeT.ASSERTBIN: (0x01, 0, 1),
83+
CoverTypeT.PASSBIN: (0x01, 0, 1),
84+
CoverTypeT.FAILBIN: (0x01, 0, 1),
8385
}
8486

8587
# ── Scope-type → implicit child cover type mapping ────────────────────────
@@ -104,3 +106,4 @@
104106
ScopeTypeT.COVER: CoverTypeT.COVERBIN,
105107
ScopeTypeT.ASSERT: CoverTypeT.ASSERTBIN,
106108
}
109+
MEMBER_COVERITEM_FLAGS = "coveritem_flags.bin"

0 commit comments

Comments
 (0)