File Coverage

File:triehash.pl
Coverage:95.5%

linestmtbrancondsubpodtimecode
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
24 - 28
=head1 NAME

triehash - Generate a perfect hash function derived from a trie.

=cut
29
30
18
18
18
157303
25
622
use strict;
31
18
18
18
65
12
500
use warnings;
32
18
18
18
6288
188351
57
use Getopt::Long;
33
34 - 158
=head1 SYNOPSIS

B<triehash> [S<I<option>>] [S<I<input file>>]

=head1 DESCRIPTION

triehash takes a list of words in input file and generates a function and
an enumeration to describe the word

=head1 INPUT FILE FORMAT

The file consists of multiple lines of the form:

    [label ~ ] word [= value]

This maps word to value, and generates an enumeration with entries of the form:

    label = value

If I<label> is undefined, the word will be used, the minus character will be
replaced by an underscore. If value is undefined it is counted upwards from
the last value.

There may also be one line of the format

    [ label ~] = value

Which defines the value to be used for non-existing keys. Note that this also
changes default value for other keys, as for normal entries. So if you place

    = 0

at the beginning of the file, unknown strings map to 0, and the other strings
map to values starting with 1. If label is not specified, the default is
I<Unknown>.

=head1 OPTIONS

=over 4

=item B<-C>I<.c file> B<--code>=I<.c file>

Generate code in the given file.

=item B<-H>I<header file> B<--header>=I<header file>

Generate a header in the given file, containing a declaration of the hash
function and an enumeration.

=item B<--enum-name=>I<word>

The name of the enumeration.

=item B<--function-name=>I<word>

The name of the function.

=item B<--namespace=>I<name>

Put the function and enum into a namespace (C++)

=item B<--class=>I<name>

Put the function and enum into a class (C++)

=item B<--enum-class>

Generate an enum class instead of an enum (C++)

=item B<--counter-name=>I<name>

Use I<name> for a counter that is set to the latest entry in the enumeration
+ 1. This can be useful for defining array sizes.

=item B<--extern-c>

Wrap everything into an extern "C" block. Not compatible with the C++
options, as a header with namespaces, classes, or enum classes is not
valid C.

=item B<--multi-byte>=I<value>

Generate code reading multiple bytes at once. The value is a string of power
of twos to enable. The default value is 320 meaning that 8, 4, and single byte
reads are enabled. Specify 0 to disable multi-byte completely, or add 2 if you
also want to allow 2-byte reads. 2-byte reads are disabled by default because
they negatively affect performance on older Intel architectures.

This generates code for both multiple bytes and single byte reads, but only
enables the multiple byte reads of GNU C compatible compilers, as the following
extensions are used:

=over 8

=item Byte-aligned integers

We must be able to generate integers that are aligned to a single byte using:

    typedef uint64_t __attribute__((aligned (1))) triehash_uu64;

=item Byte-order

The macros __BYTE_ORDER__ and __ORDER_LITTLE_ENDIAN__ must be defined.

=back

We forcefully disable multi-byte reads on platforms where the variable
I<__ARM_ARCH> is defined and I<__ARM_FEATURE_UNALIGNED> is not defined,
as there is a measurable overhead from emulating the unaligned reads on
ARM.

=item B<--language=>I<language>

Generate a file in the specified language. Currently known are 'C' and 'tree',
the latter generating a tree.

=item B<--include=>I<header>

Add the header to the include statements of the header file. The value must
be surrounded by quotes or angle brackets for C code. May be specified multiple
times.

=back

