Ne.app NeANT
The Ne.app NeANT User module of the NeANT distribution.
codecvt.cpp
Go to the documentation of this file.
1
29#include "diagnostic.h" // for issue_diag()
30#include "def.h" // for Def
31
32#include "path.h" // for get_pathname()
33#include "scanner.h" // for scanner
34
35#include <cassert> // for assert()
36#include <climits> // for UCHAR_MAX
37#include <cstring> // for memset()
38#include <fstream> // for ifstream, ofstream
39
40
41typedef std::map<std::string, wchar_t>::const_iterator n_cmap_citer2;
42
43
44std::size_t Def::
45gen_mbchar_tables (codecvt_offsets_map_t &tab,
46 std::map<std::string, unsigned> &off_map,
47 const std::string &charp /* = "" */,
48 unsigned tabno /* = 0 */)
49{
50 // upon the first call (but not during subsequent recursive calls)
51 // generate a set of multibyte prefixes from the set of all known
52 // multibyte characters
53 static unsigned ntabs = 0;
54 static std::set<std::string>* pfx_set = 0;
55
56 const n_cmap_citer2 mb_map_end = charmap_.get_mb_cmap ().end ();
57
58 if (0 == pfx_set) {
59 pfx_set = new std::set<std::string>;
60
61 // iterate over the range of valid multibyte characters
62 // obtained from the charmap and generate a complete
63 // subset of non-empty multibyte prefixes from each
64 unsigned off = 0;
65
66 const n_cmap_citer2 mb_map_begin = charmap_.get_mb_cmap ().begin ();
67
68 for (n_cmap_citer2 it = mb_map_begin; it != mb_map_end; ++it, ++off) {
69
70 // insert the ordinal number of each multibyte character
71 // into a map for fast lookup later
72 off_map.insert (std::make_pair (it->first, off));
73
74 // generate non-empty prefixes up to one byte less
75 // in length than the complete multibyte character
76 for (std::string prefix = it->first; 1 < prefix.size (); ) {
77 prefix = prefix.substr (0, prefix.size () - 1);
78 pfx_set->insert (prefix);
79 }
80 }
81 }
82
83 // number of valid characters inserted into the tables
84 std::size_t nchars = 0;
85
86 // an array of offsets to the multibyte character or to the next
87 // array containing such offsets (defined recursively for up to
88 // MB_CUR_MAX levels of nesting)
89 codecvt_offset_tab_t* const offsets = new codecvt_offset_tab_t;
90
91 std::string mb_char (charp + '\0');
92
93 for (unsigned i = 0; i <= UCHAR_MAX; ++i) {
94
95 unsigned char cur_char = (unsigned char)i;
96
97 mb_char [mb_char.size () - 1] = char (cur_char);
98
99 if (mb_map_end == charmap_.get_mb_cmap ().find (mb_char)) {
100 // mb_char is not a complete, valid multibyte character
101 // check to see if it's a prefix of one
102
103 if (pfx_set->find (mb_char) == pfx_set->end ()) {
104 // mb_char is not a prefix of a valid multibyte
105 // character, mark it invalide
106 offsets->off [cur_char] = UINT_MAX;
107 }
108 else {
109 // mb_char is a prefix of a valid multibyte character,
110 // set the MSB to denote that it "continues" in the
111 // table at the next higher offset
112 offsets->off [cur_char] = ++ntabs | 0x80000000;
113
114 // generate that table
115 nchars += gen_mbchar_tables (tab, off_map, mb_char, ntabs);
116 }
117 }
118 else {
119 // mb_char is a complete, valid miltibyte character
120 // insert its ordinal number (offset) into the array
121 offsets->off [cur_char] = off_map.find (mb_char)->second;
122 ++nchars;
123 }
124 }
125
126 // insert the completely populated table into the map
127 tab.insert (std::make_pair (tabno, offsets));
128
129 if (0 == ntabs) {
130 // clean up on return from the topmost (non-recursive) call
131 delete pfx_set;
132 pfx_set = 0;
133 }
134
135 return nchars;
136}
137
138
139std::size_t Def::
140gen_wchar_tables (codecvt_offsets_map_t &tab,
141 const std::string &charp /* = "" */,
142 unsigned int tabno /* = 0 */)
143{
144 // upon the first call (but not during subsequent recursive calls)
145 // generate a set of multibyte prefixes from the set of all known
146 // multibyte characters
147 static unsigned ntabs = 0;
148 static std::set<std::string> *pfx_set = 0;
149 static std::map<std::string, unsigned> *off_map = 0;
150 static std::map<std::string, std::string> *utf_map = 0;
151
152 if (0 == utf_map) {
153 pfx_set = new std::set<std::string>;
154 off_map = new std::map<std::string, unsigned>;
155 utf_map = new std::map<std::string, std::string>;
156
157 const n_cmap_citer2 first = charmap_.get_mb_cmap ().begin ();
158 const n_cmap_citer2 last = charmap_.get_mb_cmap ().end ();
159
160 unsigned off = 0;
161
162 for (n_cmap_citer2 it = first; it != last; ++it) {
163
164 off_map->insert (std::make_pair (it->first, off));
165
166 off += unsigned (it->first.size ()) + 1;
167
168 std::string utf = utf8_encode (it->second);
169
170 utf_map->insert (std::make_pair (utf, it->first));
171
172 while (1 < utf.size ()) {
173 utf = utf.substr (0, utf.size () - 1);
174 pfx_set->insert (utf);
175 }
176 }
177 }
178
179 codecvt_offset_tab_t* const offsets = new codecvt_offset_tab_t;
180
181 // number of valid characters inserted into the tables
182 std::size_t nchars = 0;
183
184 std::string mb_char (charp + '\0');
185
186 for (unsigned i = 0; i <= UCHAR_MAX; ++i) {
187
188 unsigned char cur_char = (unsigned char)i;
189
190 mb_char [mb_char.size () - 1] = char (cur_char);
191
192 const wchar_utf8_iter it = utf_map->find (mb_char);
193 if (it == utf_map->end ()) {
194 if (pfx_set->find (mb_char) == pfx_set->end ()) {
195 offsets->off [cur_char] = UINT_MAX;
196 }
197 else {
198 offsets->off [cur_char] = ++ntabs | 0x80000000;
199
200 nchars += gen_wchar_tables (tab, mb_char, ntabs);
201 }
202 }
203 else {
204 offsets->off [cur_char] = off_map->find (it->second)->second;
205
206 ++nchars;
207 }
208 }
209
210 tab.insert (std::make_pair (tabno, offsets));
211
212 if (0 == ntabs) {
213 // clean up
214 delete pfx_set;
215 delete utf_map;
216
217 pfx_set = 0;
218 utf_map = 0;
219 }
220
221 return nchars;
222}
223
224
225std::size_t Def::
226gen_utf8_tables (codecvt_offsets_map_t &tab,
227 std::map<std::string, unsigned> &off_map,
228 const std::string &charp /* = "" */,
229 unsigned tabno /* = 0 */)
230{
231 static unsigned ntabs = 0;
232 static std::set<std::string> *pfx_set = 0;
233 static std::map<std::string, wchar_t> *utf_map = 0;
234
235 if (0 == pfx_set) {
236 pfx_set = new std::set<std::string>;
237
238 const ucs4_cmap_iter first = charmap_.get_ucs4_cmap ().begin ();
239 const ucs4_cmap_iter last = charmap_.get_ucs4_cmap ().end ();
240
241 for (ucs4_cmap_iter it = first; it != last; ++it) {
242
243 for (std::string prefix = utf8_encode (it->second);
244 1 < prefix.size (); ) {
245 prefix = prefix.substr (0, prefix.size () - 1);
246 pfx_set->insert (prefix);
247 }
248 }
249 }
250
251 // the set of complete utf8 strings in the current character map
252 typedef std::map<std::string, wchar_t>::iterator utf8_map_iter;
253
254 if (0 == utf_map) {
255 utf_map = new std::map<std::string, wchar_t>;
256
257 const ucs4_cmap_iter first = charmap_.get_ucs4_cmap ().begin ();
258 const ucs4_cmap_iter last = charmap_.get_ucs4_cmap ().end ();
259
260 for (ucs4_cmap_iter it = first; it != last; ++it) {
261 const std::string utf = utf8_encode (it->second);
262 utf_map->insert (std::make_pair (utf, it->second));
263 }
264 }
265
266 codecvt_offset_tab_t* const offsets = new codecvt_offset_tab_t;
267
268 // number of valid characters inserted into the tables
269 std::size_t nchars = 0;
270
271 std::string mb_char = charp + '\0';
272
273 for (unsigned int i = 0; i <= UCHAR_MAX; ++i) {
274
275 unsigned char cur_char = (unsigned char)i;
276
277 mb_char [mb_char.size () - 1] = char (cur_char);
278
279 const utf8_map_iter where = utf_map->find (mb_char);
280
281 if (where == utf_map->end ()) {
282 if (pfx_set->find (mb_char) == pfx_set->end ()) {
283 offsets->off [cur_char] = UINT_MAX;
284 }
285 else {
286 offsets->off [cur_char] = ++ntabs | 0x80000000;
287 nchars += gen_utf8_tables (tab, off_map, mb_char, ntabs);
288 }
289 }
290 else {
291 // first get the symbolic name
292 std::string str
293 = charmap_.get_rucs4_cmap ().find (where->second)->second;
294
295 // then get the internal encoding of the character
296 const wchar_t int_enc = charmap_.get_w_cmap().find (str)->second;
297
298 // then get the external encoding to use in a lookup in
299 // mb_char_off_map
300 str = charmap_.get_rmb_cmap ().find (int_enc)->second;
301
302 offsets->off [cur_char] = off_map.find (str)->second;
303
304 ++nchars;
305 }
306 }
307
308 tab.insert (std::make_pair (tabno, offsets));
309
310 if (0 == ntabs) {
311 // clean up
312 delete pfx_set;
313 delete utf_map;
314
315 pfx_set = 0;
316 utf_map = 0;
317 }
318 return nchars;
319}
320
321
322void Def::
323gen_xlit_data ()
324{
325 // data offset points to the beginning of the data containing
326 // the narrow strings character encodings
327 unsigned int data_offset = 0;
328
329 // traverse the map and construct the map of offsets
330 xlit_map_t::const_iterator it = xlit_map_.begin ();
331 for (; it != xlit_map_.end (); ++it) {
332 // insert pair(wchar_t value, offset of first string in data block)
333 xlit_data_offset_map_.insert (
334 std::make_pair (it->first,data_offset));
335
336 // advance the data_offset value to the next "first" string
337 std::list<std::string>::const_iterator sit =
338 it->second.begin ();
339 for (; sit != it->second.end (); ++sit) {
340 data_offset += unsigned (sit->size ()) + 1;
341 }
342 ++data_offset;
343 }
344
345 // create a new table (first), populate it with default values
346 // and insert it in the tables map
347 xlit_offset_table_t table0;
348 unsigned int k;
349 for (k = 0; k < UCHAR_MAX + 1; ++k)
350 table0.offset_table [k] = UINT_MAX;
351
352 // insert it into the map
353 xlit_table_map_.insert (std::make_pair(0, table0));
354
355 const xlit_map_t::const_iterator xlit_map_end = xlit_map_.end ();
356
357 // traverse the map again and build the tables
358 for (it = xlit_map_.begin (); it != xlit_map_end; ++it) {
359
360 // encode the wchar_t value to UTF-8
361 const std::string utf8_rep (utf8_encode (it->first));
362 data_offset = xlit_data_offset_map_.find (it->first)->second;
363
364 // traverse the utf8 representation string and create the
365 // necessary tables and populate the indexes
366 unsigned int table_idx = 0;
367
368 const std::string::const_iterator utf8_rep_end = utf8_rep.end ();
369 std::string::const_iterator string_it = utf8_rep.begin ();
370
371 for (; string_it != utf8_rep_end; ++string_it) {
372 // get the table corresponding to the current index and locate
373 // the value at that index
374 const xlit_table_map_t::iterator res =
375 xlit_table_map_.find (table_idx);
376
377 assert (res != xlit_table_map_.end ());
378
379 // offset in table
380 unsigned char off_idx = (unsigned char)*string_it;
381
382 // res is the iterator pointing to the correct table in the map
383 // check the index and if not populated, create a new table
384 if (res->second.offset_table [off_idx] == UINT_MAX) {
385
386 // if this is the last position in the string, then
387 // fill the table position with the offset of the string data
388 if ((string_it + 1) == utf8_rep.end ()) {
389 xlit_data_offset_map_t::const_iterator data_it =
390 xlit_data_offset_map_.find (it->first);
391 assert (data_it != xlit_data_offset_map_.end ());
392
393 // fill the table position with the found offset
394 res->second.offset_table [off_idx] = data_it->second;
395 continue;
396 }
397
398 // create a new table and append it to the map
399 xlit_offset_table_t table;
400 for (unsigned int i = 0; i < UCHAR_MAX + 1; ++i)
401 table.offset_table [i] = UINT_MAX;
402
403 // insert it into the map
404 unsigned int tmp = unsigned (xlit_table_map_.size ());
405 xlit_table_map_.insert (std::make_pair(tmp, table));
406
407 // store its index at correct position in current table
408 res->second.offset_table [off_idx] = tmp | 0x80000000;
409 table_idx = tmp;
410 } else {
411 table_idx =
412 res->second.offset_table [off_idx] & 0x7FFFFFFF;
413 }
414 }
415 }
416}
417
418
420write_codecvt (std::string dir_name)
421{
422 // if it has been already written
423 if (codecvt_written_)
424 return;
425
426 // compose the directory name
427 ((dir_name += _RWSTD_PATH_SEP) += "..") += _RWSTD_PATH_SEP;
428 dir_name += charmap_.get_code_set_name ();
429
430 // check to see if the codecvt database already exists and
431 // avoid recreating it if it does (as an optimization)
432 if (std::ifstream (dir_name.c_str ())) {
433 issue_diag (I_OPENWR, false, 0,
434 "%s exists, skipping\n", dir_name.c_str ());
435 return;
436 }
437
439 // generate multibyte conversion tables
440 issue_diag (I_STAGE, false, 0, "generating multibyte tables\n");
441
442 codecvt_offsets_map_t mbchar_offs;
443 std::map<std::string, unsigned> off_map;
444 const std::size_t n_mbchars = gen_mbchar_tables (mbchar_offs, off_map);
445
446 // generate wchar_t conversion tables
447 issue_diag (I_STAGE, false, 0, "generating wchar_t tables\n");
448
449 codecvt_offsets_map_t wchar_offs;
450 const std::size_t n_wchars = gen_wchar_tables (wchar_offs);
451
452 // generate UTF-8 conversion conversion tables
453 issue_diag (I_STAGE, false, 0, "generating UTF-8 tables\n");
454
455 codecvt_offsets_map_t uchar_offs;
456 const std::size_t n_uchars = gen_utf8_tables (uchar_offs, off_map);
457
458 // not needed beyond this point, clear it out
459 off_map.clear ();
460
461 // generate the transliteration tables and the transliteration data
462 issue_diag (I_STAGE, false, 0, "generating transliteration tables\n");
463 gen_xlit_data ();
464
466 // populate the codecvt structure before writing it out
467 // in binary form to the file (the codecvt database)
468 _RW::__rw_codecvt_t codecvt_out;
469 std::memset (&codecvt_out, 0, sizeof codecvt_out);
470
471#define UINT(x) _RWSTD_STATIC_CAST(unsigned, x)
472
473 // calculate byte offsets within the structure
474 codecvt_out.n_to_w_tab_off = 0;
475 codecvt_out.w_to_n_tab_off = UINT (codecvt_out.n_to_w_tab_off
476 + mbchar_offs.size () * (UCHAR_MAX + 1) * sizeof (unsigned));
477
478 codecvt_out.utf8_to_ext_tab_off = UINT (codecvt_out.w_to_n_tab_off
479 + wchar_offs.size () * (UCHAR_MAX + 1) * sizeof (unsigned));
480
481 // insert the transliteration tables here
482 codecvt_out.xliteration_off = UINT (codecvt_out.utf8_to_ext_tab_off
483 + uchar_offs.size () * (UCHAR_MAX + 1) * sizeof (unsigned));
484
485 codecvt_out.wchar_off = UINT (codecvt_out.xliteration_off
486 + xlit_table_map_.size () * (UCHAR_MAX + 1) * sizeof (unsigned));
487
488 codecvt_out.codeset_off = UINT (codecvt_out.wchar_off
489 + charmap_.get_mb_cmap ().size () * 2 * sizeof (wchar_t));
490
491 codecvt_out.charmap_off = UINT (codecvt_out.codeset_off
492 + charmap_.get_code_set_name ().size () + 1 /* NUL */);
493
494 const std::size_t mb_offset = UINT (codecvt_out.charmap_off
495 + charmap_.get_charmap_name ().size () + 1 /* NUL */);
496
497 // compute the size of narrow strings map which added to
498 // mb_offset will give the start of the transliteration data
499 std::size_t xlit_data_offset = mb_offset;
500
501 mb_cmap_iter iter;
502
503 for (iter = charmap_.get_mb_cmap ().begin();
504 iter != charmap_.get_mb_cmap().end(); ++iter) {
505 xlit_data_offset += iter->first.size() + 1;
506 }
507
508 // now traverse again the utf8 tables for transliteration data
509 // and recompute the offsets:
510 const xlit_table_map_t::const_iterator xlit_table_map_end =
511 xlit_table_map_.end ();
512
513 xlit_table_map_t::iterator xit = xlit_table_map_.begin ();
514 for (; xit != xlit_table_map_end; ++xit) {
515 for (unsigned int i = 0; i < UCHAR_MAX + 1; ++i) {
516 if (xit->second.offset_table [i] & 0x80000000)
517 continue;
518 // add the offset for xliteration data
519 xit->second.offset_table [i] += UINT (xlit_data_offset);
520 }
521 }
522
523 codecvt_out.mb_cur_max = charmap_.get_mb_cur_max();
524
525 issue_diag (I_OPENWR, false, 0, "writing %s\n", dir_name.c_str ());
526
527 // create the stream with exceptions enabled
528 std::ofstream out (dir_name.c_str(), std::ios::binary);
529 out.exceptions (std::ios::failbit | std::ios::badbit);
530
531 // write the codecvt_out structure
532 out.write ((char*)&codecvt_out, sizeof codecvt_out);
533
534 typedef codecvt_offsets_map_t::iterator off_iter_t;
535
537 // write out the multibyte to wchar_t tables
538 issue_diag (I_WRITE, false, 0,
539 "writing %lu multibyte tables (%lu characters)\n",
540 mbchar_offs.size (), n_mbchars);
541
542 for (off_iter_t it = mbchar_offs.begin (); it != mbchar_offs.end (); ++it) {
543 for (unsigned i = 0; i <= UCHAR_MAX; ++i) {
544
545 const unsigned off = it->second->off [i];
546
547 out.write ((const char*)&off, sizeof off);
548 }
549
550 delete it->second;
551 }
552
553 // not needed beyond this point, clear it out
554 mbchar_offs.clear ();
555
557 // write out the wchar_t to multibyte conversion tables
558 issue_diag (I_WRITE, false, 0,
559 "writing %lu wchar_t tables (%lu characters)\n",
560 wchar_offs.size (), n_wchars);
561
562 for (off_iter_t it = wchar_offs.begin (); it != wchar_offs.end (); ++it) {
563 for (unsigned i = 0; i <= UCHAR_MAX; ++i) {
564
565 // adjust offsets to multibyte characters (but not those
566 // to other tables or invalid encodings)
567 unsigned off = it->second->off [i];
568
569 if (!(off & 0x80000000))
570 off += UINT (mb_offset);
571
572 out.write ((const char*)&off, sizeof off);
573 }
574
575 delete it->second;
576 }
577
578 // not needed beyond this point, clear it out
579 wchar_offs.clear ();
580
582 // write out the UTF-8 to (libc) multibyte tables
583 issue_diag (I_WRITE, false, 0,
584 "writing %lu UTF-8 tables (%lu characters)\n",
585 uchar_offs.size (), n_uchars);
586
587 for (off_iter_t it = uchar_offs.begin (); it != uchar_offs.end (); ++it) {
588 for (unsigned i = 0; i <= UCHAR_MAX; ++i) {
589
590 // adjust offsets to multibyte characters (but not those
591 // to other tables or invalid encodings)
592 unsigned off = it->second->off [i];
593
594 if (!(off & 0x80000000))
595 off += UINT (mb_offset);
596
597 out.write ((const char*)&off, sizeof off);
598 }
599
600 delete it->second;
601 }
602
603 // not needed beyond this point, clear it out
604 uchar_offs.clear ();
605
607 // write out the transliteration UTF-8 lookup tables
608 issue_diag (I_WRITE, false, 0,
609 "writing transliteration table (size %lu)\n",
610 xlit_table_map_.size ());
611
612 xit = xlit_table_map_.begin ();
613 for (; xit != xlit_table_map_end; ++xit) {
614 const unsigned int* ptable = &xit->second.offset_table [0];
615 for (unsigned int i = 0; i < UCHAR_MAX + 1; ++i, ++ptable)
616 out.write ((const char*)ptable, sizeof (unsigned int));
617 }
618
619 issue_diag (I_WRITE, false, 0,
620 "writing the UCS table (%lu characters)\n",
621 charmap_.get_mb_cmap ().size ());
622
623 const mb_cmap_iter n_cmap2_end = charmap_.get_mb_cmap ().end ();
624
625 // write the locale-encoded wchar_t and the UCS4 wchar_t
626 for (iter = charmap_.get_mb_cmap ().begin();
627 iter != n_cmap2_end; ++iter) {
628 out.write ((const char*)&iter->second, sizeof (iter->second));
629 out.write ((const char*)& (charmap_.get_ucs4_cmap().find
630 (charmap_.get_rw_cmap().find
631 (iter->second)->second))->second,
632 sizeof (wchar_t));
633 }
634
635 // write the code_set_name string and charmap string
636 out << charmap_.get_code_set_name() << std::ends
637 << charmap_.get_charmap_name() << std::ends;
638
639
640 // write out the narrow character strings
641 for (iter = charmap_.get_mb_cmap().begin();
642 iter != n_cmap2_end; ++iter) {
643 out.write (iter->first.c_str(), iter->first.size() + 1);
644 }
645
646 issue_diag (I_WRITE, false, 0,
647 "writing transliteration data (size %lu)\n",
648 xlit_map_.size ());
649
650 // write out the transliteration data
651 xlit_map_t::const_iterator xlit_data_it = xlit_map_.begin ();
652 for (; xlit_data_it != xlit_map_.end (); ++xlit_data_it) {
653 std::list<std::string>::const_iterator sit =
654 xlit_data_it->second.begin ();
655 for (; sit != xlit_data_it->second.end (); ++sit) {
656 out.write (sit->c_str (), sit->size () + 1);
657 }
658 out.write ("\0", 1);
659 }
660}
#define i
Definition 17.names.cpp:126
#define k
Definition 17.names.cpp:128
#define nchars
Definition 17.names.cpp:502
#define UINT
Definition 17.names.cpp:565
#define res
Definition 17.names.cpp:531
#define last
Definition 17.names.cpp:487
#define off
Definition 17.names.cpp:507
#define tmp
Definition 17.names.cpp:553
const int out
#define assert(expr)
Definition _cassert.h:40
unsigned short wchar_t
#define _RWSTD_PATH_SEP
Definition _config.h:297
const std::map< wchar_t, std::string > & get_rmb_cmap() const
Definition charmap.h:89
int get_mb_cur_max() const
Definition charmap.h:107
const std::string & get_code_set_name() const
Definition charmap.h:112
const std::map< wchar_t, std::string > & get_rucs4_cmap() const
Definition charmap.h:102
std::string get_charmap_name() const
Definition charmap.cpp:475
const std::map< wchar_t, std::string > & get_rw_cmap() const
Definition charmap.h:77
const std::map< std::string, wchar_t > & get_w_cmap() const
Definition charmap.h:71
const std::map< std::string, wchar_t > & get_ucs4_cmap() const
Definition charmap.h:98
const std::map< std::string, wchar_t > & get_mb_cmap() const
Definition charmap.h:83
void write_codecvt(std::string dir_name)
Definition codecvt.cpp:420
A TOML table.
Definition table.hpp:190
bool issue_diag(int type, bool, const Scanner::token_t *token, const char *fmt,...)
@ I_OPENWR
Definition diagnostic.h:83
@ I_WRITE
Definition diagnostic.h:85
@ I_STAGE
Definition diagnostic.h:81
const char * str
Definition exec.cpp:170
#define UCHAR_MAX
Definition limits.h:60
#define UINT_MAX
Definition limits.h:84
long prefix(const entry_type &entry)
Definition tele.cpp:58
std::map< std::string, wchar_t >::const_iterator n_cmap_citer2
Definition codecvt.cpp:41