1#!/usr/bin/perl -w 2# 3# Copyright (C) 2016 Julian Andres Klode <jak@jak-linux.org> 4# 5# Permission is hereby granted, free of charge, to any person obtaining a copy 6# of this software and associated documentation files (the "Software"), to deal 7# in the Software without restriction, including without limitation the rights 8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9# copies of the Software, and to permit persons to whom the Software is 10# furnished to do so, subject to the following conditions: 11# 12# The above copyright notice and this permission notice shall be included in 13# all copies or substantial portions of the Software. 14# 15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21# THE SOFTWARE. 22 23=encoding utf8 24 25=head1 NAME 26 27triehash - Generate a perfect hash function derived from a trie. 28 29=cut 30 31use strict; 32use warnings; 33use utf8; 34use Getopt::Long; 35 36=head1 SYNOPSIS 37 38B<triehash> [S<I<option>>] [S<I<input file>>] 39 40=head1 DESCRIPTION 41 42triehash takes a list of words in input file and generates a function and 43an enumeration to describe the word 44 45=head1 INPUT FILE FORMAT 46 47The file consists of multiple lines of the form: 48 49 [label ~ ] word [= value] 50 51This maps word to value, and generates an enumeration with entries of the form: 52 53 label = value 54 55If I<label> is undefined, the word will be used, the minus character will be 56replaced by an underscore. If value is undefined it is counted upwards from 57the last value. 58 59There may also be one line of the format 60 61 [ label ~] = value 62 63Which defines the value to be used for non-existing keys. Note that this also 64changes default value for other keys, as for normal entries. So if you place 65 66 = 0 67 68at the beginning of the file, unknown strings map to 0, and the other strings 69map to values starting with 1. If label is not specified, the default is 70I<Unknown>. 71 72=head1 OPTIONS 73 74=over 4 75 76=item B<-C>I<.c file> B<--code>=I<.c file> 77 78Generate code in the given file. 79 80=item B<-H>I<header file> B<--header>=I<header file> 81 82Generate a header in the given file, containing a declaration of the hash 83function and an enumeration. 84 85=item B<--enum-name=>I<word> 86 87The name of the enumeration. 88 89=item B<--function-name=>I<word> 90 91The name of the function. 92 93=item B<--label-prefix=>I<word> 94 95The prefix to use for labels. 96 97=item B<--label-uppercase> 98 99Uppercase label names when normalizing them. 100 101=item B<--namespace=>I<name> 102 103Put the function and enum into a namespace (C++) 104 105=item B<--class=>I<name> 106 107Put the function and enum into a class (C++) 108 109=item B<--enum-class> 110 111Generate an enum class instead of an enum (C++) 112 113=item B<--counter-name=>I<name> 114 115Use I<name> for a counter that is set to the latest entry in the enumeration 116+ 1. This can be useful for defining array sizes. 117 118=item B<--ignore-case> 119 120Ignore case for words. 121 122=item B<--multi-byte>=I<value> 123 124Generate code reading multiple bytes at once. The value is a string of power 125of twos to enable. The default value is 320 meaning that 8, 4, and single byte 126reads are enabled. Specify 0 to disable multi-byte completely, or add 2 if you 127also want to allow 2-byte reads. 2-byte reads are disabled by default because 128they negatively affect performance on older Intel architectures. 129 130This generates code for both multiple bytes and single byte reads, but only 131enables the multiple byte reads of GNU C compatible compilers, as the following 132extensions are used: 133 134=over 8 135 136=item Byte-aligned integers 137 138We must be able to generate integers that are aligned to a single byte using: 139 140 typedef uint64_t __attribute__((aligned (1))) triehash_uu64; 141 142=item Byte-order 143 144The macros __BYTE_ORDER__ and __ORDER_LITTLE_ENDIAN__ must be defined. 145 146=back 147 148We forcefully disable multi-byte reads on platforms where the variable 149I<__ARM_ARCH> is defined and I<__ARM_FEATURE_UNALIGNED> is not defined, 150as there is a measurable overhead from emulating the unaligned reads on 151ARM. 152 153=item B<--language=>I<language> 154 155Generate a file in the specified language. Currently known are 'C' and 'tree', 156the latter generating a tree. 157 158=item B<--include=>I<header> 159 160Add the header to the include statements of the header file. The value must 161be surrounded by quotes or angle brackets for C code. May be specified multiple 162times. 163 164=back 165 166=cut 167 168my $unknown = -1; 169my $unknown_label = undef; 170my $counter_start = 0; 171my $enum_name = 'PerfectKey'; 172my $function_name = 'PerfectHash'; 173my $enum_class = 0; 174 175my $code_name = '-'; 176my $header_name = '-'; 177my $code; 178my $header; 179my $label_prefix = undef; 180my $label_uppercase = 0; 181my $ignore_case = 0; 182my $multi_byte = '320'; 183my $language = 'C'; 184my $counter_name = undef; 185my @includes = (); 186 187 188Getopt::Long::config('default', 189 'bundling', 190 'no_getopt_compat', 191 'no_auto_abbrev', 192 'permute', 193 'auto_help'); 194 195GetOptions ('code|C=s' => \$code_name, 196 'header|H=s' => \$header_name, 197 'function-name=s' => \$function_name, 198 'label-prefix=s' => \$label_prefix, 199 'label-uppercase' => \$label_uppercase, 200 'ignore-case' => \$ignore_case, 201 'enum-name=s' => \$enum_name, 202 'language|l=s' => \$language, 203 'multi-byte=s' => \$multi_byte, 204 'enum-class' => \$enum_class, 205 'include=s' => \@includes, 206 'counter-name=s' => \$counter_name) 207 or die('Could not parse options!'); 208 209 210# This implements a simple trie. Each node has three attributes: 211# 212# children - A hash of keys to other nodes 213# value - The value to be stored here 214# label - A named representation of the value. 215# 216# The key at each level of the trie can consist of one or more bytes, and the 217# trie can be normalized to a form where all keys at a level have the same 218# length using rebuild_tree(). 219package Trie { 220 221 sub new { 222 my $class = shift; 223 my $self = {}; 224 bless $self, $class; 225 226 $self->{children} = {}; 227 $self->{value} = undef; 228 $self->{label} = undef; 229 230 return $self; 231 } 232 233 # Return the largest power of 2 smaller or equal to the argument 234 sub alignpower2 { 235 my ($self, $length) = @_; 236 237 return 8 if ($length >= 8 && $multi_byte =~ /3/); 238 return 4 if ($length >= 4 && $multi_byte =~ /2/); 239 return 2 if ($length >= 2 && $multi_byte =~ /1/); 240 241 return 1; 242 } 243 244 # Split the key into a head block and a tail 245 sub split_key { 246 my ($self, $key) = @_; 247 my $length = length $key; 248 my $split = $self->alignpower2($length); 249 250 return (substr($key, 0, $split), substr($key, $split)); 251 } 252 253 # Given a key, a label, and a value, insert that into the tree, possibly 254 # replacing an existing node. 255 sub insert { 256 my ($self, $key, $label, $value) = @_; 257 258 if (length($key) == 0) { 259 $self->{label} = $label; 260 $self->{value} = $value; 261 return; 262 } 263 264 my ($child, $tail) = $self->split_key($key); 265 266 $self->{children}{$child} = Trie->new if (!defined($self->{children}{$child})); 267 268 $self->{children}{$child}->insert($tail, $label, $value); 269 } 270 271 # Construct a new trie that only contains words of a given length. This 272 # is used to split up the common trie after knowing all words, so we can 273 # switch on the expected word length first, and have the per-trie function 274 # implement simple longest prefix matching. 275 sub filter_depth { 276 my ($self, $togo) = @_; 277 278 my $new = Trie->new; 279 280 if ($togo != 0) { 281 my $found = 0; 282 foreach my $key (sort keys %{$self->{children}}) { 283 if ($togo > length($key) || defined $self->{children}{$key}->{value}) { 284 my $child = $self->{children}{$key}->filter_depth($togo - length($key)); 285 286 $new->{children}{$key}= $child if defined $child; 287 $found = 1 if defined $child; 288 } 289 } 290 return if (!$found); 291 } else { 292 $new->{value} = $self->{value}; 293 $new->{label} = $self->{label}; 294 } 295 296 return $new; 297 } 298 299 # (helper for rebuild_tree) 300 # Reinsert all value nodes into the specified $trie, prepending $prefix 301 # to their $paths. 302 sub reinsert_value_nodes_into { 303 my ($self, $trie, $prefix) = @_; 304 305 $trie->insert($prefix, $self->{label}, $self->{value}) if (defined $self->{value}); 306 307 foreach my $key (sort keys %{$self->{children}}) { 308 $self->{children}{$key}->reinsert_value_nodes_into($trie, $prefix . $key); 309 } 310 } 311 312 # (helper for rebuild_tree) 313 # Find the earliest point to split a key. Normally, we split at the maximum 314 # power of 2 that is greater or equal than the length of the key. When we 315 # are building an ASCII-optimised case-insensitive trie that simply ORs 316 # each byte with 0x20, we need to split at the first ambiguous character: 317 # 318 # For example, the words a-bc and a\rbc are identical in such a situation: 319 # '-' | 0x20 == '-' == '\r' | 0x20 320 # We cannot simply switch on all 4 bytes at once, but need to split before 321 # the ambiguous character so we can process the ambiguous character on its 322 # own. 323 sub find_earlier_split { 324 my ($self, $key) = @_; 325 326 if ($ignore_case) { 327 for my $i (0..length($key)-1) { 328 # If the key starts with an ambiguous character, we need to 329 # take only it. Otherwise, we need to take everything 330 # before the character. 331 return $self->alignpower2($i || 1) if (main::ambiguous(substr($key, $i, 1))); 332 } 333 } 334 return $self->alignpower2(length $key); 335 } 336 337 # This rebuilds the trie, splitting each key before ambiguous characters 338 # as explained in find_earlier_split(), and then chooses the smallest 339 # such split at each level, so that all keys at all levels have the same 340 # length (so we can use a multi-byte switch). 341 sub rebuild_tree { 342 my $self = shift; 343 # Determine if/where we need to split before an ambiguous character 344 my $new_split = 99999999999999999; 345 foreach my $key (sort keys %{$self->{children}}) { 346 my $special_length = $self->find_earlier_split($key); 347 $new_split = $special_length if ($special_length < $new_split); 348 } 349 350 # Start building a new uniform trie 351 my $newself = Trie->new; 352 $newself->{label} = $self->{label}; 353 $newself->{value} = $self->{value}; 354 $newself->{children} = {}; 355 356 foreach my $key (sort keys %{$self->{children}}) { 357 my $head = substr($key, 0, $new_split); 358 my $tail = substr($key, $new_split); 359 # Rebuild the child node at $head, pushing $tail downwards 360 $newself->{children}{$head} //= Trie->new; 361 $self->{children}{$key}->reinsert_value_nodes_into($newself->{children}{$head}, $tail); 362 # We took up to one special character of each key label. There might 363 # be more, so we need to rebuild recursively. 364 $newself->{children}{$head} = $newself->{children}{$head}->rebuild_tree(); 365 } 366 367 return $newself; 368 } 369} 370 371# Code generator for C and C++ 372package CCodeGen { 373 my $static = ($code_name eq $header_name) ? "static " : ""; 374 my $enum_specifier = $enum_class ? "enum class" : "enum"; 375 376 sub new { 377 my $class = shift; 378 my $self = {}; 379 bless $self, $class; 380 381 return $self; 382 } 383 384 sub open_output { 385 my $self = shift; 386 if ($code_name ne '-') { 387 open($code, '>', $code_name) or die "Cannot open $code_name: $!" ; 388 } else { 389 $code = *STDOUT; 390 } 391 if($code_name eq $header_name) { 392 $header = $code; 393 } elsif ($header_name ne '-') { 394 open($header, '>', $header_name) or die "Cannot open $header_name: $!" ; 395 } else { 396 $header = *STDOUT; 397 } 398 } 399 400 sub mangle_label { 401 my ($self, $label) = @_; 402 403 $label = $label_prefix . $label if defined($label_prefix); 404 $label = uc $label if $label_uppercase; 405 406 return $label; 407 } 408 409 sub word_to_label { 410 my ($self, $word) = @_; 411 412 $word =~ s/_/__/g; 413 $word =~ s/-/_/g; 414 415 return $self->mangle_label($word); 416 } 417 418 # Return a case label, by shifting and or-ing bytes in the word 419 sub case_label { 420 my ($self, $key) = @_; 421 422 return sprintf("'%s'", substr($key, 0, 1)) if not $multi_byte; 423 424 my $output = '0'; 425 426 for my $i (0..length($key)-1) { 427 $output .= sprintf("| onechar('%s', %d, %d)", substr($key, $i, 1), 8 * $i, 8*length($key)); 428 } 429 430 return $output; 431 } 432 433 # Return an appropriate read instruction for $length bytes from $offset 434 sub switch_key { 435 my ($self, $offset, $length) = @_; 436 437 return "string[$offset]" if $length == 1; 438 return sprintf("*((triehash_uu%s*) &string[$offset])", $length * 8); 439 } 440 441 # Render the trie so that it matches the longest prefix. 442 sub print_table { 443 my ($self, $trie, $fh, $indent, $index) = @_; 444 $indent //= 0; 445 $index //= 0; 446 447 # If we have children, try to match them. 448 if (%{$trie->{children}}) { 449 # The difference between lowercase and uppercase alphabetical characters 450 # is that they have one bit flipped. If we have alphabetical characters 451 # in the search space, and the entire search space works fine if we 452 # always turn on the flip, just OR the character we are switching over 453 # with the bit. 454 my $want_use_bit = 0; 455 my $can_use_bit = 1; 456 my $key_length = 0; 457 foreach my $key (sort keys %{$trie->{children}}) { 458 $can_use_bit &= not main::ambiguous($key); 459 $want_use_bit |= ($key =~ /^[a-zA-Z]+$/); 460 $key_length = length($key); 461 } 462 463 if ($ignore_case && $can_use_bit && $want_use_bit) { 464 printf { $fh } ((' ' x $indent) . "switch(%s | 0x%s) {\n", $self->switch_key($index, $key_length), '20' x $key_length); 465 } else { 466 printf { $fh } ((' ' x $indent) . "switch(%s) {\n", $self->switch_key($index, $key_length)); 467 } 468 469 my $notfirst = 0; 470 foreach my $key (sort keys %{$trie->{children}}) { 471 if ($notfirst) { 472 printf { $fh } (' ' x $indent . " break;\n"); 473 } 474 if ($ignore_case) { 475 printf { $fh } (' ' x $indent . "case %s:\n", $self->case_label(lc($key))); 476 printf { $fh } (' ' x $indent . "case %s:\n", $self->case_label(uc($key))) if lc($key) ne uc($key) && !($can_use_bit && $want_use_bit); 477 } else { 478 printf { $fh } (' ' x $indent . "case %s:\n", $self->case_label($key)); 479 } 480 481 $self->print_table($trie->{children}{$key}, $fh, $indent + 1, $index + length($key)); 482 483 $notfirst=1; 484 } 485 486 printf { $fh } (' ' x $indent . "}\n"); 487 } 488 489 490 # This node has a value, so it is a possible end point. If no children 491 # matched, we have found our longest prefix. 492 if (defined $trie->{value}) { 493 printf { $fh } (' ' x $indent . "return %s;\n", ($enum_class ? "${enum_name}::" : '').$trie->{label}); 494 } 495 496 } 497 498 sub print_words { 499 my ($self, $trie, $fh, $indent, $sofar) = @_; 500 501 $indent //= 0; 502 $sofar //= ''; 503 504 505 printf { $fh } (' ' x $indent."%s = %s,\n", $trie->{label}, $trie->{value}) if defined $trie->{value}; 506 507 foreach my $key (sort keys %{$trie->{children}}) { 508 $self->print_words($trie->{children}{$key}, $fh, $indent, $sofar . $key); 509 } 510 } 511 512 sub print_functions { 513 my ($self, $trie, %lengths) = @_; 514 foreach my $local_length (sort { $a <=> $b } (keys %lengths)) { 515 print { $code } ("static enum ${enum_name} ${function_name}${local_length}(const char *string)\n"); 516 print { $code } ("{\n"); 517 $self->print_table($trie->filter_depth($local_length)->rebuild_tree(), $code, 1); 518 printf { $code } (" return %s$unknown_label;\n", ($enum_class ? "${enum_name}::" : '')); 519 print { $code } ("}\n"); 520 } 521 } 522 523 sub main { 524 my ($self, $trie, $num_values, %lengths) = @_; 525 print { $header } ("#ifndef TRIE_HASH_${function_name}\n"); 526 print { $header } ("#define TRIE_HASH_${function_name}\n"); 527 print { $header } ("#include <stddef.h>\n"); 528 print { $header } ("#include <stdint.h>\n"); 529 foreach my $include (@includes) { 530 print { $header } ("#include $include\n"); 531 } 532 printf { $header } ("enum { $counter_name = $num_values };\n") if (defined($counter_name)); 533 print { $header } ("${enum_specifier} ${enum_name} {\n"); 534 $self->print_words($trie, $header, 1); 535 printf { $header } (" $unknown_label = $unknown,\n"); 536 print { $header } ("};\n"); 537 print { $header } ("${static}enum ${enum_name} ${function_name}(const char *string, size_t length);\n"); 538 539 print { $code } ("#include \"$header_name\"\n") if ($header_name ne $code_name); 540 541 if ($multi_byte) { 542 print { $code } ("#ifdef __GNUC__\n"); 543 foreach my $i ((16, 32, 64)) { 544 print { $code } ("typedef uint${i}_t __attribute__((aligned (1))) triehash_uu${i};\n"); 545 print { $code } ("typedef char static_assert${i}[__alignof__(triehash_uu${i}) == 1 ? 1 : -1];\n"); 546 } 547 548 print { $code } ("#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\n"); 549 print { $code } ("#define onechar(c, s, l) (((uint64_t)(c)) << (s))\n"); 550 print { $code } ("#else\n"); 551 print { $code } ("#define onechar(c, s, l) (((uint64_t)(c)) << (l-8-s))\n"); 552 print { $code } ("#endif\n"); 553 print { $code } ("#if (!defined(__ARM_ARCH) || defined(__ARM_FEATURE_UNALIGNED)) && !defined(TRIE_HASH_NO_MULTI_BYTE)\n"); 554 print { $code } ("#define TRIE_HASH_MULTI_BYTE\n"); 555 print { $code } ("#endif\n"); 556 print { $code } ("#endif /*GNUC */\n"); 557 558 print { $code } ("#ifdef TRIE_HASH_MULTI_BYTE\n"); 559 $self->print_functions($trie, %lengths); 560 $multi_byte = 0; 561 print { $code } ("#else\n"); 562 $self->print_functions($trie, %lengths); 563 print { $code } ("#endif /* TRIE_HASH_MULTI_BYTE */\n"); 564 } else { 565 $self->print_functions($trie, %lengths); 566 } 567 568 print { $code } ("${static}enum ${enum_name} ${function_name}(const char *string, size_t length)\n"); 569 print { $code } ("{\n"); 570 print { $code } (" switch (length) {\n"); 571 foreach my $local_length (sort { $a <=> $b } (keys %lengths)) { 572 print { $code } (" case $local_length:\n"); 573 print { $code } (" return ${function_name}${local_length}(string);\n"); 574 } 575 print { $code } (" default:\n"); 576 printf { $code } (" return %s$unknown_label;\n", ($enum_class ? "${enum_name}::" : '')); 577 print { $code } (" }\n"); 578 print { $code } ("}\n"); 579 580 # Print end of header here, in case header and code point to the same file 581 print { $header } ("#endif /* TRIE_HASH_${function_name} */\n"); 582 } 583} 584 585# A character is ambiguous if the 1<<5 (0x20) bit does not correspond to the 586# lower case bit. A word is ambiguous if any character is. This definition is 587# used to check if we can perform the |0x20 optimization when building a case- 588# insensitive trie. 589sub ambiguous { 590 my $word = shift; 591 592 foreach my $char (split //, $word) { 593 # If 0x20 does not solely indicate lowercase, it is ambiguous 594 return 1 if ord(lc($char)) != (ord($char) | 0x20); 595 return 1 if ord(uc($char)) != (ord($char) & ~0x20); 596 } 597 598 return 0; 599} 600 601sub build_trie { 602 my $codegen = shift; 603 my $trie = Trie->new; 604 605 my $counter = $counter_start; 606 my $prev_value; 607 my %lengths; 608 609 open(my $input, '<', $ARGV[0]) or die "Cannot open $ARGV[0]: $!"; 610 while (my $line = <$input>) { 611 my ($label, $word, $value) = $line =~ m{ 612 (?:\s*([^~\s]+)\s*~)? # Label ~ 613 (?:\s*([^~=\s]+))? # Word 614 (?:\s*=\s*([^\s]+)\s+)? # = Value 615 \s* 616 }x; 617 618 if (defined $word) { 619 $label //= $codegen->word_to_label($word); 620 $value //= defined $prev_value ? $prev_value + 1 : 0; 621 622 $trie->insert($word, $label, $value); 623 $lengths{length($word)} = 1; 624 } elsif (defined $value) { 625 $unknown = $value; 626 $unknown_label = $codegen->mangle_label($label) if defined $label; 627 } else { 628 die "Invalid line: $line"; 629 } 630 631 $prev_value = $value; 632 $counter = $value + 1 if $value >= $counter; 633 } 634 635 $unknown_label //= $codegen->mangle_label('Unknown'); 636 637 return ($trie, $counter, %lengths); 638} 639 640# Generates an ASCII art tree 641package TreeCodeGen { 642 643 sub new { 644 my $class = shift; 645 my $self = {}; 646 bless $self, $class; 647 648 return $self; 649 } 650 651 sub mangle_label { 652 my ($self, $label) = @_; 653 return $label; 654 } 655 656 sub word_to_label { 657 my ($self, $word) = @_; 658 return $word; 659 } 660 661 sub main { 662 my ($self, $trie, $counter, %lengths) = @_; 663 printf { $code } ("┌────────────────────────────────────────────────────┐\n"); 664 printf { $code } ("│ Initial trie │\n"); 665 printf { $code } ("└────────────────────────────────────────────────────┘\n"); 666 $self->print($trie); 667 printf { $code } ("┌────────────────────────────────────────────────────┐\n"); 668 printf { $code } ("│ Rebuilt trie │\n"); 669 printf { $code } ("└────────────────────────────────────────────────────┘\n"); 670 $self->print($trie->rebuild_tree()); 671 672 foreach my $local_length (sort { $a <=> $b } (keys %lengths)) { 673 printf { $code } ("┌────────────────────────────────────────────────────┐\n"); 674 printf { $code } ("│ Trie for words of length %-4d │\n", $local_length); 675 printf { $code } ("└────────────────────────────────────────────────────┘\n"); 676 $self->print($trie->filter_depth($local_length)->rebuild_tree()); 677 } 678 } 679 680 sub open_output { 681 my $self = shift; 682 if ($code_name ne '-') { 683 open($code, '>:encoding(utf8)', $code_name) or die "Cannot open $ARGV[0]: $!" ; 684 } else { 685 $code = *STDOUT; 686 binmode($code, ':encoding(utf8)'); 687 } 688 } 689 690 # Print a trie 691 sub print { 692 my ($self, $trie, $depth) = @_; 693 $depth //= 0; 694 695 print { $code } (' → ') if defined($trie->{label}); 696 print { $code } ($trie->{label} // '', "\n"); 697 foreach my $key (sort keys %{$trie->{children}}) { 698 print { $code } ('│ ' x ($depth), "├── $key"); 699 $self->print($trie->{children}{$key}, $depth + 1); 700 } 701 } 702} 703 704my %codegens = ( 705 C => 'CCodeGen', 706 tree => 'TreeCodeGen', 707); 708 709 710defined($codegens{$language}) or die "Unknown language $language. Valid choices: ", join(', ', keys %codegens); 711my $codegen = $codegens{$language}->new(); 712my ($trie, $counter, %lengths) = build_trie($codegen); 713 714$codegen->open_output(); 715$codegen->main($trie, $counter, %lengths); 716 717 718=head1 LICENSE 719 720triehash is available under the MIT/Expat license, see the source code 721for more information. 722 723=head1 AUTHOR 724 725Julian Andres Klode <jak@jak-linux.org> 726 727=cut 728 729