urt-rs / scripts /huffman_model.py
nmarshall's picture
Recode Huffman updates and derive provisional seeded codebook
bdf6c1f
Raw History Blame Contribute Delete
4.92 kB
#!/usr/bin/env python3
"""Provisional node-level recoding of observed Linux Huff_addRef/increment semantics."""
class Node:
__slots__=('index','left','right','parent','next','previous','head','weight','symbol')
def __init__(self,index,symbol=0,weight=0):
self.index=index;self.symbol=symbol;self.weight=weight
self.left=self.right=self.parent=self.next=self.previous=self.head=None
class Huffman:
def __init__(self,second=False):
self.nodes=[Node(0,256)];self.root=self.nyt=self.nodes[0];self.lookup={256:self.nyt};self.cells=[];self.free=None;self.second=second;self.events={}
def event(self,name):self.events[name]=self.events.get(name,0)+1
def allocate_cell(self,node):
if self.free is None:i=len(self.cells);self.cells.append(node);self.event('cell_allocate')
else:i=self.free;self.free=self.cells[i];self.cells[i]=node;self.event('cell_reuse')
return i
def free_cell(self,i):self.cells[i]=self.free;self.free=i;self.event('cell_free')
def swap_tree(self,a,b):
pa,pb=a.parent,b.parent
if pa:
if pa.left is a:pa.left=b
else:pa.right=b
else:self.root=b
if pb:
if pb.left is b:pb.left=a
else:pb.right=a
else:self.root=a
a.parent,b.parent=pb,pa;self.event('tree_swap')
def swap_list(self,a,b):
a.next,b.next=b.next,a.next;a.previous,b.previous=b.previous,a.previous
if a.next is a:a.next=b
if b.next is b:b.next=a
for n in [a,b]:
if n.next:n.next.previous=n
if n.previous:n.previous.next=n
self.event('list_swap')
def increment(self,node):
if node is None:self.event('null_increment');return
if node.next and node.next.weight==node.weight:
self.event('equal_next');leader=self.cells[node.head]
if leader is not node.parent:self.swap_tree(leader,node)
else:self.event('leader_is_parent')
self.swap_list(leader,node)
if node.previous and node.previous.weight==node.weight:self.cells[node.head]=node.previous;self.event('old_head_previous')
else:self.free_cell(node.head)
node.weight=(node.weight+1)&0xffffffff
if node.next and node.next.weight==node.weight:node.head=node.next.head;self.event('new_head_shared')
else:node.head=self.allocate_cell(node)
if node.parent:
self.increment(node.parent)
if node.previous is node.parent:
self.event('post_parent_adjacent');parent=node.parent;self.swap_list(node,parent)
if self.cells[node.head] is node:self.cells[node.head]=parent;self.event('post_parent_head')
def add(self,symbol):
symbol&=255
if symbol in self.lookup:self.event('existing');self.increment(self.lookup[symbol]);return
self.event('new');leaf=Node(len(self.nodes),symbol,1);internal=Node(len(self.nodes)+1,257,1);self.nodes.extend([leaf,internal]);nyt=self.nyt
internal.next=nyt.next
if internal.next:
internal.next.previous=internal
if internal.next.weight==1:internal.head=internal.next.head
else:internal.head=self.allocate_cell(internal)
else:internal.head=self.allocate_cell(internal)
nyt.next=internal;internal.previous=nyt
leaf.next=nyt.next;leaf.next.previous=leaf;leaf.head=leaf.next.head;nyt.next=leaf;leaf.previous=nyt
parent=nyt.parent
if parent:
if parent.left is nyt:parent.left=internal
else:parent.right=internal
else:self.root=internal
internal.left=nyt;internal.right=leaf;internal.parent=parent;leaf.parent=nyt.parent=internal;self.lookup[symbol]=leaf;self.increment(parent)
def codewords(self):
out={}
def walk(n,bits):
if n.symbol!=257:out[n.symbol]=bits;return
walk(n.left,bits+[0]);walk(n.right,bits+[1])
walk(self.root,[]);return out
def state(self,base=0x10000):
def addr(n):return 0 if n is None else base+0x830+56*n.index
def cell(i):return 0 if i is None else base+0xb030+8*i
nodes=[{'index':n.index,'address':addr(n),'left':addr(n.left),'right':addr(n.right),'parent':addr(n.parent),'next':addr(n.next),'previous':addr(n.previous),'head_cell':cell(n.head),'weight':n.weight,'symbol':n.symbol} for n in self.nodes]
heads=sorted({n.head for n in self.nodes if n.head is not None})
values=[addr(v) if isinstance(v,Node) else cell(v) for v in self.cells]
return {'node_count':len(nodes),'pointer_cell_count':len(self.cells),'root':addr(self.root),'list_start':addr(self.nyt),'tail_slot':addr(self.nyt) if self.second else 0,'free_head':cell(self.free),'nodes':nodes,'heads':[[cell(i),addr(self.cells[i])] for i in heads],'pointer_cells':values,'lookup':[[s,addr(n)] for s,n in sorted(self.lookup.items())]}