- Added boost-atomic and boost-thread as dependencies for concurrency support
482 lines
15 KiB
Python
482 lines
15 KiB
Python
# ======================================================================================================================
|
||
# fennec, a free and open source game engine
|
||
# Copyright © 2025 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, i)
|
||
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)
|
||
|
||
|
||
# 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::tuple', '^fennec::tuple<.*>$', TuplePrinter)
|
||
return pp
|
||
|
||
printer = register_printers()
|