blob: 086ab8d78cee14fd4b0f5faf8473b13f618967fc [file] [log] [blame]
#!/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("")