from stdint import * import memhub import string import stdlib import mlirlite import win32.win32file as win32file import win32.win32base as win32base import win32.fileio as fileio import viperlib import sys import ast import lib.core.VLogger as VLogger import lib.core.IncludesScanner as IncludesScanner import lib.core.Gen.Translator as HandlesTranslator import lib.Phase.BuildPipeline as BuildPipeline import lib.Stub.StubGen.VpiReader as VpiReader import lib.Projectrans.Utils as Utils import lib.Projectrans.Config as Config import lib.Stub.StubMerger.StubMergerStore as StubMergerStore import lib.Stub.StubMerger.StubMergerTopo as StubMergerTopo import lib.Stub.StubMerger.StubMergerIR as StubMergerIR import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild import lib.Stub.StubMerger as StubMerger # StubMergerCollect - slice-path construction and stub-file SHA1 collection. # Functions live in this module; cross-module globals are accessed via StubMerger._xxx. # _sliced_path - build a slice path (stdlib.malloc; caller frees; creates subdir when level>0). # Shards by Config.Sha1SliceLevel into SHA1-prefix subdirs; returns the full path or None on failure. def _sliced_path(temp_dir: str, td_len: sizet, sha1: str, ext: str) -> str: """Build a SHA1-sliced path (stdlib.malloc; caller frees; creates the subdir).""" if temp_dir is None or sha1 is None or ext is None: return None sha1_len: sizet = string.strlen(sha1) ext_len: sizet = string.strlen(ext) # Subdir prefix: reuse Config.slice_subdir (the authoritative SHA1 slice impl; mbuddy-allocated, no free) subdir_buf: str = Config.slice_subdir(sha1, Config.Sha1SliceLevel) sub_len: sizet = 0 if subdir_buf is not None: sub_len = string.strlen(subdir_buf) # Ensure subdir {temp_dir}/{subdir} exists. # Otherwise fileio.File(MODE.W) fails silently (file.closed==True) and the reader returns 0. dir_path_len: sizet = td_len + sub_len + 2 dir_path: bytes = stdlib.malloc(dir_path_len) if dir_path is not None: viperlib.snprintf(dir_path, dir_path_len, "%s/%s", temp_dir, subdir_buf) BuildPipeline.ensure_dir(dir_path) stdlib.free(dir_path) # Build the full path path: str = None path_len: sizet = 0 if sub_len > 0: path_len = td_len + sub_len + sha1_len + ext_len + 4 path = stdlib.malloc(path_len) if path is not None: viperlib.snprintf(path, path_len, "%s/%s/%s.%s", temp_dir, subdir_buf, sha1, ext) else: path_len = td_len + sha1_len + ext_len + 3 path = stdlib.malloc(path_len) if path is not None: viperlib.snprintf(path, path_len, "%s/%s.%s", temp_dir, sha1, ext) return path # _load_includes_sha1_set - collect includes SHA1 from the global in-memory store. # Source is PopulateSha1MapStore (not _sha1_map.txt); filters stubs to avoid linking ViperC's own lib/* stubs. def _load_includes_sha1_set(pool: memhub.MemBuddy*, temp_dir: str, sha1_set: str) -> int: """Copy includes SHA1s from the global store into sha1_set; returns the count, -1 on error.""" if sha1_set is None: return -1 if StubMerger._sha1_store_arr is None: return -1 count: int = 0 for i in range(int(StubMerger._sha1_store_count)): if count >= StubMerger.MAX_INCLUDES_SHA1: break idx: sizet = sizet(i) * 17 string.strcpy(sha1_set + sizet(count) * 17, StubMerger._sha1_store_arr + idx) count += 1 return count # _is_in_sha1_set - check whether a SHA1 is in the set. def _is_in_sha1_set(sha1: str, sha1_set: str, set_count: int) -> int: """Whether sha1 is present in the set.""" if sha1 is None or sha1_set is None: return 0 for i in range(int(set_count)): if string.strcmp(sha1_set + i * 17, sha1) == 0: return 1 return 0 # WriteIncludesSha1Map - write includes scan results to _sha1_map.txt (human-readable debug output only). # Internally the in-memory store filled by PopulateSha1MapStore is used; returns 0 on success, non-zero on failure. def WriteIncludesSha1Map(mb: memhub.MemBuddy*, temp_dir: str, scan_result: IncludesScanner.ScanResult*, filter_set: str, filter_count: int) -> int: """Write the includes scan result to _sha1_map.txt (human-readable debug output).""" if temp_dir is None or scan_result is None: return 1 # Build the path temp_dir/_sha1_map.txt dir_len: sizet = string.strlen(temp_dir) map_path: bytes = stdlib.malloc(dir_len + 32) if map_path is None: return 1 viperlib.snprintf(map_path, dir_len + 32, "%s/_sha1_map.txt", temp_dir) # Open the file for writing (CREATE_ALWAYS) f: fileio.File* = fileio.File(map_path, fileio.MODE.W) if f.closed: return 1 # Write each include entry: {sha1}:includes/{rel_path}\n entry_size_w: sizet = IncludesScanner.FileEntry.__sizeof__() line_buf: bytes = stdlib.malloc(512) if line_buf is None: f.close() return 1 written_count: int = 0 for i in range(int(scan_result.Count)): ea_w: uint64 = uint64(scan_result.Entries) + i * entry_size_w ent_w: IncludesScanner.FileEntry* = (IncludesScanner.FileEntry*)(ea_w) if ent_w is None or ent_w.Sha1 is None or ent_w.RelPath is None: continue # When a filter set is provided, write only its entries if filter_set is not None and filter_count > 0: if _is_in_sha1_set(ent_w.Sha1, filter_set, filter_count) == 0: continue viperlib.snprintf(line_buf, 512, "%s:includes/%s\n", ent_w.Sha1, ent_w.RelPath) line_len_w: sizet = string.strlen(line_buf) f.write(line_buf, line_len_w) written_count += 1 f.close() return 0