mirror of https://github.com/stella-emu/stella.git
In attempting to fix weird functionality with the built-in database,
I realized it didn't need to be implemented as a binary tree at all. So now it's a simple array in sorted order, and it's searched with a simple binary search algorithm. Fixes the bug I was running into, and saves over 115KB in executable size :) git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1319 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
parent
8874d38323
commit
28345a3a43
File diff suppressed because it is too large
Load Diff
|
@ -13,7 +13,7 @@
|
|||
// See the file "license" for information on usage and redistribution of
|
||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//
|
||||
// $Id: PropsSet.cxx,v 1.31 2007-01-01 18:04:49 stephena Exp $
|
||||
// $Id: PropsSet.cxx,v 1.32 2007-04-09 18:12:40 stephena Exp $
|
||||
//============================================================================
|
||||
|
||||
#include <sstream>
|
||||
|
@ -93,14 +93,16 @@ void PropertiesSet::getMD5(const string& md5, Properties& properties,
|
|||
properties = *(current->props);
|
||||
}
|
||||
|
||||
// Otherwise, search the internal BST array
|
||||
// Otherwise, search the internal database using binary search
|
||||
if(!found)
|
||||
{
|
||||
int i = 0;
|
||||
while(i < ARRAYSIZE(DefProps))
|
||||
int low = 0, high = ARRAYSIZE(DefProps) - 1;
|
||||
while(low <= high)
|
||||
{
|
||||
int i = (low + high) / 2;
|
||||
int cmp = strncmp(md5.c_str(), DefProps[i][Cartridge_MD5], 32);
|
||||
if(cmp == 0)
|
||||
|
||||
if(cmp == 0) // found it
|
||||
{
|
||||
for(int p = 0; p < LastPropType; ++p)
|
||||
if(DefProps[i][p][0] != 0)
|
||||
|
@ -110,9 +112,9 @@ void PropertiesSet::getMD5(const string& md5, Properties& properties,
|
|||
break;
|
||||
}
|
||||
else if(cmp < 0)
|
||||
i = 2*i + 1; // left child
|
||||
high = i - 1; // look at lower range
|
||||
else
|
||||
i = 2*i + 2; // right child
|
||||
low = i + 1; // look at upper range
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,328 +0,0 @@
|
|||
|
||||
package Btrees;
|
||||
$VERSION=1.00;
|
||||
|
||||
require 5.000;
|
||||
require Exporter;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Btrees - Binary trees using the AVL balancing method.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
# yes, do USE the package ...
|
||||
use Btrees;
|
||||
|
||||
# no constructors
|
||||
|
||||
# traverse a tree and invoke a function
|
||||
traverse( $tree, $func );
|
||||
|
||||
# add a node in a balanced tree, rebalancing if required
|
||||
($tree, $node) = bal_tree_add( $tree, $val, $cmp )
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Btrees uses the AVL balancing method, by G. M. Adelson-Velskii
|
||||
and E.M. Landis. Bit scavenging, as done in low level languages like
|
||||
C, is not used for height balancing since this is too expensive for
|
||||
an interpreter. Instead the actual height of each subtree is stored
|
||||
at each node. A null pointer has a height of zero. A leaf a height of
|
||||
1. A nonleaf a height of 1 greater than the height of its two children.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Ron Squiers (ron@broadcom.com). Adapted from "Mastering Algorithms with
|
||||
Perl" by Jon Orwant, Jarkko Hietaniemi & John Macdonald. Copyright
|
||||
1999 O'Reilly and Associates, Inc. All right reserved. ISBN: 1-56592-398-7
|
||||
|
||||
=cut
|
||||
|
||||
@ISA = qw(Exporter);
|
||||
@EXPORT = qw( bal_tree_add label_tree );
|
||||
|
||||
#########################################
|
||||
#
|
||||
# Method: label_tree
|
||||
#
|
||||
# label_tree( $tree, $func );
|
||||
#
|
||||
sub label_tree {
|
||||
my $tree = shift or return undef;
|
||||
my $func = shift or return undef;
|
||||
|
||||
$tree->{index} = 0;
|
||||
|
||||
# Label all nodes with their respective indices
|
||||
sub label_node {
|
||||
my $tree = shift;
|
||||
|
||||
&$func( $tree );
|
||||
|
||||
if (defined $tree->{left}) {
|
||||
$tree->{left}->{index} = 2 * $tree->{index} + 1;
|
||||
}
|
||||
if (defined $tree->{right}) {
|
||||
$tree->{right}->{index} = 2 * $tree->{index} + 2;
|
||||
}
|
||||
}
|
||||
preorder_traverse( $tree, \&label_node );
|
||||
}
|
||||
|
||||
#########################################
|
||||
#
|
||||
# Method: inorder_traverse
|
||||
#
|
||||
# Traverse $tree in order, calling $func() for each element.
|
||||
# in turn
|
||||
# inorder_traverse( $tree, $func );
|
||||
#
|
||||
sub inorder_traverse {
|
||||
my $tree = shift or return; # skip undef pointers
|
||||
my $func = shift;
|
||||
|
||||
inorder_traverse( $tree->{left}, $func );
|
||||
&$func( $tree );
|
||||
inorder_traverse( $tree->{right}, $func );
|
||||
}
|
||||
|
||||
#########################################
|
||||
#
|
||||
# Method: preorder_traverse
|
||||
#
|
||||
# Traverse $tree in preorder form, calling $func() for each element.
|
||||
# in turn
|
||||
# preorder_traverse( $tree, $func );
|
||||
#
|
||||
sub preorder_traverse {
|
||||
my $tree = shift or return; # skip undef pointers
|
||||
my $func = shift;
|
||||
|
||||
&$func( $tree );
|
||||
preorder_traverse( $tree->{left}, $func );
|
||||
preorder_traverse( $tree->{right}, $func );
|
||||
}
|
||||
|
||||
#########################################
|
||||
#
|
||||
# Method: bal_tree_add
|
||||
#
|
||||
# Search $tree looking for a node that has the value $val,
|
||||
# add it if it does not already exist.
|
||||
# If provided, $cmp compares values instead of <=>.
|
||||
#
|
||||
# ($tree, $node) = bal_tree_add( $tree, $val, $cmp )
|
||||
# the return values:
|
||||
# $tree points to the (possible new or changed) subtree that
|
||||
# has resulted from the add operation.
|
||||
# $node points to the (possibly new) node that contains $val
|
||||
#
|
||||
sub bal_tree_add {
|
||||
my( $tree, $val, $cmp) = @_;
|
||||
my $result;
|
||||
|
||||
unless ( $tree ) {
|
||||
$result = {
|
||||
left => undef,
|
||||
right => undef,
|
||||
val => $val,
|
||||
index => -1,
|
||||
height => 1
|
||||
};
|
||||
return( $result, $result );
|
||||
}
|
||||
|
||||
my $relation = defined $cmp
|
||||
? $cmp->( $val, $tree->{val} )
|
||||
: $val <=> $tree->{val};
|
||||
|
||||
### Stop when the desired node if found.
|
||||
return ( $tree, $tree ) if $relation == 0;
|
||||
|
||||
### Add to the correct subtree.
|
||||
if( $relation < 0 ) {
|
||||
($tree->{left}, $result) =
|
||||
bal_tree_add ( $tree->{left}, $val, $cmp );
|
||||
} else {
|
||||
($tree->{right}, $result) =
|
||||
bal_tree_add ( $tree->{right}, $val, $cmp );
|
||||
}
|
||||
|
||||
### Make sure that this level is balanced, return the
|
||||
### (possibly changed) top and the (possibly new) selected node.
|
||||
return ( balance_tree( $tree ), $result );
|
||||
}
|
||||
|
||||
#########################################
|
||||
#
|
||||
# Method: balance_tree
|
||||
#
|
||||
# Balance a potentially out of balance tree
|
||||
#
|
||||
# the return values:
|
||||
# $tree points to the balanced tree root
|
||||
#
|
||||
sub balance_tree {
|
||||
### An empty tree is balanced already.
|
||||
my $tree = shift or return undef;
|
||||
|
||||
### An empty link is height 0.
|
||||
my $lh = defined $tree->{left} && $tree->{left}{height};
|
||||
my $rh = defined $tree->{right} && $tree->{right}{height};
|
||||
|
||||
### Rebalance if needed, return the (possibly changed) root.
|
||||
if ( $lh > 1+$rh ) {
|
||||
return swing_right( $tree );
|
||||
} elsif ( $lh+1 < $rh ) {
|
||||
return swing_left( $tree );
|
||||
} else {
|
||||
### Tree is either perfectly balanced or off by one.
|
||||
### Just fix its height.
|
||||
set_height( $tree );
|
||||
return $tree;
|
||||
}
|
||||
}
|
||||
|
||||
#########################################
|
||||
#
|
||||
# Method: set_height
|
||||
#
|
||||
# Set height of a node
|
||||
#
|
||||
sub set_height {
|
||||
my $tree = shift;
|
||||
|
||||
my $p;
|
||||
### get heights, an undef node is height 0.
|
||||
my $lh = defined ( $p = $tree->{left} ) && $p->{height};
|
||||
my $rh = defined ( $p = $tree->{right} ) && $p->{height};
|
||||
$tree->{height} = $lh < $rh ? $rh+1 : $lh+1;
|
||||
}
|
||||
|
||||
#########################################
|
||||
#
|
||||
# Method: $tree = swing_left( $tree )
|
||||
#
|
||||
# Change t to r or rl
|
||||
# / \ / \ / \
|
||||
# l r t rr t r
|
||||
# / \ / \ / \ / \
|
||||
# rl rr l rl l rll rlr rr
|
||||
# / \ / \
|
||||
# rll rlr rll rlr
|
||||
#
|
||||
# t and r must both exist.
|
||||
# The second form is used if height of rl is greater than height of rr
|
||||
# (since the form would then lead to the height of t at least 2 more
|
||||
# than the height of rr).
|
||||
#
|
||||
# changing to the second form is done in two steps, with first a move_right(r)
|
||||
# and then a move_left(t), so it goes:
|
||||
#
|
||||
# Change t to t and then to rl
|
||||
# / \ / \ / \
|
||||
# l r l rl t r
|
||||
# / \ / \ / \ / \
|
||||
# rl rr rll r l rll rlr rr
|
||||
# / \ / \
|
||||
# rll rlr rlr rr
|
||||
#
|
||||
sub swing_left {
|
||||
my $tree = shift;
|
||||
|
||||
my $r = $tree->{right}; # must exist
|
||||
my $rl = $r->{left}; # might exist
|
||||
my $rr = $r->{right}; # might exist
|
||||
my $l = $tree->{left}; # might exist
|
||||
|
||||
### get heights, an undef node has height 0
|
||||
my $lh = $l && $l->{height} || 0;
|
||||
my $rlh = $rl && $rl->{height} || 0;
|
||||
my $rrh = $rr && $rr->{height} || 0;
|
||||
|
||||
if ( $rlh > $rrh ) {
|
||||
$tree->{right} = move_right( $r );
|
||||
}
|
||||
|
||||
return move_left( $tree );
|
||||
}
|
||||
|
||||
# and the opposite swing
|
||||
|
||||
sub swing_right {
|
||||
my $tree = shift;
|
||||
|
||||
my $l = $tree->{left}; # must exist
|
||||
my $lr = $l->{right}; # might exist
|
||||
my $ll = $l->{left}; # might exist
|
||||
my $r = $tree->{right}; # might exist
|
||||
|
||||
### get heights, an undef node has height 0
|
||||
my $rh = $r && $r->{height} || 0;
|
||||
my $lrh = $lr && $lr->{height} || 0;
|
||||
my $llh = $ll && $ll->{height} || 0;
|
||||
|
||||
if ( $lrh > $llh ) {
|
||||
$tree->{left} = move_left( $l );
|
||||
}
|
||||
|
||||
return move_right( $tree );
|
||||
}
|
||||
|
||||
#########################################
|
||||
#
|
||||
# Method: $tree = move_left( $tree )
|
||||
#
|
||||
# Change t to r
|
||||
# / \ / \
|
||||
# l r t rr
|
||||
# / \ / \
|
||||
# rl rr l rl
|
||||
#
|
||||
# caller has determined that t and r both exist
|
||||
# (l can be undef, so can one of rl and rr)
|
||||
#
|
||||
sub move_left {
|
||||
my $tree = shift;
|
||||
my $r = $tree->{right};
|
||||
my $rl = $r->{left};
|
||||
|
||||
$tree->{right} = $rl;
|
||||
$r->{left} = $tree;
|
||||
set_height( $tree );
|
||||
set_height( $r );
|
||||
return $r;
|
||||
}
|
||||
|
||||
#########################################
|
||||
#
|
||||
# Method: $tree = move_right( $tree )
|
||||
#
|
||||
# Change t to l
|
||||
# / \ / \
|
||||
# l r ll t
|
||||
# / \ / \
|
||||
# ll lr lr r
|
||||
#
|
||||
# caller has determined that t and l both exist
|
||||
# (r can be undef, so can one of ll and lr)
|
||||
#
|
||||
sub move_right {
|
||||
my $tree = shift;
|
||||
my $l = $tree->{left};
|
||||
my $lr = $l->{right};
|
||||
|
||||
$tree->{left} = $lr;
|
||||
$l->{right} = $tree;
|
||||
set_height( $tree );
|
||||
set_height( $l );
|
||||
return $l;
|
||||
}
|
||||
|
||||
#########################################
|
||||
# That's all folks ...
|
||||
#########################################
|
||||
#
|
||||
1; # so that use() returns true
|
||||
|
|
@ -1,10 +1,7 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
use Btrees;
|
||||
|
||||
my @props = ();
|
||||
my @propset = ();
|
||||
my @propset_ordered = ();
|
||||
my %propset = ();
|
||||
|
||||
my %proptype = (
|
||||
"Cartridge.MD5" => 0,
|
||||
|
@ -73,7 +70,9 @@ foreach $line (<INFILE>) {
|
|||
|
||||
# Start a new item
|
||||
if ($line =~ /^""/) {
|
||||
push @propset, [ @props ];
|
||||
my $key = $props[$proptype{'Cartridge.MD5'}];
|
||||
# print "Inserting properties for key = $key\n";
|
||||
$propset{$key} = [ @props ];
|
||||
|
||||
undef @props;
|
||||
while(($key, $value) = each(%proptype)) {
|
||||
|
@ -90,27 +89,8 @@ foreach $line (<INFILE>) {
|
|||
}
|
||||
}
|
||||
|
||||
# Fill the AVL tree with property indices
|
||||
my $tree;
|
||||
for($i = 0; $i < @propset; $i++) {
|
||||
my $value = $propset[$i][$proptype{'Cartridge.MD5'}];
|
||||
# printf "Adding $value to tree\n";
|
||||
($tree, $node) = bal_tree_add($tree, $i, \&compare);
|
||||
}
|
||||
printf "\n";
|
||||
|
||||
# Fill the results tree with the appropriate number of items
|
||||
my $height = $tree->{height};
|
||||
my $size = 2 ** $height - 1;
|
||||
printf "Tree has height = $height, output BST array will have size = $size\n";
|
||||
for($i = 0; $i < $size; $i++) {
|
||||
$propset_ordered[$i] = -1;
|
||||
}
|
||||
|
||||
# Label the tree nodes by index into a BST array
|
||||
label_tree($tree, \&store);
|
||||
printf "\n";
|
||||
|
||||
my $size = keys (%propset);
|
||||
printf "Valid properties found: $size\n";
|
||||
|
||||
# Construct the output file in C++ format
|
||||
# Walk the results array and print each item
|
||||
|
@ -124,20 +104,20 @@ print OUTFILE " located in the src/tools directory. All properties changes\n";
|
|||
print OUTFILE " should be made in stella.pro, and then this file should be\n";
|
||||
print OUTFILE " regenerated and the application recompiled.\n";
|
||||
print OUTFILE "*/\n";
|
||||
print OUTFILE "static const char* DefProps[][" . keys( %proptype ) . "] = {\n";
|
||||
for ($i = 0; $i < @propset_ordered; $i++) {
|
||||
my $idx = $propset_ordered[$i];
|
||||
if ($idx != -1) {
|
||||
print OUTFILE get_prop($idx);
|
||||
} else {
|
||||
print OUTFILE blank_prop();
|
||||
}
|
||||
print OUTFILE "static const char* DefProps[" . $size . "][" . keys( %proptype ) . "] = {\n";
|
||||
|
||||
if ($i+1 < @propset_ordered) {
|
||||
my $idx = 0;
|
||||
for my $key ( sort keys %propset )
|
||||
{
|
||||
print OUTFILE build_prop_string(@{ $propset{$key} });
|
||||
|
||||
if ($idx+1 < $size) {
|
||||
print OUTFILE ", ";
|
||||
}
|
||||
print OUTFILE "\n";
|
||||
$idx++;
|
||||
}
|
||||
|
||||
print OUTFILE "};\n";
|
||||
print OUTFILE "\n";
|
||||
print OUTFILE "#endif\n";
|
||||
|
@ -151,35 +131,9 @@ sub usage {
|
|||
exit(0);
|
||||
}
|
||||
|
||||
sub compare {
|
||||
my ($first, $second) = @_;
|
||||
|
||||
my $first_md5 = $propset[$first][$proptype{'Cartridge.MD5'}];
|
||||
my $second_md5 = $propset[$second][$proptype{'Cartridge.MD5'}];
|
||||
|
||||
if ($first_md5 lt $second_md5) {
|
||||
return -1;
|
||||
} elsif ($first_md5 gt $second_md5) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
sub store {
|
||||
my $tree = shift;
|
||||
|
||||
$propset_ordered[$tree->{index}] = $tree->{val};
|
||||
}
|
||||
|
||||
sub get_prop {
|
||||
my $idx = shift;
|
||||
|
||||
my $arr = $propset[$idx];
|
||||
my @array = @$arr;
|
||||
|
||||
sub build_prop_string {
|
||||
my @array = @_;
|
||||
my $result = " { ";
|
||||
|
||||
my @items = ();
|
||||
for (my $i = 0; $i < @array; $i++) {
|
||||
if($prop_defaults[$i] ne $array[$i]) {
|
||||
|
@ -194,20 +148,3 @@ sub get_prop {
|
|||
|
||||
return $result;
|
||||
}
|
||||
|
||||
sub blank_prop {
|
||||
my $arr = $propset[$idx];
|
||||
my @array = @$arr;
|
||||
|
||||
my $result = " { ";
|
||||
|
||||
my @items = ();
|
||||
for my $key ( keys %proptype) {
|
||||
push(@items, "\"\"");
|
||||
}
|
||||
|
||||
$result .= join(", ", @items);
|
||||
$result .= " }";
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue