679 lines
21 KiB
Python
679 lines
21 KiB
Python
# ======================================================================================================================
|
||
# 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()
|