3 unshift @INC, qw(../../lib ../../../lib);
4 $ENV{PATH} .= ';../..;../../..' if $^O eq 'MSWin32';
11 # These may get re-ordered.
12 # RAW is a do_now as inserted by &enter
13 # AGG is an aggreagated do_now, as built up by &process
28 # (See the algorithm in encengine.c - we're building structures for it)
30 # There are two sorts of structures.
31 # "do_now" (an array, two variants of what needs storing) is whatever we need
32 # to do now we've read an input byte.
33 # It's housed in a "do_next" (which is how we got to it), and in turn points
34 # to a "do_next" which contains all the "do_now"s for the next input byte.
36 # There will be a "do_next" which is the start state.
37 # For a single byte encoding it's the only "do_next" - each "do_now" points
38 # back to it, and each "do_now" will cause bytes. There is no state.
40 # For a multi-byte encoding where all characters in the input are the same
41 # length, then there will be a tree of "do_now"->"do_next"->"do_now"
42 # branching out from the start state, one step for each input byte.
43 # The leaf "do_now"s will all be at the same distance from the start state,
44 # only the leaf "do_now"s cause output bytes, and they in turn point back to
47 # For an encoding where there are varaible length input byte sequences, you
48 # will encounter a leaf "do_now" sooner for the shorter input sequences, but
49 # as before the leaves will point back to the start state.
51 # The system will cope with escape encodings (imagine them as a mostly
52 # self-contained tree for each escape state, and cross links between trees
53 # at the state-switching characters) but so far no input format defines these.
55 # The system will also cope with having output "leaves" in the middle of
56 # the bifurcating branches, not just at the extremities, but again no
57 # input format does this yet.
59 # There are two variants of the "do_now" structure. The first, smaller variant
60 # is generated by &enter as the input file is read. There is one structure
61 # for each input byte. Say we are mapping a single byte encoding to a
62 # single byte encoding, with "ABCD" going "abcd". There will be
63 # 4 "do_now"s, {"A" => [...,"a",...], "B" => [...,"b",...], "C"=>..., "D"=>...}
65 # &process then walks the tree, building aggregate "do_now" structres for
66 # adjacent bytes where possible. The aggregate is for a contiguous range of
67 # bytes which each produce the same length of output, each move to the
68 # same next state, and each have the same fallback flag.
69 # So our 4 RAW "do_now"s above become replaced by a single structure
71 # ["A", "D", "abcd", 1, ...]
72 # ie, for an input byte $_ in "A".."D", output 1 byte, found as
73 # substr ("abcd", (ord $_ - ord "A") * 1, 1)
74 # which maps very nicely into pointer arithmetic in C for encengine.c
78 # UTF-8 encode long hand - only covers part of perl's range
80 # chr() works in native space so convert value from table
81 # into that space before using chr().
82 my $ch = chr(utf8::unicode_to_native($_[0]));
83 # Now get core perl to encode that the way it likes.
91 ## my ($ch,$page) = @_; return chr($ch);
97 # encode double byte MS byte first
98 ## my ($ch,$page) = @_; return chr($page).chr($ch);
99 return chr ($_[1]) . chr $_[0];
104 # encode Multi-byte - single for 0..255 otherwise double
105 ## my ($ch,$page) = @_;
106 ## return &encode_D if $page;
108 return chr ($_[1]) . chr $_[0] if $_[1];
112 my %encode_types = (U => \&encode_U,
118 # Win32 does not expand globs on command line
119 eval "\@ARGV = map(glob(\$_),\@ARGV)" if ($^O eq 'MSWin32');
123 # -Q to disable the duplicate codepoint test
124 # -q to remove comments written to output files
125 # -O to enable the (brute force) substring optimiser
126 # -o <output> to specify the output file name (else it's the first arg)
127 # -f <inlist> to give a file with a list of input files (else use the args)
128 # -n <name> to name the encoding (else use the basename of the input file.
129 getopts('QqOo:f:n:',\%opt);
131 # This really should go first, else the die here causes empty (non-erroneous)
132 # output files to be written.
134 if (exists $opt{'f'}) {
135 # -F is followed by name of file containing list of filenames
136 my $flist = $opt{'f'};
137 open(FLIST,$flist) || die "Cannot open $flist:$!";
138 chomp(@encfiles = <FLIST>);
144 my $cname = (exists $opt{'o'}) ? $opt{'o'} : shift(@ARGV);
145 chmod(0666,$cname) if -f $cname && !-w $cname;
146 open(C,">$cname") || die "Cannot open $cname:$!";
151 my ($doC,$doEnc,$doUcm,$doPet);
153 if ($cname =~ /\.(c|xs)$/)
156 $dname =~ s/(\.[^\.]*)?$/_def.h/;
157 chmod(0666,$dname) if -f $cname && !-w $dname;
158 open(D,">$dname") || die "Cannot open $dname:$!";
159 $hname =~ s/(\.[^\.]*)?$/.h/;
160 chmod(0666,$hname) if -f $cname && !-w $hname;
161 open(H,">$hname") || die "Cannot open $hname:$!";
163 foreach my $fh (\*C,\*D,\*H)
165 print $fh <<"END" unless $opt{'q'};
167 !!!!!!! DO NOT EDIT THIS FILE !!!!!!!
168 This file was autogenerated by:
174 if ($cname =~ /(\w+)\.xs$/)
176 print C "#include <EXTERN.h>\n";
177 print C "#include <perl.h>\n";
178 print C "#include <XSUB.h>\n";
179 print C "#define U8 U8\n";
181 print C "#include \"encode.h\"\n";
184 elsif ($cname =~ /\.enc$/)
188 elsif ($cname =~ /\.ucm$/)
192 elsif ($cname =~ /\.pet$/)
205 if ($a =~ /^.*-(\d+)/)
208 if ($b =~ /^.*-(\d+)/)
218 foreach my $enc (sort cmp_name @encfiles)
220 my ($name,$sfx) = $enc =~ /^.*?([\w-]+)\.(enc|ucm)$/;
221 $name = $opt{'n'} if exists $opt{'n'};
226 compile_enc(\*E,lc($name));
230 compile_ucm(\*E,lc($name));
235 warn "Cannot open $enc for $name:$!";
241 print STDERR "Writing compiled form\n";
242 foreach my $name (sort cmp_name keys %encoding)
244 my ($e2u,$u2e,$erep,$min_el,$max_el) = @{$encoding{$name}};
245 output(\*C,$name.'_utf8',$e2u);
246 output(\*C,'utf8_'.$name,$u2e);
247 push(@{$encoding{$name}},outstring(\*C,$e2u->{Cname}.'_def',$erep));
249 foreach my $enc (sort cmp_name keys %encoding)
251 my ($e2u,$u2e,$rep,$min_el,$max_el,$rsym) = @{$encoding{$enc}};
252 my @info = ($e2u->{Cname},$u2e->{Cname},$rsym,length($rep),$min_el,$max_el);
253 my $sym = "${enc}_encoding";
255 print C "encode_t $sym = \n";
256 print C " {",join(',',@info,"{\"$enc\",(const char *)0}"),"};\n\n";
259 foreach my $enc (sort cmp_name keys %encoding)
261 my $sym = "${enc}_encoding";
263 print H "extern encode_t $sym;\n";
264 print D " Encode_XSEncoding(aTHX_ &$sym);\n";
267 if ($cname =~ /(\w+)\.xs$/)
273 Encode_XSEncoding(pTHX_ encode_t *enc)
276 HV *stash = gv_stashpv("Encode::XS", TRUE);
277 SV *sv = sv_bless(newRV_noinc(newSViv(PTR2IV(enc))),stash);
283 const char *name = enc->name[i++];
284 XPUSHs(sv_2mortal(newSVpvn(name,strlen(name))));
287 call_pv("Encode::define_encoding",G_DISCARD);
293 print C "\nMODULE = Encode::$mod\tPACKAGE = Encode::$mod\n\n";
294 print C "BOOT:\n{\n";
295 print C "#include \"$dname\"\n";
298 # Close in void context is bad, m'kay
299 close(D) or warn "Error closing '$dname': $!";
300 close(H) or warn "Error closing '$hname': $!";
302 my $perc_saved = $strings/($strings + $saved) * 100;
303 my $perc_subsaved = $strings/($strings + $subsave) * 100;
304 printf STDERR "%d bytes in string tables\n",$strings;
305 printf STDERR "%d bytes (%.3g%%) saved spotting duplicates\n",
306 $saved, $perc_saved if $saved;
307 printf STDERR "%d bytes (%.3g%%) saved using substrings\n",
308 $subsave, $perc_subsaved if $subsave;
312 foreach my $name (sort cmp_name keys %encoding)
314 my ($e2u,$u2e,$erep,$min_el,$max_el) = @{$encoding{$name}};
315 output_enc(\*C,$name,$e2u);
320 foreach my $name (sort cmp_name keys %encoding)
322 my ($e2u,$u2e,$erep,$min_el,$max_el) = @{$encoding{$name}};
323 output_ucm(\*C,$name,$u2e,$erep,$min_el,$max_el);
327 # writing half meg files and then not checking to see if you just filled the
329 close(C) or die "Error closing '$cname': $!";
331 # End of the main program.
343 last if /^\s*CHARMAP\s*$/i;
344 if (/^\s*<(\w+)>\s+"?([^"]*)"?\s*$/i) # " # Grrr
349 if (!defined($cs = $attr{'code_set_name'}))
351 warn "No <code_set_name> in $name\n";
355 $name = $cs unless exists $opt{'n'};
361 if (exists $attr{'subchar'})
364 $attr{'subchar'} =~ /^\s*/cg;
365 push(@byte,$1) while $attr{'subchar'} =~ /\G\\x([0-9a-f]+)/icg;
366 $erep = join('',map(chr(hex($_)),@byte));
368 print "Reading $name ($cs)\n";
374 last if /^\s*END\s+CHARMAP\s*$/i;
378 $u = $1 if (/^<U([0-9a-f]+)>\s+/igc);
379 push(@byte,$1) while /\G\\x([0-9a-f]+)/igc;
380 $fb = $1 if /\G\s*(\|[0-3])/gc;
381 # warn "$_: $u @byte | $fb\n";
382 die "Bad line:$_" unless /\G\s*(#.*)?$/gc;
385 my $uch = encode_U(hex($u));
386 my $ech = join('',map(chr(hex($_)),@byte));
387 my $el = length($ech);
388 $max_el = $el if (!defined($max_el) || $el > $max_el);
389 $min_el = $el if (!defined($min_el) || $el < $min_el);
400 # $fb is fallback flag
401 # 0 - round trip safe
402 # 1 - fallback for unicode -> enc
403 # 2 - skip sub-char mapping
404 # 3 - fallback enc -> unicode
405 enter($u2e,$uch,$ech,$u2e,$fb+0) if ($fb =~ /[01]/);
406 enter($e2u,$ech,$uch,$e2u,$fb+0) if ($fb =~ /[03]/);
415 die "$nfb entries without fallback, $hfb entries with\n";
417 $encoding{$name} = [$e2u,$u2e,$erep,$min_el,$max_el];
427 while ($type = <$fh>)
429 last if $type !~ /^\s*#/;
432 return if $type eq 'E';
433 # Do the hash lookup once, rather than once per function call. 4% speedup.
434 my $type_func = $encode_types{$type};
435 my ($def,$sym,$pages) = split(/\s+/,scalar(<$fh>));
436 warn "$type encoded $name\n";
438 # Save a defined test by setting these to defined values.
439 my $min_el = ~0; # A very big integer
440 my $max_el = 0; # Anything must be longer than 0
443 $rep = &$type_func($v & 0xFF, ($v >> 8) & 0xffe);
446 # use -Q to silence the seen test. Makefile.PL uses this by default.
447 $seen = {} unless $opt{Q};
452 my $page = hex($line);
457 # So why is it 1% faster to leave the my here?
459 die "Line should be exactly 65 characters long including newline"
460 unless length ($line) == 65;
461 # Split line into groups of 4 hex digits, convert groups to ints
463 # map {hex $_} $line =~ /(....)/g
464 # This takes 63.75 (2.5% less time)
465 # unpack "n*", pack "H*", $line
466 # There's an implicit loop in map. Loops are bad, m'kay. Ops are bad, m'kay
467 # Doing it as while ($line =~ /(....)/g) took 74.63
468 foreach my $val (unpack "n*", pack "H*", $line)
470 next if $val == 0xFFFD;
471 my $ech = &$type_func($ch,$page);
472 if ($val || (!$ch && !$page))
474 my $el = length($ech);
475 $max_el = $el if $el > $max_el;
476 $min_el = $el if $el < $min_el;
477 my $uch = encode_U($val);
479 # We're doing the test.
480 # We don't need to read this quickly, so storing it as a scalar,
481 # rather than 3 (anon array, plus the 2 scalars it holds) saves
482 # RAM and may make us faster on low RAM systems. [see __END__]
483 if (exists $seen->{$uch})
485 warn sprintf("U%04X is %02X%02X and %04X\n",
486 $val,$page,$ch,$seen->{$uch});
490 $seen->{$uch} = $page << 8 | $ch;
493 # Passing 2 extra args each time is 3.6% slower!
494 # Even with having to add $fallback ||= 0 later
495 enter_fb0($e2u,$ech,$uch);
496 enter_fb0($u2e,$uch,$ech);
500 # No character at this position
501 # enter($e2u,$ech,undef,$e2u);
507 die "\$min_el=$min_el, \$max_el=$max_el - seems we read no lines"
508 if $min_el > $max_el;
509 $encoding{$name} = [$e2u,$u2e,$rep,$min_el,$max_el];
512 # my ($a,$s,$d,$t,$fb) = @_;
514 my ($current,$inbytes,$outbytes,$next,$fallback) = @_;
515 # state we shift to after this (multibyte) input character defaults to same
518 # Making sure it is defined seems to be faster than {no warnings;} in
519 # &process, or passing it in as 0 explicity.
520 # XXX $fallback ||= 0;
522 # Start at the beginning and work forwards through the string to zero.
523 # effectively we are removing 1 character from the front each time
524 # but we don't actually edit the string. [this alone seems to be 14% speedup]
525 # Hence -$pos is the length of the remaining string.
526 my $pos = -length $inbytes;
528 my $byte = substr $inbytes, $pos, 1;
531 # RAW_OUT_BYTES => 2,
533 # to unicode an array would seem to be better, because the pages are dense.
534 # from unicode can be very sparse, favouring a hash.
535 # hash using the bytes (all length 1) as keys rather than ord value,
536 # as it's easier to sort these in &process.
538 # It's faster to always add $fallback even if it's undef, rather than
539 # choosing between 3 and 4 element array. (hence why we set it defined
541 my $do_now = $current->{Raw}{$byte} ||= [{},-$pos,'',$fallback];
542 # When $pos was -1 we were at the last input character.
544 $do_now->[RAW_OUT_BYTES] = $outbytes;
545 $do_now->[RAW_NEXT] = $next;
548 # Tail recursion. The intermdiate state may not have a name yet.
549 $current = $do_now->[RAW_NEXT];
553 # This is purely for optimistation. It's just &enter hard coded for $fallback
554 # of 0, using only a 3 entry array ref to save memory for every entry.
556 my ($current,$inbytes,$outbytes,$next) = @_;
559 my $pos = -length $inbytes;
561 my $byte = substr $inbytes, $pos, 1;
562 my $do_now = $current->{Raw}{$byte} ||= [{},-$pos,''];
564 $do_now->[RAW_OUT_BYTES] = $outbytes;
565 $do_now->[RAW_NEXT] = $next;
568 $current = $do_now->[RAW_NEXT];
575 my ($fh,$name,$s) = @_;
576 my $sym = $strings{$s};
579 $saved += length($s);
584 foreach my $o (keys %strings)
586 next unless (my $i = index($o,$s)) >= 0;
588 $sym .= sprintf("+0x%02x",$i) if ($i);
589 $subsave += length($s);
590 return $strings{$s} = $sym;
593 $strings{$s} = $sym = $name;
594 $strings += length($s);
595 my $definition = sprintf "static const U8 %s[%d] = { ",$name,length($s);
596 # Maybe we should assert that these are all <256.
597 $definition .= join(',',unpack "C*",$s);
598 # We have a single long line. Split it at convenient commas.
599 $definition =~ s/(.{74,77},)/$1\n/g;
600 print $fh "$definition };\n\n";
611 my ($l, $agg_max_in, $agg_next, $agg_in_len, $agg_out_len, $agg_fallback);
614 foreach my $key (sort keys %$raw) {
617 # RAW_OUT_BYTES => 2,
619 my ($next, $in_len, $out_bytes, $fallback) = @{$raw->{$key}};
620 # Now we are converting from raw to aggregate, switch from 1 byte strings
625 # If this == fails, we're going to reset $agg_max_in below anyway.
626 $b == ++$agg_max_in &&
627 # References in numeric context give the pointer as an int.
628 $agg_next == $next &&
629 $agg_in_len == $in_len &&
630 $agg_out_len == length $out_bytes &&
631 $agg_fallback == $fallback
632 # && length($l->[AGG_OUT_BYTES]) < 16
634 # my $i = ord($b)-ord($l->[AGG_MIN_IN]);
635 # we can aggregate this byte onto the end.
636 $l->[AGG_MAX_IN] = $b;
637 $l->[AGG_OUT_BYTES] .= $out_bytes;
641 # AGG_OUT_BYTES => 2,
646 # Reset the last thing we saw, plus set 5 lexicals to save some derefs.
647 # (only gains .6% on euc-jp -- is it worth it?)
648 push @ent, $l = [$b, $agg_max_in = $b, $out_bytes, $agg_next = $next,
649 $agg_in_len = $in_len, $agg_out_len = length $out_bytes,
650 $agg_fallback = $fallback];
652 if (exists $next->{Cname}) {
653 $next->{'Forward'} = 1 if $next != $a;
655 process(sprintf("%s_%02x",$name,$b),$next);
658 # encengine.c rules say that last entry must be for 255
659 if ($agg_max_in < 255) {
660 push @ent, [1+$agg_max_in, 255,undef,$a,0,0];
662 $a->{'Entries'} = \@ent;
668 my $name = $a->{'Cname'};
670 foreach my $b (@{$a->{'Entries'}})
672 next unless $b->[AGG_OUT_LEN];
673 my $s = $b->[AGG_MIN_IN];
674 my $e = $b->[AGG_MAX_IN];
675 outstring($fh,sprintf("%s__%02x_%02x",$name,$s,$e),$b->[AGG_OUT_BYTES]);
679 print $fh "\nstatic encpage_t $name\[",scalar(@{$a->{'Entries'}}),"];\n";
682 foreach my $b (@{$a->{'Entries'}})
684 my ($s,$e,$out,$t,$end,$l) = @$b;
685 outtable($fh,$t) unless $t->{'Done'};
687 print $fh "\nstatic encpage_t $name\[",scalar(@{$a->{'Entries'}}),"] = {\n";
688 foreach my $b (@{$a->{'Entries'}})
690 my ($sc,$ec,$out,$t,$end,$l,$fb) = @$b;
695 printf $fh outstring($fh,'',$out);
701 print $fh ",",$t->{Cname};
702 printf $fh ",0x%02x,0x%02x,$l,$end},\n",$sc,$ec;
709 my ($fh,$name,$a) = @_;
717 my ($fh,$name,$a) = @_;
718 die "Changed - fix me for new structure";
719 foreach my $b (sort keys %$a)
721 my ($s,$e,$out,$t,$end,$l,$fb) = @{$a->{$b}};
733 my $s = do "unicore/Name.pl";
734 die "char_names: unicore/Name.pl: $!\n" unless defined $s;
736 while ($s =~ /\G([0-9a-f]+)\t([0-9a-f]*)\t(.*?)\s*\n/igc)
740 last if $s >= 0x10000;
741 my $e = length($2) ? hex($2) : $s;
742 for (my $i = $s; $i <= $e; $i++)
745 # print sprintf("U%04X $name\n",$i);
752 my ($cmap,$a,$t,$pre) = @_;
753 # warn sprintf("Page %x\n",$pre);
755 foreach my $key (sort keys %$raw) {
758 # RAW_OUT_BYTES => 2,
760 my ($next, $in_len, $out_bytes, $fallback) = @{$raw->{$key}};
764 if ($next != $a && $next != $t) {
765 output_ucm_page($cmap,$a,$next,(($pre|($u &0x3F)) << 6)&0xFFFF);
766 } elsif (length $out_bytes) {
768 $u = $pre|($u &0x3f);
770 my $s = sprintf "<U%04X> ",$u;
771 #foreach my $c (split(//,$out_bytes)) {
772 # $s .= sprintf "\\x%02X",ord($c);
774 # 9.5% faster changing that loop to this:
775 $s .= sprintf +("\\x%02X" x length $out_bytes), unpack "C*", $out_bytes;
776 $s .= sprintf " |%d # %s\n",($fallback ? 1 : 0),$uname[$u];
779 warn join(',',$u, @{$raw->{$key}},$a,$t);
786 my ($fh,$name,$h,$rep,$min_el,$max_el) = @_;
787 print $fh "# $0 @orig_ARGV\n" unless $opt{'q'};
788 print $fh "<code_set_name> \"$name\"\n";
792 print $fh "<mb_cur_min> $min_el\n";
796 print $fh "<mb_cur_max> $max_el\n";
800 print $fh "<subchar> ";
801 foreach my $c (split(//,$rep))
803 printf $fh "\\x%02X",ord($c);
808 output_ucm_page(\@cmap,$h,$h,0);
809 print $fh "#\nCHARMAP\n";
810 foreach my $line (sort { substr($a,8) cmp substr($b,8) } @cmap)
814 print $fh "END CHARMAP\n";
819 With %seen holding array refs:
821 865.66 real 28.80 user 8.79 sys
822 7904 maximum resident set size
823 1356 average shared memory size
824 18566 average unshared data size
825 229 average unshared stack size
829 With %seen holding simple scalars:
831 342.16 real 27.11 user 3.54 sys
832 8388 maximum resident set size
833 1394 average shared memory size
834 14969 average unshared data size
835 236 average unshared stack size
839 Yes, 5 minutes is faster than 15. Above is for CP936 in CN. Only difference is
840 how %seen is storing things its seen. So it is pathalogically bad on a 16M
841 RAM machine, but it's going to help even on modern machines.
842 Swapping is bad, m'kay :-)