| #!/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("") | |