161 lines
5.9 KiB
Python
Executable File
161 lines
5.9 KiB
Python
Executable File
#!/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("")
|
|
|
|
|