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