Files
fennec/gdb/fennec/containers.py
2026-01-06 19:48:28 -05:00

679 lines
21 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ======================================================================================================================
# fennec, a free and open source game engine
# Copyright © 2025 - 2026 Medusa Slockbower
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# ======================================================================================================================
import gdb
from collections import deque
# OPTIONAL =============================================================================================================
class OptionalPrinter:
"""Print a fennec::optional"""
def __init__(self, val):
self.val = val
def to_string(self):
is_initialized = self.val['_set']
if is_initialized:
return "{{ value = {} }}".format(self.val['_val'])
else:
return "{ empty }"
# PAIR =================================================================================================================
class PairPrinter:
"""Print a fennec::optional"""
def __init__(self, val):
self.val = val
def to_string(self):
return "{ first = " + str(self.val['first']) + ", second = " + str(self.val['second']) + " }"
def children(self):
return ("first", self.val['first']), ("second", self.val['second'])
# TUPLE ================================================================================================================
class TuplePrinter:
"""Print a fennec::tuple"""
def __init__(self, val):
self.fields = val.type.fields()[0].type.fields()
self.elems = val
self.count = len(self.fields)
def to_string(self):
return " { size = " + str(len(self.fields)) + " }"
def children(self):
return (('[{}]'.format(i), self.elems.cast(self.fields[i].type)['value']) for i in range(self.count))
# ARRAY ================================================================================================================
class ArrayPrinter:
"""Print a fennec::array"""
def __init__(self, val):
self.val = val
def to_string(self):
return "{ length = " + str(self.val['elements'].type.range()[1] + 1) + " }"
def display_hint(self):
return 'array'
def children(self):
start = self.val['elements']
return (('[{}]'.format(i), start[i]) for i in range(0, self.val['elements'].type.range()[1] + 1))
# DYNARRAY =============================================================================================================
class DynArrayPrinter:
"""Print a fennec::dynarray"""
def __init__(self, val):
self.val = val
def to_string(self):
return "{ length " + str(self.val['_size']) + ", capacity " + str(self.val['_alloc']['_capacity']) + " }"
def children(self):
size = int(self.val['_size'])
start = self.val['_alloc']['_data']
return (('[{}]'.format(i), start[i]) for i in range(size))
# LIST =================================================================================================================
class ListPrinter:
"""Print a fennec::list"""
class Iterator:
def __init__(self, val):
self.list = val
self.node = self.list['_root']
self.index = 0
self.table = self.list['_table']['_data']
def __iter__(self):
return self
def __next__(self):
if self.node == 18446744073709551615:
raise StopIteration
i = self.index
self.index = self.index + 1
value = self.table[self.node]['value']['_val']
self.node = self.table[self.node]['next']
return '[{}]'.format(i), value
def __init__(self, val):
self.val = val
def to_string(self):
return "{ length " + str(self.val['_size']) + ", capacity " + str(self.val['_table']['_capacity']) + " }"
def children(self):
return self.Iterator(self.val)
def display_hint(self):
return 'array'
# DEQUE ================================================================================================================
class DequePrinter:
"""Print a fennec::deque"""
class Iterator:
def __init__(self, start):
self.node = start
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.node is None:
raise StopIteration
i = self.index
value = self.node.dereference()['value']
self.node = self.node.dereference()['next']
self.index = self.index + 1
return '[{}]'.format(i), value
def __init__(self, val):
self.first = val['_first']
self.last = val['_last']
self.size = val['_size']
def to_string(self):
if self.first is None:
return "{ empty }"
return "{ length " + str(self.size) + " }"
def children(self):
return self.Iterator(self.first)
# SET ==================================================================================================================
class SetPrinter:
"""Print a fennec::set"""
class Iterator:
def __init__(self, table, capacity):
self.table = table
self.capacity = capacity
self.node = 0
self.index = 0
def __iter__(self):
return self
def __next__(self):
while self.node < self.capacity:
if self.table[self.node]['value']['_set']:
break
self.node = self.node + 1
if self.node >= self.capacity:
raise StopIteration
i = self.index
value = self.table[self.node]['value']['_val']
self.node = self.node + 1
self.index = self.index + 1
return '[{}]'.format(i), value
def __init__(self, val):
self.table = val['_alloc']['_data']
self.capacity = val['_alloc']['_capacity']
self.size = val['_size']
def to_string(self):
if self.size == 0:
return "{ empty }"
return "{ size = " + str(self.size) + " }"
def children(self):
return self.Iterator(self.table, self.capacity)
# MAP ==================================================================================================================
class MapPrinter:
"""Print a fennec::map"""
class Iterator:
def __init__(self, table, capacity):
self.table = table
self.capacity = capacity
self.node = 0
self.index = 0
self.move = False
def __iter__(self):
return self
def __next__(self):
while self.node < self.capacity:
if self.table[self.node]['value']['_set']:
break
self.node = self.node + 1
if self.node >= self.capacity:
raise StopIteration
i = self.index
pair = self.table[self.node]['value']['_val']
key = pair['first']
val = pair['second']
if not self.move:
self.move = True
return 'key' + str(i), key
else:
self.move = False
self.index = self.index + 1
self.node = self.node + 1
return 'value' + str(i), val
def __init__(self, val):
self.table = val['_set']['_alloc']['_data']
self.capacity = val['_set']['_alloc']['_capacity']
self.size = val['_set']['_size']
def to_string(self):
if self.size == 0:
return "{ empty }"
return "{ size = " + str(self.size) + " }"
def children(self):
return self.Iterator(self.table, self.capacity)
def display_hint(self):
return 'map'
# OBJECT_POOL ==========================================================================================================
class ObjectPoolPrinter:
"""Print a fennec::object_pool"""
class Iterator:
def __init__(self, val):
self.list = val
self.index = 0
self.capacity = self.list['_table']['_alloc']['_capacity']
self.table = self.list['_table']['_alloc']['_data']
def __iter__(self):
return self
def __next__(self):
i = self.index
while True:
i = self.index
self.index = self.index + 1
if self.index >= self.capacity:
raise StopIteration
if bool(self.table[i]['_set']):
value = self.table[i]['_val']
break
return '[{}]'.format(i), value
def __init__(self, val):
self.val = val
def to_string(self):
return "{ length " + str(self.val['_size']) + ", capacity " + str(self.val['_table']['_alloc']['_capacity']) + " }"
def children(self):
return self.Iterator(self.val)
def display_hint(self):
return 'array'
# RDTREE ===============================================================================================================
class RDTreePrinter:
"""Print a fennec::rdtree"""
class Iterator:
def __init__(self, tree, node, capacity):
self.tree = tree
self.capacity = capacity
self.visit = deque()
self.visit.append((node, 0, 0))
def __iter__(self):
return self
def __next__(self):
if len(self.visit) == 0:
raise StopIteration
node = self.visit[0][0]
i = self.visit[0][1]
depth = self.visit[0][2]
self.visit.popleft()
value = self.tree[node]['value']
nnext = self.tree[node]['next']
nprev = self.tree[node]['prev']
nprevc = self.tree[nprev]['child'] if nprev != 18446744073709551615 else 18446744073709551615
child = self.tree[node]['child']
n_chld = self.tree[node]['num_children']
index = '' * depth * 2 # Uses Braille Space, otherwise it would get eaten as whitespace by parsers
if nnext < self.capacity:
self.visit.appendleft((nnext, i + 1, depth))
if child < self.capacity:
self.visit.appendleft((child, 0, depth + 1))
# ┌ ─ ├ └
if nnext != 18446744073709551615:
index += ''
else:
index += ''
index += ''
index += '[{}]'.format(node)
return index, value
def __init__(self, val):
self.tree = val['_table']['_data']
self.size = val['_size']
self.capacity = val['_table']['_capacity']
def to_string(self):
if self.size == 0:
return "{ empty }"
return "{ size = " + str(self.size) + " }"
def children(self):
return self.Iterator(self.tree, 0, self.capacity)
# PRIORITY QUEUE =======================================================================================================
class PriorityQueuePrinter:
"""Print a fennec::rdtree"""
class Iterator:
def __init__(self, tree, node, capacity):
self.tree = tree
self.capacity = capacity
self.visit = deque()
self.skip = True
self.visit.append((node, 0, 0, node))
def __iter__(self):
return self
def __next__(self):
if len(self.visit) == 0:
raise StopIteration
node = self.visit[0][0]
i = self.visit[0][1]
depth = self.visit[0][2]
start = self.visit[0][3]
self.visit.popleft()
if node == start and not self.skip:
return self.__next__()
self.skip = False
value = self.tree[node]['_val']['key']
nnext = self.tree[node]['_val']['next']
child = self.tree[node]['_val']['child']
index = '' * depth * 2 # Uses Braille Space, otherwise it would get eaten as whitespace by parsers
if nnext < self.capacity:
self.visit.appendleft((nnext, i + 1, depth, start))
if child < self.capacity:
self.visit.appendleft((child, 0, depth + 1, child))
self.skip = True
# ┌ ─ ├ └
if nnext != 18446744073709551615:
index += ''
else:
index += ''
index += ''
index += '[{}]'.format(node)
return index, value
def __init__(self, val):
self.tree = val['_table']['_table']['_alloc']['_data']
self.size = val['_table']['_size']
self.capacity = val['_table']['_table']['_alloc']['_capacity']
self.min = val['_min']
def to_string(self):
if self.size == 0:
return "{ empty }"
return "{ size = " + str(self.size) + " }"
def children(self):
return self.Iterator(self.tree, self.min, self.capacity)
# BINTREE ==============================================================================================================
class BinTreePrinter:
"""Print a fennec::bintree"""
class Iterator:
def __init__(self, tree, node, capacity):
self.tree = tree
self.capacity = capacity
self.visit = deque()
if capacity > 0:
self.visit.append((node, 0, 0))
def __iter__(self):
return self
def __next__(self):
if len(self.visit) == 0:
raise StopIteration
node = self.visit[0][0]
i = self.visit[0][1]
depth = self.visit[0][2]
parent = self.tree[node]['parent']
self.visit.popleft()
value = self.tree[node]['value']
left = self.tree[node]['child'][0]
right = self.tree[node]['child'][1]
if right < self.capacity:
self.visit.appendleft((right, 1, depth + 1))
if left < self.capacity:
self.visit.appendleft((left, 0, depth + 1))
index = '' * depth * 2 # Uses Braille Space, otherwise it would get eaten as whitespace by parsers
if i == 0 and parent != 18446744073709551615 and self.tree[parent]['right'] != 18446744073709551615:
index += ''
else:
index += ''
index += ''
index += '[{}]'.format(node)
return index, value
def __init__(self, val):
self.tree = val['_table']['_data']
self.size = val['_size']
self.root = val['_root']
self.capacity = val['_table']['_capacity']
def to_string(self):
if self.size == 0:
return "{ empty }"
return "{ size = " + str(self.size) + " }"
def children(self):
return self.Iterator(self.tree, self.root, self.capacity)
# SEQUENCE =============================================================================================================
class SequencePrinter:
"""Print a fennec::sequence"""
class Iterator:
def __init__(self, node):
self.visit = deque()
if node is not None:
self.visit.append((node, 0, 0))
def __iter__(self):
return self
def __next__(self):
if len(self.visit) == 0:
raise StopIteration
node = self.visit[0][0]
i = self.visit[0][1]
depth = self.visit[0][2]
self.visit.popleft()
value = node['key']
left = node['child'][0]
right = node['child'][1]
print("it: ", node, " ", left, " ", right);
if right != 0:
self.visit.appendleft((right, 1, depth + 1))
if left != 0:
self.visit.appendleft((left, 0, depth + 1))
index = '' * depth * 2 # Uses Braille Space, otherwise it would get eaten as whitespace by parsers
if i == 0:
index += ''
else:
index += ''
index += ''
index += '[{}]'.format(node)
return index, value
def __init__(self, val):
self.size = val['_size']
self.root = val['_root']
def to_string(self):
if self.size == 0:
return "{ empty }"
return "{ size = " + str(self.size) + " }"
def children(self):
print("root: ", self.root)
return self.Iterator(self.root)
# Graph ================================================================================================================
class GraphPrinter:
"""Print a fennec::graph"""
class Iterator:
def __init__(self, val):
self.node_pool = val['_vertex_pool']['_table']['_alloc']['_data']
self.max_nodes = val['_vertex_pool']['_table']['_alloc']['_capacity']
self.conn_map = val['_edge_map']['_alloc']['_data']
self.max_conn = val['_edge_map']['_size']
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= self.max_nodes:
raise StopIteration
i = self.index
self.index = self.index + 1
while not bool(self.node_pool[i]['_set']):
i = self.index
self.index = self.index + 1
conns = self.get_conns(i)
value = self.node_pool[i]['_val']
return '[{} -> {{{}}}]'.format(i, str(conns)), value
def get_conns(self, index):
indices = []
if index >= self.max_conn:
return indices
map = self.conn_map[index]['_set']
max_conns = map['_alloc']['_capacity']
conns = map['_alloc']['_data']
print(max_conns)
if max_conns == 0:
return indices
for i in range(0, max_conns):
if bool(conns[i]['value']['_set']):
conn = conns[i]['value']['_val']
indices.append(str(int(conn['first'])))
return indices
def __init__(self, val):
self.val = val
self.size = val['_vertex_pool']['_size']
def to_string(self):
if self.size == 0:
return "{ empty }"
return "{ size = " + str(self.size) + " }"
def children(self):
return self.Iterator(self.val)
# GDB Code =============================================================================================================
def register_printers():
print("registering containers")
pp = gdb.printing.RegexpCollectionPrettyPrinter("fennec::containers")
pp.add_printer('fennec::array', '^fennec::array<.*>$', ArrayPrinter)
pp.add_printer('fennec::deque', '^fennec::deque<.*>$', DequePrinter)
pp.add_printer('fennec::dynarray', '^fennec::dynarray<.*>$', DynArrayPrinter)
pp.add_printer('fennec::graph', '^fennec::graph<.*>$', GraphPrinter)
pp.add_printer('fennec::list', '^fennec::list<.*>$', ListPrinter)
pp.add_printer('fennec::map', '^fennec::map<.*>$', MapPrinter)
pp.add_printer('fennec::object_pool', '^fennec::object_pool<.*>$', ObjectPoolPrinter)
pp.add_printer('fennec::optional', '^fennec::optional<.*>$', OptionalPrinter)
pp.add_printer('fennec::pair', '^fennec::pair<.*>$', PairPrinter)
pp.add_printer('fennec::set', '^fennec::set<.*>$', SetPrinter)
pp.add_printer('fennec::rdtree', '^fennec::rdtree<.*>$', RDTreePrinter)
pp.add_printer('fennec::bintree', '^fennec::bintree<.*>$', BinTreePrinter)
pp.add_printer('fennec::sequence', '^fennec::sequence<.*>$', SequencePrinter)
pp.add_printer('fennec::priority_queue', '^fennec::priority_queue<.*>$', PriorityQueuePrinter)
pp.add_printer('fennec::tuple', '^fennec::tuple<.*>$', TuplePrinter)
return pp
printer = register_printers()