Resubmit change #28095
[p5sagit/p5-mst-13.2.git] / ext / Encode / bin / enc2xs
CommitLineData
3ef515df 1#!./perl
67d7b5ef 2BEGIN {
a999c27c 3 # @INC poking no longer needed w/ new MakeMaker and Makefile.PL's
4 # with $ENV{PERL_CORE} set
5 # In case we need it in future...
6 require Config; import Config;
67d7b5ef 7}
8use strict;
b536bf57 9use warnings;
67d7b5ef 10use Getopt::Std;
11my @orig_ARGV = @ARGV;
41c240f5 12our $VERSION = do { my @r = (q$Revision: 2.2 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r };
67d7b5ef 13
14# These may get re-ordered.
15# RAW is a do_now as inserted by &enter
16# AGG is an aggreagated do_now, as built up by &process
a999c27c 17
67d7b5ef 18use constant {
19 RAW_NEXT => 0,
20 RAW_IN_LEN => 1,
21 RAW_OUT_BYTES => 2,
22 RAW_FALLBACK => 3,
23
24 AGG_MIN_IN => 0,
25 AGG_MAX_IN => 1,
26 AGG_OUT_BYTES => 2,
27 AGG_NEXT => 3,
28 AGG_IN_LEN => 4,
29 AGG_OUT_LEN => 5,
30 AGG_FALLBACK => 6,
31};
a999c27c 32
67d7b5ef 33# (See the algorithm in encengine.c - we're building structures for it)
34
35# There are two sorts of structures.
36# "do_now" (an array, two variants of what needs storing) is whatever we need
37# to do now we've read an input byte.
38# It's housed in a "do_next" (which is how we got to it), and in turn points
39# to a "do_next" which contains all the "do_now"s for the next input byte.
40
41# There will be a "do_next" which is the start state.
42# For a single byte encoding it's the only "do_next" - each "do_now" points
43# back to it, and each "do_now" will cause bytes. There is no state.
44
45# For a multi-byte encoding where all characters in the input are the same
46# length, then there will be a tree of "do_now"->"do_next"->"do_now"
47# branching out from the start state, one step for each input byte.
48# The leaf "do_now"s will all be at the same distance from the start state,
49# only the leaf "do_now"s cause output bytes, and they in turn point back to
50# the start state.
51
52# For an encoding where there are varaible length input byte sequences, you
53# will encounter a leaf "do_now" sooner for the shorter input sequences, but
54# as before the leaves will point back to the start state.
55
56# The system will cope with escape encodings (imagine them as a mostly
57# self-contained tree for each escape state, and cross links between trees
58# at the state-switching characters) but so far no input format defines these.
59
60# The system will also cope with having output "leaves" in the middle of
61# the bifurcating branches, not just at the extremities, but again no
62# input format does this yet.
63
64# There are two variants of the "do_now" structure. The first, smaller variant
65# is generated by &enter as the input file is read. There is one structure
66# for each input byte. Say we are mapping a single byte encoding to a
67# single byte encoding, with "ABCD" going "abcd". There will be
68# 4 "do_now"s, {"A" => [...,"a",...], "B" => [...,"b",...], "C"=>..., "D"=>...}
69
70# &process then walks the tree, building aggregate "do_now" structres for
71# adjacent bytes where possible. The aggregate is for a contiguous range of
72# bytes which each produce the same length of output, each move to the
73# same next state, and each have the same fallback flag.
74# So our 4 RAW "do_now"s above become replaced by a single structure
75# containing:
76# ["A", "D", "abcd", 1, ...]
77# ie, for an input byte $_ in "A".."D", output 1 byte, found as
78# substr ("abcd", (ord $_ - ord "A") * 1, 1)
79# which maps very nicely into pointer arithmetic in C for encengine.c
80
81sub encode_U
82{
83 # UTF-8 encode long hand - only covers part of perl's range
84 ## my $uv = shift;
85 # chr() works in native space so convert value from table
86 # into that space before using chr().
87 my $ch = chr(utf8::unicode_to_native($_[0]));
88 # Now get core perl to encode that the way it likes.
89 utf8::encode($ch);
90 return $ch;
91}
92
93sub encode_S
94{
95 # encode single byte
96 ## my ($ch,$page) = @_; return chr($ch);
97 return chr $_[0];
98}
99
100sub encode_D
101{
102 # encode double byte MS byte first
103 ## my ($ch,$page) = @_; return chr($page).chr($ch);
104 return chr ($_[1]) . chr $_[0];
105}
106
107sub encode_M
108{
109 # encode Multi-byte - single for 0..255 otherwise double
110 ## my ($ch,$page) = @_;
111 ## return &encode_D if $page;
112 ## return &encode_S;
113 return chr ($_[1]) . chr $_[0] if $_[1];
114 return chr $_[0];
115}
116
117my %encode_types = (U => \&encode_U,
118 S => \&encode_S,
119 D => \&encode_D,
120 M => \&encode_M,
121 );
122
123# Win32 does not expand globs on command line
124eval "\@ARGV = map(glob(\$_),\@ARGV)" if ($^O eq 'MSWin32');
125
126my %opt;
127# I think these are:
128# -Q to disable the duplicate codepoint test
129# -S make mapping errors fatal
130# -q to remove comments written to output files
131# -O to enable the (brute force) substring optimiser
132# -o <output> to specify the output file name (else it's the first arg)
133# -f <inlist> to give a file with a list of input files (else use the args)
134# -n <name> to name the encoding (else use the basename of the input file.
aae85ceb 135getopts('CM:SQqOo:f:n:',\%opt);
67d7b5ef 136
137$opt{M} and make_makefile_pl($opt{M}, @ARGV);
aae85ceb 138$opt{C} and make_configlocal_pm($opt{C}, @ARGV);
67d7b5ef 139
140# This really should go first, else the die here causes empty (non-erroneous)
141# output files to be written.
142my @encfiles;
143if (exists $opt{'f'}) {
144 # -F is followed by name of file containing list of filenames
145 my $flist = $opt{'f'};
146 open(FLIST,$flist) || die "Cannot open $flist:$!";
147 chomp(@encfiles = <FLIST>);
148 close(FLIST);
149} else {
150 @encfiles = @ARGV;
151}
152
153my $cname = (exists $opt{'o'}) ? $opt{'o'} : shift(@ARGV);
154chmod(0666,$cname) if -f $cname && !-w $cname;
155open(C,">$cname") || die "Cannot open $cname:$!";
156
157my $dname = $cname;
158my $hname = $cname;
159
160my ($doC,$doEnc,$doUcm,$doPet);
161
0e4142c9 162if ($cname =~ /\.(c|xs)$/i) # VMS may have upcased filenames with DECC$ARGV_PARSE_STYLE defined
67d7b5ef 163 {
164 $doC = 1;
e7cbefb8 165 $dname =~ s/(\.[^\.]*)?$/.exh/;
67d7b5ef 166 chmod(0666,$dname) if -f $cname && !-w $dname;
167 open(D,">$dname") || die "Cannot open $dname:$!";
168 $hname =~ s/(\.[^\.]*)?$/.h/;
169 chmod(0666,$hname) if -f $cname && !-w $hname;
170 open(H,">$hname") || die "Cannot open $hname:$!";
171
172 foreach my $fh (\*C,\*D,\*H)
173 {
174 print $fh <<"END" unless $opt{'q'};
175/*
176 !!!!!!! DO NOT EDIT THIS FILE !!!!!!!
177 This file was autogenerated by:
178 $^X $0 @orig_ARGV
179*/
180END
181 }
182
183 if ($cname =~ /(\w+)\.xs$/)
184 {
185 print C "#include <EXTERN.h>\n";
186 print C "#include <perl.h>\n";
187 print C "#include <XSUB.h>\n";
188 print C "#define U8 U8\n";
189 }
b536bf57 190 print C "#include \"encode.h\"\n\n";
67d7b5ef 191
192 }
193elsif ($cname =~ /\.enc$/)
194 {
195 $doEnc = 1;
196 }
197elsif ($cname =~ /\.ucm$/)
198 {
199 $doUcm = 1;
200 }
201elsif ($cname =~ /\.pet$/)
202 {
203 $doPet = 1;
204 }
205
206my %encoding;
207my %strings;
b536bf57 208my $string_acc;
209my %strings_in_acc;
210
67d7b5ef 211my $saved = 0;
212my $subsave = 0;
213my $strings = 0;
214
215sub cmp_name
216{
217 if ($a =~ /^.*-(\d+)/)
218 {
219 my $an = $1;
220 if ($b =~ /^.*-(\d+)/)
221 {
222 my $r = $an <=> $1;
223 return $r if $r;
224 }
225 }
226 return $a cmp $b;
227}
228
229
230foreach my $enc (sort cmp_name @encfiles)
231 {
232 my ($name,$sfx) = $enc =~ /^.*?([\w-]+)\.(enc|ucm)$/;
233 $name = $opt{'n'} if exists $opt{'n'};
234 if (open(E,$enc))
235 {
236 if ($sfx eq 'enc')
237 {
238 compile_enc(\*E,lc($name));
239 }
240 else
241 {
242 compile_ucm(\*E,lc($name));
243 }
244 }
245 else
246 {
247 warn "Cannot open $enc for $name:$!";
248 }
249 }
250
251if ($doC)
252 {
253 print STDERR "Writing compiled form\n";
254 foreach my $name (sort cmp_name keys %encoding)
255 {
256 my ($e2u,$u2e,$erep,$min_el,$max_el) = @{$encoding{$name}};
b536bf57 257 process($name.'_utf8',$e2u);
258 addstrings(\*C,$e2u);
259
260 process('utf8_'.$name,$u2e);
261 addstrings(\*C,$u2e);
262 }
263 outbigstring(\*C,"enctable");
264 foreach my $name (sort cmp_name keys %encoding)
265 {
266 my ($e2u,$u2e,$erep,$min_el,$max_el) = @{$encoding{$name}};
267 outtable(\*C,$e2u, "enctable");
268 outtable(\*C,$u2e, "enctable");
269
b2704119 270 # push(@{$encoding{$name}},outstring(\*C,$e2u->{Cname}.'_def',$erep));
67d7b5ef 271 }
272 foreach my $enc (sort cmp_name keys %encoding)
273 {
b2704119 274 # my ($e2u,$u2e,$rep,$min_el,$max_el,$rsym) = @{$encoding{$enc}};
275 my ($e2u,$u2e,$rep,$min_el,$max_el) = @{$encoding{$enc}};
276 #my @info = ($e2u->{Cname},$u2e->{Cname},$rsym,length($rep),$min_el,$max_el);
277 my $replen = 0;
278 $replen++ while($rep =~ /\G\\x[0-9A-Fa-f]/g);
67d7b5ef 279 my $sym = "${enc}_encoding";
280 $sym =~ s/\W+/_/g;
7dd03145 281 my @info = ($e2u->{Cname},$u2e->{Cname},"${sym}_rep_character",$replen,
282 $min_el,$max_el);
283 print C "static const U8 ${sym}_rep_character[] = \"$rep\";\n";
284 print C "static const char ${sym}_enc_name[] = \"$enc\";\n\n";
0629a5b3 285 print C "const encode_t $sym = \n";
f0a41339 286 # This is to make null encoding work -- dankogai
287 for (my $i = (scalar @info) - 1; $i >= 0; --$i){
288 $info[$i] ||= 1;
289 }
290 # end of null tweak -- dankogai
7dd03145 291 print C " {",join(',',@info,"{${sym}_enc_name,(const char *)0}"),"};\n\n";
67d7b5ef 292 }
293
294 foreach my $enc (sort cmp_name keys %encoding)
295 {
296 my $sym = "${enc}_encoding";
297 $sym =~ s/\W+/_/g;
298 print H "extern encode_t $sym;\n";
299 print D " Encode_XSEncoding(aTHX_ &$sym);\n";
300 }
301
302 if ($cname =~ /(\w+)\.xs$/)
303 {
304 my $mod = $1;
305 print C <<'END';
306
307static void
308Encode_XSEncoding(pTHX_ encode_t *enc)
309{
310 dSP;
311 HV *stash = gv_stashpv("Encode::XS", TRUE);
312 SV *sv = sv_bless(newRV_noinc(newSViv(PTR2IV(enc))),stash);
313 int i = 0;
314 PUSHMARK(sp);
315 XPUSHs(sv);
316 while (enc->name[i])
317 {
318 const char *name = enc->name[i++];
319 XPUSHs(sv_2mortal(newSVpvn(name,strlen(name))));
320 }
321 PUTBACK;
322 call_pv("Encode::define_encoding",G_DISCARD);
323 SvREFCNT_dec(sv);
324}
325
326END
327
328 print C "\nMODULE = Encode::$mod\tPACKAGE = Encode::$mod\n\n";
329 print C "BOOT:\n{\n";
330 print C "#include \"$dname\"\n";
331 print C "}\n";
332 }
333 # Close in void context is bad, m'kay
334 close(D) or warn "Error closing '$dname': $!";
335 close(H) or warn "Error closing '$hname': $!";
336
cf9f87ce 337 my $perc_saved = $saved/($strings + $saved) * 100;
338 my $perc_subsaved = $subsave/($strings + $subsave) * 100;
67d7b5ef 339 printf STDERR "%d bytes in string tables\n",$strings;
340 printf STDERR "%d bytes (%.3g%%) saved spotting duplicates\n",
341 $saved, $perc_saved if $saved;
342 printf STDERR "%d bytes (%.3g%%) saved using substrings\n",
343 $subsave, $perc_subsaved if $subsave;
344 }
345elsif ($doEnc)
346 {
347 foreach my $name (sort cmp_name keys %encoding)
348 {
349 my ($e2u,$u2e,$erep,$min_el,$max_el) = @{$encoding{$name}};
350 output_enc(\*C,$name,$e2u);
351 }
352 }
353elsif ($doUcm)
354 {
355 foreach my $name (sort cmp_name keys %encoding)
356 {
357 my ($e2u,$u2e,$erep,$min_el,$max_el) = @{$encoding{$name}};
358 output_ucm(\*C,$name,$u2e,$erep,$min_el,$max_el);
359 }
360 }
361
362# writing half meg files and then not checking to see if you just filled the
363# disk is bad, m'kay
364close(C) or die "Error closing '$cname': $!";
365
366# End of the main program.
367
368sub compile_ucm
369{
370 my ($fh,$name) = @_;
371 my $e2u = {};
372 my $u2e = {};
373 my $cs;
374 my %attr;
375 while (<$fh>)
376 {
377 s/#.*$//;
378 last if /^\s*CHARMAP\s*$/i;
379 if (/^\s*<(\w+)>\s+"?([^"]*)"?\s*$/i) # " # Grrr
380 {
381 $attr{$1} = $2;
382 }
383 }
384 if (!defined($cs = $attr{'code_set_name'}))
385 {
386 warn "No <code_set_name> in $name\n";
387 }
388 else
389 {
390 $name = $cs unless exists $opt{'n'};
391 }
392 my $erep;
393 my $urep;
394 my $max_el;
395 my $min_el;
396 if (exists $attr{'subchar'})
397 {
b2704119 398 #my @byte;
399 #$attr{'subchar'} =~ /^\s*/cg;
400 #push(@byte,$1) while $attr{'subchar'} =~ /\G\\x([0-9a-f]+)/icg;
401 #$erep = join('',map(chr(hex($_)),@byte));
402 $erep = $attr{'subchar'};
403 $erep =~ s/^\s+//; $erep =~ s/\s+$//;
67d7b5ef 404 }
405 print "Reading $name ($cs)\n";
406 my $nfb = 0;
407 my $hfb = 0;
408 while (<$fh>)
409 {
410 s/#.*$//;
411 last if /^\s*END\s+CHARMAP\s*$/i;
412 next if /^\s*$/;
a999c27c 413 my (@uni, @byte) = ();
414 my ($uni, $byte, $fb) = m/^(\S+)\s+(\S+)\s+(\S+)\s+/o
415 or die "Bad line: $_";
416 while ($uni =~ m/\G<([U0-9a-fA-F\+]+)>/g){
417 push @uni, map { substr($_, 1) } split(/\+/, $1);
418 }
419 while ($byte =~ m/\G\\x([0-9a-fA-F]+)/g){
420 push @byte, $1;
421 }
422 if (@uni)
67d7b5ef 423 {
a999c27c 424 my $uch = join('', map { encode_U(hex($_)) } @uni );
67d7b5ef 425 my $ech = join('',map(chr(hex($_)),@byte));
426 my $el = length($ech);
427 $max_el = $el if (!defined($max_el) || $el > $max_el);
428 $min_el = $el if (!defined($min_el) || $el < $min_el);
429 if (length($fb))
430 {
431 $fb = substr($fb,1);
432 $hfb++;
433 }
434 else
435 {
436 $nfb++;
437 $fb = '0';
438 }
439 # $fb is fallback flag
440 # 0 - round trip safe
441 # 1 - fallback for unicode -> enc
442 # 2 - skip sub-char mapping
443 # 3 - fallback enc -> unicode
444 enter($u2e,$uch,$ech,$u2e,$fb+0) if ($fb =~ /[01]/);
445 enter($e2u,$ech,$uch,$e2u,$fb+0) if ($fb =~ /[03]/);
446 }
447 else
448 {
449 warn $_;
450 }
451 }
452 if ($nfb && $hfb)
453 {
454 die "$nfb entries without fallback, $hfb entries with\n";
455 }
456 $encoding{$name} = [$e2u,$u2e,$erep,$min_el,$max_el];
457}
458
459
460
461sub compile_enc
462{
463 my ($fh,$name) = @_;
464 my $e2u = {};
465 my $u2e = {};
466
467 my $type;
468 while ($type = <$fh>)
469 {
470 last if $type !~ /^\s*#/;
471 }
472 chomp($type);
473 return if $type eq 'E';
474 # Do the hash lookup once, rather than once per function call. 4% speedup.
475 my $type_func = $encode_types{$type};
476 my ($def,$sym,$pages) = split(/\s+/,scalar(<$fh>));
477 warn "$type encoded $name\n";
478 my $rep = '';
479 # Save a defined test by setting these to defined values.
480 my $min_el = ~0; # A very big integer
481 my $max_el = 0; # Anything must be longer than 0
482 {
483 my $v = hex($def);
484 $rep = &$type_func($v & 0xFF, ($v >> 8) & 0xffe);
485 }
486 my $errors;
487 my $seen;
488 # use -Q to silence the seen test. Makefile.PL uses this by default.
489 $seen = {} unless $opt{Q};
490 do
491 {
492 my $line = <$fh>;
493 chomp($line);
494 my $page = hex($line);
495 my $ch = 0;
496 my $i = 16;
497 do
498 {
499 # So why is it 1% faster to leave the my here?
500 my $line = <$fh>;
501 $line =~ s/\r\n$/\n/;
502 die "$.:${line}Line should be exactly 65 characters long including
503 newline (".length($line).")" unless length ($line) == 65;
504 # Split line into groups of 4 hex digits, convert groups to ints
505 # This takes 65.35
506 # map {hex $_} $line =~ /(....)/g
507 # This takes 63.75 (2.5% less time)
508 # unpack "n*", pack "H*", $line
509 # There's an implicit loop in map. Loops are bad, m'kay. Ops are bad, m'kay
510 # Doing it as while ($line =~ /(....)/g) took 74.63
511 foreach my $val (unpack "n*", pack "H*", $line)
512 {
513 next if $val == 0xFFFD;
514 my $ech = &$type_func($ch,$page);
515 if ($val || (!$ch && !$page))
516 {
517 my $el = length($ech);
518 $max_el = $el if $el > $max_el;
519 $min_el = $el if $el < $min_el;
520 my $uch = encode_U($val);
521 if ($seen) {
522 # We're doing the test.
523 # We don't need to read this quickly, so storing it as a scalar,
524 # rather than 3 (anon array, plus the 2 scalars it holds) saves
525 # RAM and may make us faster on low RAM systems. [see __END__]
526 if (exists $seen->{$uch})
527 {
528 warn sprintf("U%04X is %02X%02X and %04X\n",
529 $val,$page,$ch,$seen->{$uch});
530 $errors++;
531 }
532 else
533 {
534 $seen->{$uch} = $page << 8 | $ch;
535 }
536 }
537 # Passing 2 extra args each time is 3.6% slower!
538 # Even with having to add $fallback ||= 0 later
539 enter_fb0($e2u,$ech,$uch);
540 enter_fb0($u2e,$uch,$ech);
541 }
542 else
543 {
544 # No character at this position
545 # enter($e2u,$ech,undef,$e2u);
546 }
547 $ch++;
548 }
549 } while --$i;
550 } while --$pages;
551 die "\$min_el=$min_el, \$max_el=$max_el - seems we read no lines"
552 if $min_el > $max_el;
553 die "$errors mapping conflicts\n" if ($errors && $opt{'S'});
554 $encoding{$name} = [$e2u,$u2e,$rep,$min_el,$max_el];
555}
556
557# my ($a,$s,$d,$t,$fb) = @_;
558sub enter {
559 my ($current,$inbytes,$outbytes,$next,$fallback) = @_;
560 # state we shift to after this (multibyte) input character defaults to same
561 # as current state.
562 $next ||= $current;
563 # Making sure it is defined seems to be faster than {no warnings;} in
564 # &process, or passing it in as 0 explicity.
565 # XXX $fallback ||= 0;
566
567 # Start at the beginning and work forwards through the string to zero.
568 # effectively we are removing 1 character from the front each time
569 # but we don't actually edit the string. [this alone seems to be 14% speedup]
570 # Hence -$pos is the length of the remaining string.
571 my $pos = -length $inbytes;
572 while (1) {
573 my $byte = substr $inbytes, $pos, 1;
574 # RAW_NEXT => 0,
575 # RAW_IN_LEN => 1,
576 # RAW_OUT_BYTES => 2,
577 # RAW_FALLBACK => 3,
578 # to unicode an array would seem to be better, because the pages are dense.
579 # from unicode can be very sparse, favouring a hash.
580 # hash using the bytes (all length 1) as keys rather than ord value,
581 # as it's easier to sort these in &process.
582
583 # It's faster to always add $fallback even if it's undef, rather than
584 # choosing between 3 and 4 element array. (hence why we set it defined
585 # above)
586 my $do_now = $current->{Raw}{$byte} ||= [{},-$pos,'',$fallback];
587 # When $pos was -1 we were at the last input character.
588 unless (++$pos) {
589 $do_now->[RAW_OUT_BYTES] = $outbytes;
590 $do_now->[RAW_NEXT] = $next;
591 return;
592 }
593 # Tail recursion. The intermdiate state may not have a name yet.
594 $current = $do_now->[RAW_NEXT];
595 }
596}
597
598# This is purely for optimistation. It's just &enter hard coded for $fallback
599# of 0, using only a 3 entry array ref to save memory for every entry.
600sub enter_fb0 {
601 my ($current,$inbytes,$outbytes,$next) = @_;
602 $next ||= $current;
603
604 my $pos = -length $inbytes;
605 while (1) {
606 my $byte = substr $inbytes, $pos, 1;
607 my $do_now = $current->{Raw}{$byte} ||= [{},-$pos,''];
608 unless (++$pos) {
609 $do_now->[RAW_OUT_BYTES] = $outbytes;
610 $do_now->[RAW_NEXT] = $next;
611 return;
612 }
613 $current = $do_now->[RAW_NEXT];
614 }
615}
616
67d7b5ef 617sub process
618{
619 my ($name,$a) = @_;
620 $name =~ s/\W+/_/g;
621 $a->{Cname} = $name;
622 my $raw = $a->{Raw};
623 my ($l, $agg_max_in, $agg_next, $agg_in_len, $agg_out_len, $agg_fallback);
624 my @ent;
625 $agg_max_in = 0;
626 foreach my $key (sort keys %$raw) {
627 # RAW_NEXT => 0,
628 # RAW_IN_LEN => 1,
629 # RAW_OUT_BYTES => 2,
630 # RAW_FALLBACK => 3,
631 my ($next, $in_len, $out_bytes, $fallback) = @{$raw->{$key}};
632 # Now we are converting from raw to aggregate, switch from 1 byte strings
633 # to numbers
634 my $b = ord $key;
635 $fallback ||= 0;
636 if ($l &&
637 # If this == fails, we're going to reset $agg_max_in below anyway.
638 $b == ++$agg_max_in &&
639 # References in numeric context give the pointer as an int.
640 $agg_next == $next &&
641 $agg_in_len == $in_len &&
642 $agg_out_len == length $out_bytes &&
643 $agg_fallback == $fallback
644 # && length($l->[AGG_OUT_BYTES]) < 16
645 ) {
646 # my $i = ord($b)-ord($l->[AGG_MIN_IN]);
647 # we can aggregate this byte onto the end.
648 $l->[AGG_MAX_IN] = $b;
649 $l->[AGG_OUT_BYTES] .= $out_bytes;
650 } else {
651 # AGG_MIN_IN => 0,
652 # AGG_MAX_IN => 1,
653 # AGG_OUT_BYTES => 2,
654 # AGG_NEXT => 3,
655 # AGG_IN_LEN => 4,
656 # AGG_OUT_LEN => 5,
657 # AGG_FALLBACK => 6,
658 # Reset the last thing we saw, plus set 5 lexicals to save some derefs.
659 # (only gains .6% on euc-jp -- is it worth it?)
660 push @ent, $l = [$b, $agg_max_in = $b, $out_bytes, $agg_next = $next,
661 $agg_in_len = $in_len, $agg_out_len = length $out_bytes,
662 $agg_fallback = $fallback];
663 }
664 if (exists $next->{Cname}) {
665 $next->{'Forward'} = 1 if $next != $a;
666 } else {
667 process(sprintf("%s_%02x",$name,$b),$next);
668 }
669 }
670 # encengine.c rules say that last entry must be for 255
671 if ($agg_max_in < 255) {
672 push @ent, [1+$agg_max_in, 255,undef,$a,0,0];
673 }
674 $a->{'Entries'} = \@ent;
675}
676
b536bf57 677
678sub addstrings
67d7b5ef 679{
680 my ($fh,$a) = @_;
681 my $name = $a->{'Cname'};
682 # String tables
683 foreach my $b (@{$a->{'Entries'}})
684 {
685 next unless $b->[AGG_OUT_LEN];
b536bf57 686 $strings{$b->[AGG_OUT_BYTES]} = undef;
67d7b5ef 687 }
688 if ($a->{'Forward'})
689 {
f0a41339 690 my $var = $^O eq 'MacOS' ? 'extern' : 'static';
0629a5b3 691 print $fh "$var const encpage_t $name\[",scalar(@{$a->{'Entries'}}),"];\n";
b536bf57 692 }
693 $a->{'DoneStrings'} = 1;
694 foreach my $b (@{$a->{'Entries'}})
695 {
696 my ($s,$e,$out,$t,$end,$l) = @$b;
697 addstrings($fh,$t) unless $t->{'DoneStrings'};
67d7b5ef 698 }
b536bf57 699}
700
701sub outbigstring
702{
703 my ($fh,$name) = @_;
704
705 $string_acc = '';
706
707 # Make the big string in the string accumulator. Longest first, on the hope
708 # that this makes it more likely that we find the short strings later on.
709 # Not sure if it helps sorting strings of the same length lexcically.
710 foreach my $s (sort {length $b <=> length $a || $a cmp $b} keys %strings) {
711 my $index = index $string_acc, $s;
712 if ($index >= 0) {
713 $saved += length($s);
714 $strings_in_acc{$s} = $index;
715 } else {
716 OPTIMISER: {
717 if ($opt{'O'}) {
718 my $sublength = length $s;
719 while (--$sublength > 0) {
720 # progressively lop characters off the end, to see if the start of
721 # the new string overlaps the end of the accumulator.
722 if (substr ($string_acc, -$sublength)
723 eq substr ($s, 0, $sublength)) {
724 $subsave += $sublength;
725 $strings_in_acc{$s} = length ($string_acc) - $sublength;
726 # append the last bit on the end.
727 $string_acc .= substr ($s, $sublength);
728 last OPTIMISER;
729 }
730 # or if the end of the new string overlaps the start of the
731 # accumulator
732 next unless substr ($string_acc, 0, $sublength)
733 eq substr ($s, -$sublength);
734 # well, the last $sublength characters of the accumulator match.
735 # so as we're prepending to the accumulator, need to shift all our
736 # existing offsets forwards
737 $_ += $sublength foreach values %strings_in_acc;
738 $subsave += $sublength;
739 $strings_in_acc{$s} = 0;
740 # append the first bit on the start.
741 $string_acc = substr ($s, 0, -$sublength) . $string_acc;
742 last OPTIMISER;
743 }
744 }
745 # Optimiser (if it ran) found nothing, so just going have to tack the
746 # whole thing on the end.
747 $strings_in_acc{$s} = length $string_acc;
748 $string_acc .= $s;
749 };
750 }
751 }
752
753 $strings = length $string_acc;
754 my $definition = "\nstatic const U8 $name\[$strings] = { " .
755 join(',',unpack "C*",$string_acc);
756 # We have a single long line. Split it at convenient commas.
757 print $fh $1, "\n" while $definition =~ /\G(.{74,77},)/gcs;
758 print $fh substr ($definition, pos $definition), " };\n";
759}
760
761sub findstring {
762 my ($name,$s) = @_;
763 my $offset = $strings_in_acc{$s};
764 die "Can't find string " . join (',',unpack "C*",$s) . " in accumulator"
765 unless defined $offset;
766 "$name + $offset";
767}
768
769sub outtable
770{
771 my ($fh,$a,$bigname) = @_;
772 my $name = $a->{'Cname'};
67d7b5ef 773 $a->{'Done'} = 1;
774 foreach my $b (@{$a->{'Entries'}})
775 {
776 my ($s,$e,$out,$t,$end,$l) = @$b;
b536bf57 777 outtable($fh,$t,$bigname) unless $t->{'Done'};
67d7b5ef 778 }
0629a5b3 779 print $fh "\nstatic const encpage_t $name\[",
780 scalar(@{$a->{'Entries'}}), "] = {\n";
67d7b5ef 781 foreach my $b (@{$a->{'Entries'}})
782 {
783 my ($sc,$ec,$out,$t,$end,$l,$fb) = @$b;
d6f02b51 784 # $end |= 0x80 if $fb; # what the heck was on your mind, Nick? -- Dan
67d7b5ef 785 print $fh "{";
786 if ($l)
787 {
b536bf57 788 printf $fh findstring($bigname,$out);
67d7b5ef 789 }
790 else
791 {
792 print $fh "0";
793 }
794 print $fh ",",$t->{Cname};
795 printf $fh ",0x%02x,0x%02x,$l,$end},\n",$sc,$ec;
796 }
797 print $fh "};\n";
798}
799
67d7b5ef 800sub output_enc
801{
802 my ($fh,$name,$a) = @_;
803 die "Changed - fix me for new structure";
804 foreach my $b (sort keys %$a)
805 {
806 my ($s,$e,$out,$t,$end,$l,$fb) = @{$a->{$b}};
807 }
808}
809
810sub decode_U
811{
812 my $s = shift;
813}
814
815my @uname;
816sub char_names
817{
818 my $s = do "unicore/Name.pl";
819 die "char_names: unicore/Name.pl: $!\n" unless defined $s;
820 pos($s) = 0;
821 while ($s =~ /\G([0-9a-f]+)\t([0-9a-f]*)\t(.*?)\s*\n/igc)
822 {
823 my $name = $3;
824 my $s = hex($1);
825 last if $s >= 0x10000;
826 my $e = length($2) ? hex($2) : $s;
827 for (my $i = $s; $i <= $e; $i++)
828 {
829 $uname[$i] = $name;
830# print sprintf("U%04X $name\n",$i);
831 }
832 }
833}
834
835sub output_ucm_page
836{
837 my ($cmap,$a,$t,$pre) = @_;
838 # warn sprintf("Page %x\n",$pre);
839 my $raw = $t->{Raw};
840 foreach my $key (sort keys %$raw) {
841 # RAW_NEXT => 0,
842 # RAW_IN_LEN => 1,
843 # RAW_OUT_BYTES => 2,
844 # RAW_FALLBACK => 3,
845 my ($next, $in_len, $out_bytes, $fallback) = @{$raw->{$key}};
846 my $u = ord $key;
847 $fallback ||= 0;
848
849 if ($next != $a && $next != $t) {
850 output_ucm_page($cmap,$a,$next,(($pre|($u &0x3F)) << 6)&0xFFFF);
851 } elsif (length $out_bytes) {
852 if ($pre) {
853 $u = $pre|($u &0x3f);
854 }
855 my $s = sprintf "<U%04X> ",$u;
856 #foreach my $c (split(//,$out_bytes)) {
857 # $s .= sprintf "\\x%02X",ord($c);
858 #}
859 # 9.5% faster changing that loop to this:
860 $s .= sprintf +("\\x%02X" x length $out_bytes), unpack "C*", $out_bytes;
861 $s .= sprintf " |%d # %s\n",($fallback ? 1 : 0),$uname[$u];
862 push(@$cmap,$s);
863 } else {
864 warn join(',',$u, @{$raw->{$key}},$a,$t);
865 }
866 }
867}
868
869sub output_ucm
870{
871 my ($fh,$name,$h,$rep,$min_el,$max_el) = @_;
872 print $fh "# $0 @orig_ARGV\n" unless $opt{'q'};
873 print $fh "<code_set_name> \"$name\"\n";
874 char_names();
875 if (defined $min_el)
876 {
877 print $fh "<mb_cur_min> $min_el\n";
878 }
879 if (defined $max_el)
880 {
881 print $fh "<mb_cur_max> $max_el\n";
882 }
883 if (defined $rep)
884 {
885 print $fh "<subchar> ";
886 foreach my $c (split(//,$rep))
887 {
888 printf $fh "\\x%02X",ord($c);
889 }
890 print $fh "\n";
891 }
892 my @cmap;
893 output_ucm_page(\@cmap,$h,$h,0);
894 print $fh "#\nCHARMAP\n";
895 foreach my $line (sort { substr($a,8) cmp substr($b,8) } @cmap)
896 {
897 print $fh $line;
898 }
899 print $fh "END CHARMAP\n";
900}
901
3ef515df 902use vars qw(
903 $_Enc2xs
904 $_Version
905 $_Inc
b2704119 906 $_E2X
3ef515df 907 $_Name
908 $_TableFiles
909 $_Now
910);
911
b2704119 912sub find_e2x{
b536bf57 913 eval { require File::Find; };
b2704119 914 my (@inc, %e2x_dir);
915 for my $inc (@INC){
916 push @inc, $inc unless $inc eq '.'; #skip current dir
917 }
918 File::Find::find(
919 sub {
920 my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
921 $atime,$mtime,$ctime,$blksize,$blocks)
922 = lstat($_) or return;
923 -f _ or return;
924 if (/^.*\.e2x$/o){
b536bf57 925 no warnings 'once';
b2704119 926 $e2x_dir{$File::Find::dir} ||= $mtime;
927 }
928 return;
929 }, @inc);
930 warn join("\n", keys %e2x_dir), "\n";
931 for my $d (sort {$e2x_dir{$a} <=> $e2x_dir{$b}} keys %e2x_dir){
932 $_E2X = $d;
933 # warn "$_E2X => ", scalar localtime($e2x_dir{$d});
934 return $_E2X;
935 }
936}
937
67d7b5ef 938sub make_makefile_pl
939{
940 eval { require Encode; };
941 $@ and die "You need to install Encode to use enc2xs -M\nerror: $@\n";
3ef515df 942 # our used for variable expanstion
943 $_Enc2xs = $0;
944 $_Version = $VERSION;
b2704119 945 $_E2X = find_e2x();
3ef515df 946 $_Name = shift;
947 $_TableFiles = join(",", map {qq('$_')} @_);
948 $_Now = scalar localtime();
b2704119 949
aae85ceb 950 eval { require File::Spec; };
b2704119 951 _print_expand(File::Spec->catfile($_E2X,"Makefile_PL.e2x"),"Makefile.PL");
b2704119 952 _print_expand(File::Spec->catfile($_E2X,"_PM.e2x"), "$_Name.pm");
b2704119 953 _print_expand(File::Spec->catfile($_E2X,"_T.e2x"), "t/$_Name.t");
b2704119 954 _print_expand(File::Spec->catfile($_E2X,"README.e2x"), "README");
b2704119 955 _print_expand(File::Spec->catfile($_E2X,"Changes.e2x"), "Changes");
3ef515df 956 exit;
957}
958
aae85ceb 959use vars qw(
960 $_ModLines
961 $_LocalVer
962 );
963
964sub make_configlocal_pm
965{
966 eval { require Encode; };
967 $@ and die "Unable to require Encode: $@\n";
968 eval { require File::Spec; };
969 # our used for variable expanstion
970 my %in_core = map {$_=>1}('ascii','iso-8859-1','utf8');
971 my %LocalMod = ();
972 for my $d (@INC){
973 my $inc = File::Spec->catfile($d, "Encode");
974 -d $inc or next;
975 opendir my $dh, $inc or die "$inc:$!";
976 warn "Checking $inc...\n";
977 for my $f (grep /\.pm$/o, readdir($dh)){
978 -f File::Spec->catfile($inc, "$f") or next;
979 $INC{"Encode/$f"} and next;
980 warn "require Encode/$f;\n";
981 eval { require "Encode/$f"; };
982 $@ and die "Can't require Encode/$f: $@\n";
983 for my $enc (Encode->encodings()){
b536bf57 984 no warnings 'once';
aae85ceb 985 $in_core{$enc} and next;
986 $Encode::Config::ExtModule{$enc} and next;
987 my $mod = "Encode/$f";
988 $mod =~ s/\.pm$//o; $mod =~ s,/,::,og;
b2704119 989 $LocalMod{$enc} ||= $mod;
aae85ceb 990 }
991 }
992 }
993 $_ModLines = "";
994 for my $enc (sort keys %LocalMod){
995 $_ModLines .=
996 qq(\$Encode::ExtModule{'$enc'} =\t"$LocalMod{$enc}";\n);
997 }
b2704119 998 warn $_ModLines;
aae85ceb 999 $_LocalVer = _mkversion();
b2704119 1000 $_E2X = find_e2x();
aae85ceb 1001 $_Inc = $INC{"Encode.pm"}; $_Inc =~ s/\.pm$//o;
b2704119 1002 _print_expand(File::Spec->catfile($_E2X,"ConfigLocal_PM.e2x"),
621b0f8d 1003 File::Spec->catfile($_Inc,"ConfigLocal.pm"),
1004 1);
aae85ceb 1005 exit;
1006}
1007
1008sub _mkversion{
1009 my ($ss,$mm,$hh,$dd,$mo,$yyyy) = localtime();
1010 $yyyy += 1900, $mo +=1;
1011 return sprintf("v%04d.%04d.%04d", $yyyy, $mo*100+$dd, $hh*100+$mm);
1012}
1013
3ef515df 1014sub _print_expand{
67d7b5ef 1015 eval { require File::Basename; };
1016 $@ and die "File::Basename needed. Are you on miniperl?;\nerror: $@\n";
1017 File::Basename->import();
621b0f8d 1018 my ($src, $dst, $clobber) = @_;
1019 if (!$clobber and -e $dst){
1020 warn "$dst exists. skipping\n";
1021 return;
1022 }
1023 warn "Generating $dst...\n";
3ef515df 1024 open my $in, $src or die "$src : $!";
1025 if ((my $d = dirname($dst)) ne '.'){
1026 -d $d or mkdir $d, 0755 or die "mkdir $d : $!";
1027 }
1028 open my $out, ">$dst" or die "$!";
1029 my $asis = 0;
1030 while (<$in>){
1031 if (/^#### END_OF_HEADER/){
1032 $asis = 1; next;
1033 }
1034 s/(\$_[A-Z][A-Za-z0-9]+)_/$1/gee unless $asis;
1035 print $out $_;
67d7b5ef 1036 }
67d7b5ef 1037}
67d7b5ef 1038__END__
1039
1040=head1 NAME
1041
1042enc2xs -- Perl Encode Module Generator
1043
1044=head1 SYNOPSIS
1045
67d7b5ef 1046 enc2xs -[options]
aae85ceb 1047 enc2xs -M ModName mapfiles...
1048 enc2xs -C
67d7b5ef 1049
1050=head1 DESCRIPTION
1051
1052F<enc2xs> builds a Perl extension for use by Encode from either
0ab8f81e 1053Unicode Character Mapping files (.ucm) or Tcl Encoding Files (.enc).
1054Besides being used internally during the build process of the Encode
1055module, you can use F<enc2xs> to add your own encoding to perl.
1056No knowledge of XS is necessary.
67d7b5ef 1057
1058=head1 Quick Guide
1059
0ab8f81e 1060If you want to know as little about Perl as possible but need to
67d7b5ef 1061add a new encoding, just read this chapter and forget the rest.
1062
1063=over 4
1064
1065=item 0.
1066
0ab8f81e 1067Have a .ucm file ready. You can get it from somewhere or you can write
1068your own from scratch or you can grab one from the Encode distribution
1069and customize it. For the UCM format, see the next Chapter. In the
1070example below, I'll call my theoretical encoding myascii, defined
1071in I<my.ucm>. C<$> is a shell prompt.
67d7b5ef 1072
1073 $ ls -F
1074 my.ucm
1075
1076=item 1.
1077
1078Issue a command as follows;
1079
1080 $ enc2xs -M My my.ucm
3ef515df 1081 generating Makefile.PL
1082 generating My.pm
1083 generating README
1084 generating Changes
67d7b5ef 1085
1086Now take a look at your current directory. It should look like this.
1087
1088 $ ls -F
1089 Makefile.PL My.pm my.ucm t/
1090
0ab8f81e 1091The following files were created.
67d7b5ef 1092
0ab8f81e 1093 Makefile.PL - MakeMaker script
1094 My.pm - Encode submodule
1095 t/My.t - test file
1096
1097=over 4
67d7b5ef 1098
037b88d6 1099=item 1.1.
1100
1101If you want *.ucm installed together with the modules, do as follows;
1102
1103 $ mkdir Encode
1104 $ mv *.ucm Encode
1105 $ enc2xs -M My Encode/*ucm
1106
0ab8f81e 1107=back
1108
67d7b5ef 1109=item 2.
1110
1111Edit the files generated. You don't have to if you have no time AND no
1112intention to give it to someone else. But it is a good idea to edit
0ab8f81e 1113the pod and to add more tests.
67d7b5ef 1114
1115=item 3.
1116
0ab8f81e 1117Now issue a command all Perl Mongers love:
67d7b5ef 1118
9160fdbd 1119 $ perl Makefile.PL
67d7b5ef 1120 Writing Makefile for Encode::My
1121
1122=item 4.
1123
1124Now all you have to do is make.
1125
1126 $ make
1127 cp My.pm blib/lib/Encode/My.pm
1128 /usr/local/bin/perl /usr/local/bin/enc2xs -Q -O \
1129 -o encode_t.c -f encode_t.fnm
1130 Reading myascii (myascii)
1131 Writing compiled form
1132 128 bytes in string tables
cf9f87ce 1133 384 bytes (75%) saved spotting duplicates
1134 1 bytes (0.775%) saved using substrings
67d7b5ef 1135 ....
1136 chmod 644 blib/arch/auto/Encode/My/My.bs
1137 $
1138
0ab8f81e 1139The time it takes varies depending on how fast your machine is and
1140how large your encoding is. Unless you are working on something big
1141like euc-tw, it won't take too long.
67d7b5ef 1142
1143=item 5.
1144
1145You can "make install" already but you should test first.
1146
1147 $ make test
1148 PERL_DL_NONLAZY=1 /usr/local/bin/perl -Iblib/arch -Iblib/lib \
1149 -e 'use Test::Harness qw(&runtests $verbose); \
1150 $verbose=0; runtests @ARGV;' t/*.t
1151 t/My....ok
1152 All tests successful.
1153 Files=1, Tests=2, 0 wallclock secs
1154 ( 0.09 cusr + 0.01 csys = 0.09 CPU)
1155
1156=item 6.
1157
1158If you are content with the test result, just "make install"
1159
aae85ceb 1160=item 7.
1161
0ab8f81e 1162If you want to add your encoding to Encode's demand-loading list
aae85ceb 1163(so you don't have to "use Encode::YourEncoding"), run
1164
1165 enc2xs -C
1166
1167to update Encode::ConfigLocal, a module that controls local settings.
1168After that, "use Encode;" is enough to load your encodings on demand.
1169
67d7b5ef 1170=back
1171
1172=head1 The Unicode Character Map
1173
0ab8f81e 1174Encode uses the Unicode Character Map (UCM) format for source character
1175mappings. This format is used by IBM's ICU package and was adopted
1176by Nick Ing-Simmons for use with the Encode module. Since UCM is
1177more flexible than Tcl's Encoding Map and far more user-friendly,
1178this is the recommended formet for Encode now.
67d7b5ef 1179
0ab8f81e 1180A UCM file looks like this.
67d7b5ef 1181
1182 #
1183 # Comments
1184 #
1185 <code_set_name> "US-ascii" # Required
1186 <code_set_alias> "ascii" # Optional
1187 <mb_cur_min> 1 # Required; usually 1
1188 <mb_cur_max> 1 # Max. # of bytes/char
1189 <subchar> \x3F # Substitution char
1190 #
1191 CHARMAP
1192 <U0000> \x00 |0 # <control>
1193 <U0001> \x01 |0 # <control>
1194 <U0002> \x02 |0 # <control>
1195 ....
1196 <U007C> \x7C |0 # VERTICAL LINE
1197 <U007D> \x7D |0 # RIGHT CURLY BRACKET
1198 <U007E> \x7E |0 # TILDE
1199 <U007F> \x7F |0 # <control>
1200 END CHARMAP
1201
1202=over 4
1203
1204=item *
1205
0ab8f81e 1206Anything that follows C<#> is treated as a comment.
67d7b5ef 1207
1208=item *
1209
0ab8f81e 1210The header section continues until a line containing the word
1211CHARMAP. This section has a form of I<E<lt>keywordE<gt> value>, one
1212pair per line. Strings used as values must be quoted. Barewords are
1213treated as numbers. I<\xXX> represents a byte.
67d7b5ef 1214
1215Most of the keywords are self-explanatory. I<subchar> means
1216substitution character, not subcharacter. When you decode a Unicode
1217sequence to this encoding but no matching character is found, the byte
1218sequence defined here will be used. For most cases, the value here is
0ab8f81e 1219\x3F; in ASCII, this is a question mark.
67d7b5ef 1220
1221=item *
1222
1223CHARMAP starts the character map section. Each line has a form as
0ab8f81e 1224follows:
67d7b5ef 1225
1226 <UXXXX> \xXX.. |0 # comment
1227 ^ ^ ^
1228 | | +- Fallback flag
1229 | +-------- Encoded byte sequence
1230 +-------------- Unicode Character ID in hex
1231
0ab8f81e 1232The format is roughly the same as a header section except for the
1233fallback flag: | followed by 0..3. The meaning of the possible
1234values is as follows:
67d7b5ef 1235
0ab8f81e 1236=over 4
67d7b5ef 1237
1238=item |0
1239
0ab8f81e 1240Round trip safe. A character decoded to Unicode encodes back to the
1241same byte sequence. Most characters have this flag.
67d7b5ef 1242
1243=item |1
1244
1245Fallback for unicode -> encoding. When seen, enc2xs adds this
0ab8f81e 1246character for the encode map only.
67d7b5ef 1247
1248=item |2
1249
1250Skip sub-char mapping should there be no code point.
1251
1252=item |3
1253
1254Fallback for encoding -> unicode. When seen, enc2xs adds this
0ab8f81e 1255character for the decode map only.
67d7b5ef 1256
1257=back
1258
1259=item *
1260
1261And finally, END OF CHARMAP ends the section.
1262
1263=back
1264
6d1c0808 1265When you are manually creating a UCM file, you should copy ascii.ucm
0ab8f81e 1266or an existing encoding which is close to yours, rather than write
1267your own from scratch.
67d7b5ef 1268
1269When you do so, make sure you leave at least B<U0000> to B<U0020> as
0ab8f81e 1270is, unless your environment is EBCDIC.
67d7b5ef 1271
1272B<CAVEAT>: not all features in UCM are implemented. For example,
1273icu:state is not used. Because of that, you need to write a perl
0ab8f81e 1274module if you want to support algorithmical encodings, notably
1275the ISO-2022 series. Such modules include L<Encode::JP::2022_JP>,
67d7b5ef 1276L<Encode::KR::2022_KR>, and L<Encode::TW::HZ>.
1277
6d1c0808 1278=head2 Coping with duplicate mappings
1279
1280When you create a map, you SHOULD make your mappings round-trip safe.
1281That is, C<encode('your-encoding', decode('your-encoding', $data)) eq
1282$data> stands for all characters that are marked as C<|0>. Here is
0ab8f81e 1283how to make sure:
6d1c0808 1284
0ab8f81e 1285=over 4
6d1c0808 1286
1287=item *
1288
1289Sort your map in Unicode order.
1290
1291=item *
1292
1293When you have a duplicate entry, mark either one with '|1' or '|3'.
1294
1295=item *
1296
0ab8f81e 1297And make sure the '|1' or '|3' entry FOLLOWS the '|0' entry.
6d1c0808 1298
1299=back
1300
1301Here is an example from big5-eten.
1302
1303 <U2550> \xF9\xF9 |0
1304 <U2550> \xA2\xA4 |3
1305
1306Internally Encoding -> Unicode and Unicode -> Encoding Map looks like
1307this;
1308
1309 E to U U to E
1310 --------------------------------------
1311 \xF9\xF9 => U2550 U2550 => \xF9\xF9
1312 \xA2\xA4 => U2550
1313
1314So it is round-trip safe for \xF9\xF9. But if the line above is upside
1315down, here is what happens.
1316
1317 E to U U to E
1318 --------------------------------------
1319 \xA2\xA4 => U2550 U2550 => \xF9\xF9
1320 (\xF9\xF9 => U2550 is now overwritten!)
1321
1322The Encode package comes with F<ucmlint>, a crude but sufficient
0ab8f81e 1323utility to check the integrity of a UCM file. Check under the
1324Encode/bin directory for this.
cf9f87ce 1325
1326When in doubt, you can use F<ucmsort>, yet another utility under
1327Encode/bin directory.
6d1c0808 1328
67d7b5ef 1329=head1 Bookmarks
1330
0ab8f81e 1331=over 4
1332
1333=item *
1334
67d7b5ef 1335ICU Home Page
1336L<http://oss.software.ibm.com/icu/>
1337
0ab8f81e 1338=item *
1339
67d7b5ef 1340ICU Character Mapping Tables
1341L<http://oss.software.ibm.com/icu/charset/>
1342
0ab8f81e 1343=item *
1344
67d7b5ef 1345ICU:Conversion Data
1346L<http://oss.software.ibm.com/icu/userguide/conversion-data.html>
1347
0ab8f81e 1348=back
1349
67d7b5ef 1350=head1 SEE ALSO
1351
1352L<Encode>,
1353L<perlmod>,
1354L<perlpod>
1355
1356=cut
1357
1358# -Q to disable the duplicate codepoint test
1359# -S make mapping errors fatal
1360# -q to remove comments written to output files
1361# -O to enable the (brute force) substring optimiser
1362# -o <output> to specify the output file name (else it's the first arg)
1363# -f <inlist> to give a file with a list of input files (else use the args)
1364# -n <name> to name the encoding (else use the basename of the input file.
1365
1366With %seen holding array refs:
1367
1368 865.66 real 28.80 user 8.79 sys
1369 7904 maximum resident set size
1370 1356 average shared memory size
1371 18566 average unshared data size
1372 229 average unshared stack size
1373 46080 page reclaims
1374 33373 page faults
1375
1376With %seen holding simple scalars:
1377
1378 342.16 real 27.11 user 3.54 sys
1379 8388 maximum resident set size
1380 1394 average shared memory size
1381 14969 average unshared data size
1382 236 average unshared stack size
1383 28159 page reclaims
1384 9839 page faults
1385
1386Yes, 5 minutes is faster than 15. Above is for CP936 in CN. Only difference is
1387how %seen is storing things its seen. So it is pathalogically bad on a 16M
1388RAM machine, but it's going to help even on modern machines.
1389Swapping is bad, m'kay :-)