=cut
159
160
18
1016710
my $unknown = -1;
161
18
26
my $unknown_label = "Unknown";
162
18
19
my $counter_start = 0;
163
18
21
my $enum_name = "PerfectKey";
164
18
23
my $function_name = "PerfectHash";
165
18
17
my $enum_class = 0;
166
167
18
18
my $code_name = "-";
168
18
78
my $header_name = "-";
169
18
17
my $code;
170my $header;
171
18
13
my $ignore_case = 0;
172
18
17
my $multi_byte = "320";
173
18
17
my $language = 'C';
174
18
17
my $counter_name = undef;
175
18
25
my @includes = ();
176
177
178
18
46
Getopt::Long::config('default',
179                     'bundling',
180                     'no_getopt_compat',
181                     'no_auto_abbrev',
182                     'permute',
183                     'auto_help');
184
185
18
1355
GetOptions ("code|C=s" => \$code_name,
186            "header|H=s"   => \$header_name,
187            "function-name=s" => \$function_name,
188            "ignore-case" => \$ignore_case,
189            "enum-name=s" => \$enum_name,
190            "language|l=s" => \$language,
191            "multi-byte=s" => \$multi_byte,
192            "enum-class" => \$enum_class,
193            "include=s" => \@includes,
194            "counter-name=s" => \$counter_name)
195    or die("Could not parse options!");
196
197
198
18
10601
package Trie {
199
200    sub new {
201
1345
660
        my $class = shift;
202
1345
701
        my $self = {};
203
1345
722
        bless $self, $class;
204
205
1345
992
        $self->{children} = {};
206
1345
703
        $self->{value} = undef;
207
1345
664
        $self->{label} = undef;
208
209
1345
1161
        return $self;
210    }
211
212    # Return the largest power of 2 smaller or equal to the argument
213    sub alignpower2 {
214
908
408
        my ($self, $length) = @_;
215
216
908
1033
        return 8 if ($length >= 8 && $multi_byte =~ /3/);
217
896
1291
        return 4 if ($length >= 4 && $multi_byte =~ /2/);
218
838
1511
        return 2 if ($length >= 2 && $multi_byte =~ /1/);
219
220
754
449
        return 1;
221    }
222
223    # Split the key into a head block and a tail
224    sub split_key {
225
684
348
        my ($self, $key) = @_;
226
684
310
        my $length = length $key;
227
684
429
        my $split = $self->alignpower2($length);
228
229
684
833
        return (substr($key, 0, $split), substr($key, $split));
230    }
231
232    sub insert {
233
970
640
        my ($self, $key, $label, $value) = @_;
234
235
970
853
        if (length($key) == 0) {
236
286
174
            $self->{label} = $label;
237
286
175
            $self->{value} = $value;
238
286
256
            return;
239        }
240
241
684
448
        my ($child, $tail) = $self->split_key($key);
242
243
684
923
        $self->{children}{$child} = Trie->new if (!defined($self->{children}{$child}));
244
245
684
632
        $self->{children}{$child}->insert($tail, $label, $value);
246    }
247
248    sub filter_depth {
249
223
142
        my ($self, $togo) = @_;
250
251
223
179
        my $new = Trie->new;
252
253
223
203
        if ($togo != 0) {
254
184
84
            my $found = 0;
255
184
184
78
209
            foreach my $key (sort keys %{$self->{children}}) {
256
216
401
                if ($togo > length($key) || defined $self->{children}{$key}->{value}) {
257
188
240
                    my $child = $self->{children}{$key}->filter_depth($togo - length($key));
258
259
188
208
                    $new->{children}{$key}= $child if defined $child;
260
188
201
                    $found = 1 if defined $child;
261                }
262            }
263
184
211
            return undef if (!$found);
264        } else {
265
39
45
            $new->{value} = $self->{value};
266
39
28
            $new->{label} = $self->{label};
267        }
268
269
154
114
        return $new;
270    }
271
272    # Reinsert all value nodes into the specified $trie, prepending $prefix
273    # to their $paths.
274    sub reinsert_value_nodes_into {
275
730
425
        my ($self, $trie, $prefix) = @_;
276
277
730
770
        $trie->insert($prefix, $self->{label}, $self->{value}) if (defined $self->{value});
278
279
730
730
276
960
        foreach my $key (sort keys %{$self->{children}}) {
280
506
542
            $self->{children}{$key}->reinsert_value_nodes_into($trie, $prefix . $key);
281        }
282    }
283
284    # Find an earlier split due a an ambiguous character
285    sub find_ealier_split {
286
224
139
        my ($self, $key) = @_;
287
288
224
203
        if ($ignore_case) {
289
34
27
            for my $i (0..length($key)-1) {
290                # If the key starts with an ambiguous character, we need to
291                # take only it. Otherwise, we need to take everything
292                # before the character.
293
59
53
                return $self->alignpower2($i || 1) if (main::ambiguous(substr($key, $i, 1)));
294            }
295        }
296
212
186
        return $self->alignpower2(length $key);
297    }
298
299    # Rebuild the trie, splitting at ambigous chars, and unifying key lengths
300    sub rebuild_tree {
301
264
138
        my $self = shift;
302        # Determine if/where we need to split before an ambiguous character
303
264
131
        my $new_split = 99999999999999999;
304
264
264
116
336
        foreach my $key (sort keys %{$self->{children}}) {
305
224
166
            my $special_length = $self->find_ealier_split($key);
306
224
275
            $new_split = $special_length if ($special_length < $new_split);
307        }
308
309        # Start building a new uniform trie
310
264
264
        my $newself = Trie->new;
311
264
169
        $newself->{label} = $self->{label};
312
264
140
        $newself->{value} = $self->{value};
313
264
154
        $newself->{children} = {};
314
315
264
264
159
272
        foreach my $key (sort keys %{$self->{children}}) {
316
224
176
            my $head = substr($key, 0, $new_split);
317
224
135
            my $tail = substr($key, $new_split);
318            # Rebuild the child node at $head, pushing $tail downwards
319
224
450
            $newself->{children}{$head} //= Trie->new;
320
224
238
            $self->{children}{$key}->reinsert_value_nodes_into($newself->{children}{$head}, $tail);
321            # We took up to one special character of each key label. There might
322            # be more, so we need to rebuild recursively.
323
224
303
            $newself->{children}{$head} = $newself->{children}{$head}->rebuild_tree();
324        }
325
326
264
487
        return $newself;
327    }
328
0
0
}
329
330# Code generator for C and C++
331
18
17
package CCodeGen {
332
18
47
    my $static = ($code_name eq $header_name) ? "static" : "";
333
18
34
    my $enum_specifier = $enum_class ? "enum class" : "enum";
334
335    sub new {
336
13
15
        my $class = shift;
337
13
16
        my $self = {};
338
13
15
        bless $self, $class;
339
340
13
15
        return $self;
341    }
342
343    sub open_output {
344
12
13
        my $self = shift;
345
12
28
        if ($code_name ne "-") {
346
3
97
            open($code, '>', $code_name) or die "Cannot open $code_name: $!" ;
347        } else {
348
9
18
            $code = *STDOUT;
349        }
350
12
30
        if($code_name eq $header_name) {
351
7
9
            $header = $code;
352        } elsif ($header_name ne "-") {
353
4
64
            open($header, '>', $header_name) or die "Cannot open $header_name: $!" ;
354        } else {
355
1
2
            $header = *STDOUT;
356        }
357    }
358
359    sub word_to_label {
360
10
11
        my ($class, $word) = @_;
361
362
10
14
        $word =~ s/_/__/g;
363
10
10
        $word =~ s/-/_/g;
364
10
28
        return $word;
365    }
366
367    # Return a case label, by shifting and or-ing bytes in the word
368    sub case_label {
369
72
54
        my ($self, $key) = @_;
370
371
72
171
        return sprintf("'%s'", substr($key, 0, 1)) if not $multi_byte;
372
373
20
14
        my $output = '0';
374
375
20
18
        for my $i (0..length($key)-1) {
376
46
106
            $output .= sprintf("| onechar('%s', %d, %d)", substr($key, $i, 1), 8 * $i, 8*length($key));
377        }
378
379
20
24
        return $output;
380    }
381
382    # Return an appropriate read instruction for $length bytes from $offset
383    sub switch_key {
384
64
45
        my ($self, $offset, $length) = @_;
385
386
64
158
        return "string[$offset]" if $length == 1;
387
8
29
        return sprintf("*((triehash_uu%s*) &string[$offset])", $length * 8);
388    }
389
390    sub print_table {
391
83
65
        my ($self, $trie, $fh, $indent, $index) = @_;
392
83
85
        $indent //= 0;
393
83
90
        $index //= 0;
394
395
83
94
        if (defined $trie->{value}) {
396
19
55
            printf $fh ("    " x $indent . "return %s;\n", ($enum_class ? "${enum_name}::" : "").$trie->{label});
397
19
19
            return;
398        }
399
400        # The difference between lowercase and uppercase alphabetical characters
401        # is that they have one bit flipped. If we have alphabetical characters
402        # in the search space, and the entire search space works fine if we
403        # always turn on the flip, just OR the character we are switching over
404        # with the bit.
405
64
27
        my $want_use_bit = 0;
406
64
35
        my $can_use_bit = 1;
407
64
18
        my $key_length = 0;
408
64
64
35
78
        foreach my $key (sort keys %{$trie->{children}}) {
409
68
55
            $can_use_bit &= not main::ambiguous($key);
410
68
114
            $want_use_bit |= ($key =~ /^[a-zA-Z]+$/);
411
68
38
            $key_length = length($key);
412        }
413
414
64
144
        if ($ignore_case && $can_use_bit && $want_use_bit) {
415
12
19
            printf $fh (("    " x $indent) . "switch(%s | 0x%s) {\n", $self->switch_key($index, $key_length), "20" x $key_length);
416        } else {
417
52
95
            printf $fh (("    " x $indent) . "switch(%s) {\n", $self->switch_key($index, $key_length));
418        }
419
420
64
44
        my $notfirst = 0;
421
64
64
37
83
        foreach my $key (sort keys %{$trie->{children}}) {
422
68
62
            if ($notfirst) {
423
4
6
                printf $fh ("    " x $indent . "    break;\n");
424            }
425
68
48
            if ($ignore_case) {
426
20
31
                printf $fh ("    " x $indent . "case %s:\n", $self->case_label(lc($key)));
427
20
74
                printf $fh ("    " x $indent . "case %s:\n", $self->case_label(uc($key))) if lc($key) ne uc($key) && !($can_use_bit && $want_use_bit);
428            } else {
429
48
70
                printf $fh ("    " x $indent . "case %s:\n", $self->case_label($key));
430            }
431
432
68
187
            $self->print_table($trie->{children}{$key}, $fh, $indent + 1, $index + length($key));
433
434
68
54
            $notfirst=1;
435        }
436
437
64
90
        printf $fh ("    " x $indent . "}\n");
438    }
439
440    sub print_words {
441
31
31
        my ($self, $trie, $fh, $indent, $sofar) = @_;
442
443
31
47
        $indent //= 0;
444
31
82
        $sofar //= "";
445
446
447
31
85
        printf $fh ("    " x $indent."%s = %s,\n", $trie->{label}, $trie->{value}) if defined $trie->{value};
448
449
31
31
25
81
        foreach my $key (sort keys %{$trie->{children}}) {
450
19
43
            $self->print_words($trie->{children}{$key}, $fh, $indent, $sofar . $key);
451        }
452    }
453
454    sub print_functions {
455
15
23
        my ($self, $trie, %lengths) = @_;
456
15
10
33
9
        foreach my $local_length (sort { $a <=> $b } (keys %lengths)) {
457
15
39
            print $code ("static enum ${enum_name} ${function_name}${local_length}(const char *string)\n");
458
15
39
            print $code ("{\n");
459
15
30
            $self->print_table($trie->filter_depth($local_length)->rebuild_tree(), $code, 1);
460
15
81
            printf $code ("    return %s$unknown_label;\n", ($enum_class ? "${enum_name}::" : ""));
461
15
27
            print $code ("}\n");
462        }
463    }
464
465    sub main {
466
12
19
        my ($self, $trie, $num_values, %lengths) = @_;
467
12
85
        print $header ("#ifndef TRIE_HASH_${function_name}\n");
468
12
30
        print $header ("#define TRIE_HASH_${function_name}\n");
469
12
16
        print $header ("#include <stddef.h>\n");
470
12
13
        print $header ("#include <stdint.h>\n");
471
12
22
        foreach my $include (@includes) {
472
1
3
            print $header ("#include $include\n");
473        }
474
12
28
        printf $header ("enum { $counter_name = $num_values };\n") if (defined($counter_name));
475
12
30
        print $header ("${enum_specifier} ${enum_name} {\n");
476
12
27
        $self->print_words($trie, $header, 1);
477
12
47
        printf $header ("    $unknown_label = $unknown,\n");
478
12
21
        print $header ("};\n");
479
12
33
        print $header ("$static enum ${enum_name} ${function_name}(const char *string, size_t length);\n");
480
481
12
62
        print $code ("#include \"$header_name\"\n") if ($header_name ne $code_name);
482
483
12
23
        if ($multi_byte) {
484
3
5
            print $code ("#ifdef __GNUC__\n");
485
3
14
            for (my $i=16; $i <= 64; $i *= 2) {
486
9
41
                print $code ("typedef uint${i}_t __attribute__((aligned (1))) triehash_uu${i};\n");
487
9
19
                print $code ("typedef char static_assert${i}[__alignof__(triehash_uu${i}) == 1 ? 1 : -1];\n");
488            }
489
490
3
4
            print $code ("#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\n");
491
3
12
            print $code ("#define onechar(c, s, l) (((uint64_t)(c)) << (s))\n");
492
3
14
            print $code ("#else\n");
493
3
4
            print $code ("#define onechar(c, s, l) (((uint64_t)(c)) << (l-8-s))\n");
494
3
3
            print $code ("#endif\n");
495
3
5
            print $code ("#if (!defined(__ARM_ARCH) || defined(__ARM_FEATURE_UNALIGNED)) && !defined(TRIE_HASH_NO_MULTI_BYTE)\n");
496
3
15
            print $code ("#define TRIE_HASH_MULTI_BYTE\n");
497
3
2
            print $code ("#endif\n");
498
3
3
            print $code ("#endif /*GNUC */\n");
499
500
3
2
            print $code ("#ifdef TRIE_HASH_MULTI_BYTE\n");
501
3
6
            $self->print_functions($trie, %lengths);
502
3
3
            $multi_byte = 0;
503
3
2
            print $code ("#else\n");
504
3
7
            $self->print_functions($trie, %lengths);
505
3
6
            print $code ("#endif /* TRIE_HASH_MULTI_BYTE */\n");
506        } else {
507
9
20
            $self->print_functions($trie, %lengths);
508        }
509
510
12
34
        print $code ("$static enum ${enum_name} ${function_name}(const char *string, size_t length)\n");
511
12
17
        print $code ("{\n");
512
12
11
        print $code ("    switch (length) {\n");
513
12
5
24
4
        foreach my $local_length (sort { $a <=> $b } (keys %lengths)) {
514
9
9
            print $code ("    case $local_length:\n");
515
9
16
            print $code ("        return ${function_name}${local_length}(string);\n");
516        }
517
12
18
        print $code ("    default:\n");
518
12
49
        printf $code ("        return %s$unknown_label;\n", ($enum_class ? "${enum_name}::" : ""));
519
12
22
        print $code ("    }\n");
520
12
15
        print $code ("}\n");
521
522        # Print end of header here, in case header and code point to the same file
523
12
0
        print $header ("#endif                       /* TRIE_HASH_${function_name} */\n");
524    }
525
0
0
}
526
527# Check if the word can be reached by exactly one word in (alphabet OR 0x20).
528sub ambiguous {
529
127
76
    my $word = shift;
530
531
127
112
    foreach my $char (split //, $word) {
532        # Setting the lowercase flag in the character produces a different
533        # character, the character would thus not be matched.
534
153
173
        return 1 if ((ord($char) | 0x20) != ord(lc($char)));
535
536        # A word is also ambiguous if any character in lowercase can be reached
537        # by ORing 0x20 from another character in the charset that is not a
538        # lowercase character of the current character.
539        # Assume that we have UTF-8 and the most significant bit can be set
540
151
104
        for my $i (0..255) {
541
33380
33624
            return 1 if (($i | 0x20) == ord(lc($char)) && lc(chr($i)) ne lc($char));
542        }
543    }
544
545
103
104
    return 0;
546}
547
548sub build_trie {
549
18
18
    my $codegen = shift;
550
18
52
    my $trie = Trie->new;
551
552
18
18
    my $counter = $counter_start;
553
18
213
    my %lengths;
554
555
18
691
    open(my $input, '<', $ARGV[0]) or die "Cannot open ".$ARGV[0].": $!";
556
17
203
    while (my $line = <$input>) {
557
37
166
        my ($label, $word, $value) = $line =~/\s*(?:([^~\s]+)\s*~)?(?:\s*([^~=\s]+)\s*)?(?:=\s*([^\s]+)\s+)?\s*/;
558
559
37
57
        if (defined $word) {
560
31
40
            $counter = $value if defined($value);
561
31
84
            $label //= $codegen->word_to_label($word);
562
563
31
40
            $trie->insert($word, $label, $counter);
564
31
39
            $lengths{length($word)} = 1;
565
31
76
            $counter++;
566        } elsif (defined $value) {
567
6
6
            $unknown = $value;
568
6
21
            $unknown_label = $label if defined($label);
569
6
33
            $counter = $value + 1;
570        } else {
571
0
0
            die "Invalid line: $line";
572        }
573    }
574
575
17
181
    return ($trie, $counter, %lengths);
576}
577
578# Generates an ASCII art tree
579
18
14
package TreeCodeGen {
580
581    sub new {
582
5
5
        my $class = shift;
583
5
6
        my $self = {};
584
5
6
        bless $self, $class;
585
586
5
6
        return $self;
587    }
588
589    sub word_to_label {
590
15
14
        my ($self, $word) = @_;
591
15
35
        return $word;
592    }
593
594    sub main {
595
5
11
        my ($self, $trie, $counter, %lengths) = @_;
596
5
43
        printf $code ("┌────────────────────────────────────────────────────┐\n");
597
5
15
        printf $code ("│                   Initial trie                     â”‚\n");
598
5
11
        printf $code ("└────────────────────────────────────────────────────┘\n");
599
5
9
        $self->print($trie);
600
5
14
        printf $code ("┌────────────────────────────────────────────────────┐\n");
601
5
10
        printf $code ("│                   Rebuilt trie                     â”‚\n");
602
5
10
        printf $code ("└────────────────────────────────────────────────────┘\n");
603
5
11
        $self->print($trie->rebuild_tree());
604
605
5
23
31
32
        foreach my $local_length (sort { $a <=> $b } (keys %lengths)) {
606
20
47
            printf $code ("┌────────────────────────────────────────────────────┐\n");
607
20
87
            printf $code ("│              Trie for words of length %-4d         â”‚\n", $local_length);
608
20
35
            printf $code ("└────────────────────────────────────────────────────┘\n");
609
20
26
            $self->print($trie->filter_depth($local_length)->rebuild_tree());
610        }
611    }
612
613    sub open_output {
614
5
5
        my $self = shift;
615
5
12
        if ($code_name ne "-") {
616
0
0
            open($code, '>', $code_name) or die "Cannot open ".$ARGV[0].": $!" ;
617        } else {
618
5
11
            $code = *STDOUT;
619        }
620    }
621
622    # Print a trie
623    sub print {
624
231
135
        my ($self, $trie, $depth) = @_;
625
231
266
        $depth //= 0;
626
627
231
246
        print $code (" → ") if defined($trie->{label});
628
231
500
        print $code ($trie->{label} // "", "\n");
629
231
231
128
381
        foreach my $key (sort keys %{$trie->{children}}) {
630
201
244
            print $code ("│   " x ($depth), "├── $key");
631
201
248
            $self->print($trie->{children}{$key}, $depth + 1);
632        }
633    }
634
0
0
}
635
636
18
48
my %codegens = (
637    C => "CCodeGen",
638    tree => "TreeCodeGen",
639);
640
641
642
18
45
defined($codegens{$language}) or die "Unknown language $language. Valid choices: ", join(", ", keys %codegens);
643
18
71
my $codegen = $codegens{$language}->new();
644
18
30
my ($trie, $counter, %lengths) = build_trie($codegen);
645
646
17
40
$codegen->open_output();
647
17
49
$codegen->main($trie, $counter, %lengths);
648
649
650 - 659
=head1 LICENSE

triehash is available under the MIT/Expat license, see the source code
for more information.

=head1 AUTHOR

Julian Andres Klode <jak@jak-linux.org>

=cut
660