#!/usr/bin/env python3 # Copyright 2025 Google LLC # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #from typing import * from enum import IntEnum from typing import Dict, List from . IndexRange import * from . StringList import * class Context(): """ Contains global tables for strings, and lists-of-strings. It contains: - string_buffer: A list of null-terminated strings. The list is partitioned into contiguous segments dedicated to a specific kind of string, e.g.: - all instruction opcodes - all extension names - all aliases for a given enum - all enum names for a first operand type - all enum names for a second operand type, etc. Strings are sorted within each segment. - string_total_len: The sum of lengths of strings in string_buffer. - strings: Maps a string to an IndexRange indicating where the string can be found in the (future) concatenation of all strings in the string_buffer. - range_buffer: A dictionary mapping a string kind to a list IndexRange objects R. Each R is one of: - An index range referencing one string as it appears in the (future) concatenation of all strings in the string_buffer. In this case R represents a single string. - An index range referencing earlier elements in range_buffer[kind] itself. In this case R represents a list of strings. - ranges: A dictionary mapping a string kind and lists-of-strings to its encoding in the range_buffer array. It is a two-level mapping of Python type: Dict[str,dict[StringList,IndexRange]] where ranges[kind][list of strings] = an IndexRange The 'kind' string encodes a purpose including: - opcodes: the list of instruction opcode strings. - the list of aliases for an opcode, or an enum - an operand type such as 'SPV_OPERAND_TYPE_DIMENSIONALITY': the list of operand type names, e.g. '2D', '3D', 'Cube', 'Rect', etc. in the case of SPV_OPERAND_TYPE_DIMENSIONALITY. By convention, the 'kind' string should be a singular noun for the type of object named by each member of the list. The IndexRange leaf value encodes a list of strings as in the second case described for 'range_buffer'. """ def __init__(self) -> None: self.string_total_len: int = 0 # Sum of lengths of all strings in string_buffer self.string_buffer: List[str] = [] self.strings: Dict[str, IndexRange] = {} self.ir_to_string: Dict[IndexRange, str] = {} # Inverse of self.strings self.range_buffer: Dict[str,List[IndexRange]] = {} # We need StringList here because it's hashable, and so it # can be used as the key for a dict. self.ranges: Dict[str,Dict[StringList,IndexRange]] = {} def GetString(self, ir: IndexRange) -> str: if ir in self.ir_to_string: return self.ir_to_string[ir] raise Exception("unregistered index range {}".format(str(ir))) def AddString(self, s: str) -> IndexRange: """ Adds or finds a string in the string_buffer. Returns its IndexRange. """ if s in self.strings: return self.strings[s] # Allocate space, including for the terminating null. s_space: int = len(s) + 1 ir = IndexRange(self.string_total_len, s_space) self.strings[s] = ir self.ir_to_string[ir] = s self.string_total_len += s_space self.string_buffer.append(s) return ir def AddStringList(self, kind: str, words: List[str]) -> IndexRange: """ Ensures a list of strings is recorded in range_buffer[kind], and returns its location in the range_buffer[kind]. As a side effect, also ensures each string in the list is in the string_buffer. """ l = StringList(words) entry: Dict[StringList, IndexRange] = self.ranges.get(kind, {}) if kind not in self.ranges: self.ranges[kind] = entry self.range_buffer[kind] = [] if l in entry: return entry[l] new_ranges = [self.AddString(s) for s in l] ir = IndexRange(len(self.range_buffer[kind]), len(new_ranges)) self.range_buffer[kind].extend(new_ranges) entry[l] = ir return ir def dump(self) -> None: print("string_total_len: {}".format(self.string_total_len)) sbi = 0 print("string_buffer:") for sb in self.string_buffer: print(" {}: '{}'".format(sbi, sb)) sbi += len(sb) + 1 print("") s = [] for k,v in self.strings.items(): s.append("'{}': {}".format(k,str(v))) print("strings:\n {}\n".format('\n '.join(s))) for rbk, rbv in self.range_buffer.items(): print("range_buffer[{}]:".format(rbk)) i: int = 0 for r in rbv: print(" {} {}: {}".format(rbk, i, str(r))) i += 1 print("") for rk, rv in self.ranges.items(): for key,val in rv.items(): print("ranges[{}][{}]: {}".format(str(rk),str(key), str(val))) print("")