diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/encoding_rs/generate-encoding-data.py | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/encoding_rs/generate-encoding-data.py')
-rw-r--r-- | third_party/rust/encoding_rs/generate-encoding-data.py | 2008 |
1 files changed, 2008 insertions, 0 deletions
diff --git a/third_party/rust/encoding_rs/generate-encoding-data.py b/third_party/rust/encoding_rs/generate-encoding-data.py new file mode 100644 index 0000000000..99cec1adc5 --- /dev/null +++ b/third_party/rust/encoding_rs/generate-encoding-data.py @@ -0,0 +1,2008 @@ +#!/usr/bin/python + +# Copyright Mozilla Foundation. See the COPYRIGHT +# file at the top-level directory of this distribution. +# +# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +# https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +import json +import subprocess +import sys +import os.path + +if (not os.path.isfile("../encoding/encodings.json")) or (not os.path.isfile("../encoding/indexes.json")): + sys.stderr.write("This script needs a clone of https://github.com/whatwg/encoding/ (preferably at revision f381389) next to the encoding_rs directory.\n"); + sys.exit(-1) + +if not os.path.isfile("../encoding_c/src/lib.rs"): + sys.stderr.write("This script also writes the generated parts of the encoding_c crate and needs a clone of https://github.com/hsivonen/encoding_c next to the encoding_rs directory.\n"); + sys.exit(-1) + +if not os.path.isfile("../codepage/src/lib.rs"): + sys.stderr.write("This script also writes the generated parts of the codepage crate and needs a clone of https://github.com/hsivonen/codepage next to the encoding_rs directory.\n"); + sys.exit(-1) + +def cmp_from_end(one, other): + c = cmp(len(one), len(other)) + if c != 0: + return c + i = len(one) - 1 + while i >= 0: + c = cmp(one[i], other[i]) + if c != 0: + return c + i -= 1 + return 0 + + +class Label: + def __init__(self, label, preferred): + self.label = label + self.preferred = preferred + def __cmp__(self, other): + return cmp_from_end(self.label, other.label) + +class CodePage: + def __init__(self, code_page, preferred): + self.code_page = code_page + self.preferred = preferred + def __cmp__(self, other): + return self.code_page, other.code_page + +def static_u16_table(name, data): + data_file.write('''pub static %s: [u16; %d] = [ + ''' % (name, len(data))) + + for i in xrange(len(data)): + data_file.write('0x%04X,\n' % data[i]) + + data_file.write(''']; + + ''') + +def static_u16_table_from_indexable(name, data, item, feature): + data_file.write('''#[cfg(all( + feature = "less-slow-%s", + not(feature = "fast-%s") +))] +static %s: [u16; %d] = [ + ''' % (feature, feature, name, len(data))) + + for i in xrange(len(data)): + data_file.write('0x%04X,\n' % data[i][item]) + + data_file.write(''']; + + ''') + +def static_u8_pair_table_from_indexable(name, data, item, feature): + data_file.write('''#[cfg(all( + feature = "less-slow-%s", + not(feature = "fast-%s") +))] +static %s: [[u8; 2]; %d] = [ + ''' % (feature, feature, name, len(data))) + + for i in xrange(len(data)): + data_file.write('[0x%02X, 0x%02X],\n' % data[i][item]) + + data_file.write(''']; + + ''') + +def static_u8_pair_table(name, data, feature): + data_file.write('''#[cfg(feature = "%s")] +static %s: [[u8; 2]; %d] = [ + ''' % (feature, name, len(data))) + + for i in xrange(len(data)): + pair = data[i] + if not pair: + pair = (0, 0) + data_file.write('[0x%02X, 0x%02X],\n' % pair) + + data_file.write(''']; + + ''') + +preferred = [] + +dom = [] + +labels = [] + +data = json.load(open("../encoding/encodings.json", "r")) + +indexes = json.load(open("../encoding/indexes.json", "r")) + +single_byte = [] + +multi_byte = [] + +def to_camel_name(name): + if name == u"iso-8859-8-i": + return u"Iso8I" + if name.startswith(u"iso-8859-"): + return name.replace(u"iso-8859-", u"Iso") + return name.title().replace(u"X-", u"").replace(u"-", u"").replace(u"_", u"") + +def to_constant_name(name): + return name.replace(u"-", u"_").upper() + +def to_snake_name(name): + return name.replace(u"-", u"_").lower() + +def to_dom_name(name): + return name + +# Guestimate based on +# https://w3techs.com/technologies/overview/character_encoding/all +# whose methodology is known to be bogus, but the results are credible for +# this purpose. UTF-16LE lifted up due to prevalence on Windows and +# "ANSI codepages" prioritized. +encodings_by_code_page_frequency = [ + "UTF-8", + "UTF-16LE", + "windows-1252", + "windows-1251", + "GBK", + "Shift_JIS", + "EUC-KR", + "windows-1250", + "windows-1256", + "windows-1254", + "Big5", + "windows-874", + "windows-1255", + "windows-1253", + "windows-1257", + "windows-1258", + "EUC-JP", + "ISO-8859-2", + "ISO-8859-15", + "ISO-8859-7", + "KOI8-R", + "gb18030", + "ISO-8859-5", + "ISO-8859-8-I", + "ISO-8859-4", + "ISO-8859-6", + "ISO-2022-JP", + "KOI8-U", + "ISO-8859-13", + "ISO-8859-3", + "UTF-16BE", + "IBM866", + "ISO-8859-10", + "ISO-8859-8", + "macintosh", + "x-mac-cyrillic", + "ISO-8859-14", + "ISO-8859-16", +] + +encodings_by_code_page = { + 932: "Shift_JIS", + 936: "GBK", + 949: "EUC-KR", + 950: "Big5", + 866: "IBM866", + 874: "windows-874", + 1200: "UTF-16LE", + 1201: "UTF-16BE", + 1250: "windows-1250", + 1251: "windows-1251", + 1252: "windows-1252", + 1253: "windows-1253", + 1254: "windows-1254", + 1255: "windows-1255", + 1256: "windows-1256", + 1257: "windows-1257", + 1258: "windows-1258", + 10000: "macintosh", + 10017: "x-mac-cyrillic", + 20866: "KOI8-R", + 20932: "EUC-JP", + 21866: "KOI8-U", + 28592: "ISO-8859-2", + 28593: "ISO-8859-3", + 28594: "ISO-8859-4", + 28595: "ISO-8859-5", + 28596: "ISO-8859-6", + 28597: "ISO-8859-7", + 28598: "ISO-8859-8", + 28600: "ISO-8859-10", + 28603: "ISO-8859-13", + 28604: "ISO-8859-14", + 28605: "ISO-8859-15", + 28606: "ISO-8859-16", + 38598: "ISO-8859-8-I", + 50221: "ISO-2022-JP", + 54936: "gb18030", + 65001: "UTF-8", +} + +code_pages_by_encoding = {} + +for code_page, encoding in encodings_by_code_page.iteritems(): + code_pages_by_encoding[encoding] = code_page + +encoding_by_alias_code_page = { + 951: "Big5", + 10007: "x-mac-cyrillic", + 20936: "GBK", + 20949: "EUC-KR", + 21010: "UTF-16LE", # Undocumented; needed by calamine for Excel compat + 28591: "windows-1252", + 28599: "windows-1254", + 28601: "windows-874", + 50220: "ISO-2022-JP", + 50222: "ISO-2022-JP", + 50225: "replacement", # ISO-2022-KR + 50227: "replacement", # ISO-2022-CN + 51949: "EUC-JP", + 51936: "GBK", + 51949: "EUC-KR", + 52936: "replacement", # HZ +} + +code_pages = [] + +for name in encodings_by_code_page_frequency: + code_pages.append(code_pages_by_encoding[name]) + +encodings_by_code_page.update(encoding_by_alias_code_page) + +temp_keys = encodings_by_code_page.keys() +temp_keys.sort() +for code_page in temp_keys: + if not code_page in code_pages: + code_pages.append(code_page) + +# The position in the index (0 is the first index entry, +# i.e. byte value 0x80) that starts the longest run of +# consecutive code points. Must not be in the first +# quadrant. If the character to be encoded is not in this +# run, the part of the index after the run is searched +# forward. Then the part of the index from 32 to the start +# of the run. The first quadrant is searched last. +# +# If there is no obviously most useful longest run, +# the index here is just used to affect the search order. +start_of_longest_run_in_single_byte = { + "IBM866": 96, # 0 would be longest, but we don't want to start in the first quadrant + "windows-874": 33, + "windows-1250": 92, + "windows-1251": 64, + "windows-1252": 32, + "windows-1253": 83, + "windows-1254": 95, + "windows-1255": 96, + "windows-1256": 65, + "windows-1257": 95, # not actually longest + "windows-1258": 95, # not actually longest + "macintosh": 106, # useless + "x-mac-cyrillic": 96, + "KOI8-R": 64, # not actually longest + "KOI8-U": 64, # not actually longest + "ISO-8859-2": 95, # not actually longest + "ISO-8859-3": 95, # not actually longest + "ISO-8859-4": 95, # not actually longest + "ISO-8859-5": 46, + "ISO-8859-6": 65, + "ISO-8859-7": 83, + "ISO-8859-8": 96, + "ISO-8859-10": 90, # not actually longest + "ISO-8859-13": 95, # not actually longest + "ISO-8859-14": 95, + "ISO-8859-15": 63, + "ISO-8859-16": 95, # not actually longest +} + +# + +for group in data: + if group["heading"] == "Legacy single-byte encodings": + single_byte = group["encodings"] + else: + multi_byte.extend(group["encodings"]) + for encoding in group["encodings"]: + preferred.append(encoding["name"]) + for label in encoding["labels"]: + labels.append(Label(label, encoding["name"])) + +for name in preferred: + dom.append(to_dom_name(name)) + +preferred.sort() +labels.sort() +dom.sort(cmp=cmp_from_end) + +longest_label_length = 0 +longest_name_length = 0 +longest_label = None +longest_name = None + +for name in preferred: + if len(name) > longest_name_length: + longest_name_length = len(name) + longest_name = name + +for label in labels: + if len(label.label) > longest_label_length: + longest_label_length = len(label.label) + longest_label = label.label + +def longest_run_for_single_byte(name): + if name == u"ISO-8859-8-I": + name = u"ISO-8859-8" + index = indexes[name.lower()] + run_byte_offset = start_of_longest_run_in_single_byte[name] + run_bmp_offset = index[run_byte_offset] + previous_code_point = run_bmp_offset + run_length = 1 + while True: + i = run_byte_offset + run_length + if i == len(index): + break + code_point = index[i] + if previous_code_point + 1 != code_point: + break + previous_code_point = code_point + run_length += 1 + return (run_bmp_offset, run_byte_offset, run_length) + +def is_single_byte(name): + for encoding in single_byte: + if name == encoding["name"]: + return True + return False + +def read_non_generated(path): + partially_generated_file = open(path, "r") + full = partially_generated_file.read() + partially_generated_file.close() + + generated_begin = "// BEGIN GENERATED CODE. PLEASE DO NOT EDIT." + generated_end = "// END GENERATED CODE" + + generated_begin_index = full.find(generated_begin) + if generated_begin_index < 0: + sys.stderr.write("Can't find generated code start marker in %s. Exiting.\n" % path) + sys.exit(-1) + generated_end_index = full.find(generated_end) + if generated_end_index < 0: + sys.stderr.write("Can't find generated code end marker in %s. Exiting.\n" % path) + sys.exit(-1) + + return (full[0:generated_begin_index + len(generated_begin)], + full[generated_end_index:]) + +(lib_rs_begin, lib_rs_end) = read_non_generated("src/lib.rs") + +label_file = open("src/lib.rs", "w") + +label_file.write(lib_rs_begin) +label_file.write(""" +// Instead, please regenerate using generate-encoding-data.py + +const LONGEST_LABEL_LENGTH: usize = %d; // %s + +""" % (longest_label_length, longest_label)) + +for name in preferred: + variant = None + if is_single_byte(name): + (run_bmp_offset, run_byte_offset, run_length) = longest_run_for_single_byte(name) + variant = "SingleByte(&data::SINGLE_BYTE_DATA.%s, 0x%04X, %d, %d)" % (to_snake_name(u"iso-8859-8" if name == u"ISO-8859-8-I" else name), run_bmp_offset, run_byte_offset, run_length) + else: + variant = to_camel_name(name) + + docfile = open("doc/%s.txt" % name, "r") + doctext = docfile.read() + docfile.close() + + label_file.write('''/// The initializer for the [%s](static.%s.html) encoding. +/// +/// For use only for taking the address of this form when +/// Rust prohibits the use of the non-`_INIT` form directly, +/// such as in initializers of other `static`s. If in doubt, +/// use the corresponding non-`_INIT` reference-typed `static`. +/// +/// This part of the public API will go away if Rust changes +/// to make the referent of `pub const FOO: &'static Encoding` +/// unique cross-crate or if Rust starts allowing static arrays +/// to be initialized with `pub static FOO: &'static Encoding` +/// items. +pub static %s_INIT: Encoding = Encoding { + name: "%s", + variant: VariantEncoding::%s, +}; + +/// The %s encoding. +/// +%s/// +/// This will change from `static` to `const` if Rust changes +/// to make the referent of `pub const FOO: &'static Encoding` +/// unique cross-crate, so don't take the address of this +/// `static`. +pub static %s: &'static Encoding = &%s_INIT; + +''' % (to_dom_name(name), to_constant_name(name), to_constant_name(name), to_dom_name(name), variant, to_dom_name(name), doctext, to_constant_name(name), to_constant_name(name))) + +label_file.write("""static LABELS_SORTED: [&'static str; %d] = [ +""" % len(labels)) + +for label in labels: + label_file.write('''"%s",\n''' % label.label) + +label_file.write("""]; + +static ENCODINGS_IN_LABEL_SORT: [&'static Encoding; %d] = [ +""" % len(labels)) + +for label in labels: + label_file.write('''&%s_INIT,\n''' % to_constant_name(label.preferred)) + +label_file.write(''']; + +''') +label_file.write(lib_rs_end) +label_file.close() + +label_test_file = open("src/test_labels_names.rs", "w") +label_test_file.write('''// Any copyright to the test code below this comment is dedicated to the +// Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ + +// THIS IS A GENERATED FILE. PLEASE DO NOT EDIT. +// Instead, please regenerate using generate-encoding-data.py + +use super::*; + +#[test] +fn test_all_labels() { +''') + +for label in labels: + label_test_file.write('''assert_eq!(Encoding::for_label(b"%s"), Some(%s));\n''' % (label.label, to_constant_name(label.preferred))) + +label_test_file.write('''} +''') +label_test_file.close() + +def null_to_zero(code_point): + if not code_point: + code_point = 0 + return code_point + +(data_rs_begin, data_rs_end) = read_non_generated("src/data.rs") + +data_file = open("src/data.rs", "w") +data_file.write(data_rs_begin) +data_file.write(''' +// Instead, please regenerate using generate-encoding-data.py + +#[repr(align(64))] // Align to cache lines +pub struct SingleByteData { +''') + +# Single-byte + +for encoding in single_byte: + name = encoding["name"] + if name == u"ISO-8859-8-I": + continue + + data_file.write(''' pub %s: [u16; 128], +''' % to_snake_name(name)) + +data_file.write('''} + +pub static SINGLE_BYTE_DATA: SingleByteData = SingleByteData { +''') + +for encoding in single_byte: + name = encoding["name"] + if name == u"ISO-8859-8-I": + continue + + data_file.write(''' %s: [ +''' % to_snake_name(name)) + + for code_point in indexes[name.lower()]: + data_file.write('0x%04X,\n' % null_to_zero(code_point)) + + data_file.write('''], +''') + +data_file.write('''}; + +''') + +# Big5 + +index = indexes["big5"] + +astralness = [] +low_bits = [] + +for code_point in index[942:19782]: + if code_point: + astralness.append(1 if code_point > 0xFFFF else 0) + low_bits.append(code_point & 0xFFFF) + else: + astralness.append(0) + low_bits.append(0) + +# pad length to multiple of 32 +for j in xrange(32 - (len(astralness) % 32)): + astralness.append(0) + +data_file.write('''#[cfg_attr(feature = "cargo-clippy", allow(unreadable_literal))] +static BIG5_ASTRALNESS: [u32; %d] = [ +''' % (len(astralness) / 32)) + +i = 0 +while i < len(astralness): + accu = 0 + for j in xrange(32): + accu |= astralness[i + j] << j + data_file.write('0x%08X,\n' % accu) + i += 32 + +data_file.write(''']; + +''') + +static_u16_table("BIG5_LOW_BITS", low_bits) + +# Encoder table for Level 1 Hanzi +# Note: If we were OK with doubling this table, we +# could use a directly-indexable table instead... +level1_hanzi_index = index[5495:10896] +level1_hanzi_pairs = [] +for i in xrange(len(level1_hanzi_index)): + hanzi_lead = (i / 157) + 0xA4 + hanzi_trail = (i % 157) + hanzi_trail += 0x40 if hanzi_trail < 0x3F else 0x62 + level1_hanzi_pairs.append((level1_hanzi_index[i], (hanzi_lead, hanzi_trail))) +level1_hanzi_pairs.append((0x4E5A, (0xC8, 0x7B))) +level1_hanzi_pairs.append((0x5202, (0xC8, 0x7D))) +level1_hanzi_pairs.append((0x9FB0, (0xC8, 0xA1))) +level1_hanzi_pairs.append((0x5188, (0xC8, 0xA2))) +level1_hanzi_pairs.append((0x9FB1, (0xC8, 0xA3))) +level1_hanzi_pairs.sort(key=lambda x: x[0]) + +static_u16_table_from_indexable("BIG5_LEVEL1_HANZI_CODE_POINTS", level1_hanzi_pairs, 0, "big5-hanzi-encode") +static_u8_pair_table_from_indexable("BIG5_LEVEL1_HANZI_BYTES", level1_hanzi_pairs, 1, "big5-hanzi-encode") + +# Fast Unified Ideograph encode +big5_unified_ideograph_bytes = [None] * (0x9FCC - 0x4E00) +for row in xrange(0x7E - 0x20): + for column in xrange(157): + pointer = 5024 + column + (row * 157) + code_point = index[pointer] + if code_point and code_point >= 0x4E00 and code_point <= 0x9FCB: + unified_offset = code_point - 0x4E00 + unified_lead = 0xA1 + row + unified_trail = (0x40 if column < 0x3F else 0x62) + column + if code_point == 0x5341 or code_point == 0x5345 or not big5_unified_ideograph_bytes[unified_offset]: + big5_unified_ideograph_bytes[unified_offset] = (unified_lead, unified_trail) + +static_u8_pair_table("BIG5_UNIFIED_IDEOGRAPH_BYTES", big5_unified_ideograph_bytes, "fast-big5-hanzi-encode") + +# JIS0208 + +index = indexes["jis0208"] + +# JIS 0208 Level 1 Kanji +static_u16_table("JIS0208_LEVEL1_KANJI", index[1410:4375]) + +# JIS 0208 Level 2 Kanji and Additional Kanji +static_u16_table("JIS0208_LEVEL2_AND_ADDITIONAL_KANJI", index[4418:7808]) + +# IBM Kanji +static_u16_table("IBM_KANJI", index[8272:8632]) + +# Check that the other instance is the same +if index[8272:8632] != index[10744:11104]: + raise Error() + +# JIS 0208 symbols (all non-Kanji, non-range items) +symbol_index = [] +symbol_triples = [] +pointers_to_scan = [ + (0, 188), + (658, 691), + (1159, 1221), +] +in_run = False +run_start_pointer = 0 +run_start_array_index = 0 +for (start, end) in pointers_to_scan: + for i in range(start, end): + code_point = index[i] + if in_run: + if code_point: + symbol_index.append(code_point) + else: + symbol_triples.append(run_start_pointer) + symbol_triples.append(i - run_start_pointer) + symbol_triples.append(run_start_array_index) + in_run = False + else: + if code_point: + in_run = True + run_start_pointer = i + run_start_array_index = len(symbol_index) + symbol_index.append(code_point) + if in_run: + symbol_triples.append(run_start_pointer) + symbol_triples.append(end - run_start_pointer) + symbol_triples.append(run_start_array_index) + in_run = False +if in_run: + raise Error() + +# Now add manually the two overlapping slices of +# index from the NEC/IBM extensions. +run_start_array_index = len(symbol_index) +symbol_index.extend(index[10736:10744]) +# Later +symbol_triples.append(10736) +symbol_triples.append(8) +symbol_triples.append(run_start_array_index) +# Earlier +symbol_triples.append(8644) +symbol_triples.append(4) +symbol_triples.append(run_start_array_index) + +static_u16_table("JIS0208_SYMBOLS", symbol_index) +static_u16_table("JIS0208_SYMBOL_TRIPLES", symbol_triples) + +# Write down the magic numbers needed when preferring the earlier case +data_file.write('''const IBM_SYMBOL_START: usize = %d;''' % (run_start_array_index + 1)) +data_file.write('''const IBM_SYMBOL_END: usize = %d;''' % (run_start_array_index + 4)) +data_file.write('''const IBM_SYMBOL_POINTER_START: usize = %d;''' % 8645) + +# JIS 0208 ranges (excluding kana) +range_triples = [] +pointers_to_scan = [ + (188, 281), + (470, 657), + (1128, 1159), + (8634, 8644), + (10716, 10736), +] +in_run = False +run_start_pointer = 0 +run_start_code_point = 0 +previous_code_point = 0 +for (start, end) in pointers_to_scan: + for i in range(start, end): + code_point = index[i] + if in_run: + if code_point: + if previous_code_point + 1 != code_point: + range_triples.append(run_start_pointer) + range_triples.append(i - run_start_pointer) + range_triples.append(run_start_code_point) + run_start_pointer = i + run_start_code_point = code_point + previous_code_point = code_point + else: + range_triples.append(run_start_pointer) + range_triples.append(i - run_start_pointer) + range_triples.append(run_start_code_point) + run_start_pointer = 0 + run_start_code_point = 0 + previous_code_point = 0 + in_run = False + else: + if code_point: + in_run = True + run_start_pointer = i + run_start_code_point = code_point + previous_code_point = code_point + if in_run: + range_triples.append(run_start_pointer) + range_triples.append(end - run_start_pointer) + range_triples.append(run_start_code_point) + run_start_pointer = 0 + run_start_code_point = 0 + previous_code_point = 0 + in_run = False +if in_run: + raise Error() + +static_u16_table("JIS0208_RANGE_TRIPLES", range_triples) + +# Encoder table for Level 1 Kanji +# Note: If we were OK with 30 KB more footprint, we +# could use a directly-indexable table instead... +level1_kanji_index = index[1410:4375] +level1_kanji_pairs = [] +for i in xrange(len(level1_kanji_index)): + pointer = 1410 + i + (lead, trail) = divmod(pointer, 188) + lead += 0x81 if lead < 0x1F else 0xC1 + trail += 0x40 if trail < 0x3F else 0x41 + level1_kanji_pairs.append((level1_kanji_index[i], (lead, trail))) +level1_kanji_pairs.sort(key=lambda x: x[0]) + +static_u16_table_from_indexable("JIS0208_LEVEL1_KANJI_CODE_POINTS", level1_kanji_pairs, 0, "kanji-encode") +static_u8_pair_table_from_indexable("JIS0208_LEVEL1_KANJI_SHIFT_JIS_BYTES", level1_kanji_pairs, 1, "kanji-encode") + +# Fast encoder table for Kanji +kanji_bytes = [None] * (0x9FA1 - 0x4E00) +for pointer in xrange(len(index)): + code_point = index[pointer] + if code_point and code_point >= 0x4E00 and code_point <= 0x9FA0: + (lead, trail) = divmod(pointer, 188) + lead += 0x81 if lead < 0x1F else 0xC1 + trail += 0x40 if trail < 0x3F else 0x41 + # unset the high bit of lead if IBM Kanji + if pointer >= 8272: + lead = lead & 0x7F + kanji_bytes[code_point - 0x4E00] = (lead, trail) + +static_u8_pair_table("JIS0208_KANJI_BYTES", kanji_bytes, "fast-kanji-encode") + +# ISO-2022-JP half-width katakana + +# index is still jis0208 +half_width_index = indexes["iso-2022-jp-katakana"] + +data_file.write('''pub static ISO_2022_JP_HALF_WIDTH_TRAIL: [u8; %d] = [ +''' % len(half_width_index)) + +for i in xrange(len(half_width_index)): + code_point = half_width_index[i] + pointer = index.index(code_point) + trail = pointer % 94 + 0x21 + data_file.write('0x%02X,\n' % trail) + +data_file.write(''']; + +''') + +# EUC-KR + +index = indexes["euc-kr"] + +# Unicode 1.1 Hangul above the old KS X 1001 block +# Compressed form takes 35% of uncompressed form +pointers = [] +offsets = [] +previous_code_point = 0 +for row in xrange(0x20): + for column in xrange(190): + i = column + (row * 190) + # Skip the gaps + if (column >= 0x1A and column < 0x20) or (column >= 0x3A and column < 0x40): + continue + code_point = index[i] + if previous_code_point > code_point: + raise Error() + if code_point - previous_code_point != 1: + adjustment = 0 + if column >= 0x40: + adjustment = 12 + elif column >= 0x20: + adjustment = 6 + pointers.append(column - adjustment + (row * (190 - 12))) + offsets.append(code_point) + previous_code_point = code_point + +static_u16_table("CP949_TOP_HANGUL_POINTERS", pointers) +static_u16_table("CP949_TOP_HANGUL_OFFSETS", offsets) + +# Unicode 1.1 Hangul to the left of the old KS X 1001 block +pointers = [] +offsets = [] +previous_code_point = 0 +for row in xrange(0x46 - 0x20): + for column in xrange(190 - 94): + i = 6080 + column + (row * 190) + # Skip the gaps + if (column >= 0x1A and column < 0x20) or (column >= 0x3A and column < 0x40): + continue + if i > 13127: + # Exclude unassigned on partial last row + break + code_point = index[i] + if previous_code_point > code_point: + raise Error() + if code_point - previous_code_point != 1: + adjustment = 0 + if column >= 0x40: + adjustment = 12 + elif column >= 0x20: + adjustment = 6 + pointers.append(column - adjustment + (row * (190 - 94 - 12))) + offsets.append(code_point) + previous_code_point = code_point + +static_u16_table("CP949_LEFT_HANGUL_POINTERS", pointers) +static_u16_table("CP949_LEFT_HANGUL_OFFSETS", offsets) + +# KS X 1001 Hangul +hangul_index = [] +previous_code_point = 0 +for row in xrange(0x48 - 0x2F): + for column in xrange(94): + code_point = index[9026 + column + (row * 190)] + if previous_code_point >= code_point: + raise Error() + hangul_index.append(code_point) + previous_code_point = code_point + +static_u16_table("KSX1001_HANGUL", hangul_index) + +# KS X 1001 Hanja +hanja_index = [] +for row in xrange(0x7D - 0x49): + for column in xrange(94): + hanja_index.append(index[13966 + column + (row * 190)]) + +static_u16_table("KSX1001_HANJA", hanja_index) + +# KS X 1001 symbols +symbol_index = [] +for i in range(6176, 6270): + symbol_index.append(index[i]) +for i in range(6366, 6437): + symbol_index.append(index[i]) + +static_u16_table("KSX1001_SYMBOLS", symbol_index) + +# KS X 1001 Uppercase Latin +subindex = [] +for i in range(7506, 7521): + subindex.append(null_to_zero(index[i])) + +static_u16_table("KSX1001_UPPERCASE", subindex) + +# KS X 1001 Lowercase Latin +subindex = [] +for i in range(7696, 7712): + subindex.append(index[i]) + +static_u16_table("KSX1001_LOWERCASE", subindex) + +# KS X 1001 Box drawing +subindex = [] +for i in range(7126, 7194): + subindex.append(index[i]) + +static_u16_table("KSX1001_BOX", subindex) + +# KS X 1001 other +pointers = [] +offsets = [] +previous_code_point = 0 +for row in xrange(10): + for column in xrange(94): + i = 6556 + column + (row * 190) + code_point = index[i] + # Exclude ranges that were processed as lookup tables + # or that contain unmapped cells by filling them with + # ASCII. Upon encode, ASCII code points will + # never appear as the search key. + if (i >= 6946 and i <= 6950): + code_point = i - 6946 + elif (i >= 6961 and i <= 6967): + code_point = i - 6961 + elif (i >= 6992 and i <= 6999): + code_point = i - 6992 + elif (i >= 7024 and i <= 7029): + code_point = i - 7024 + elif (i >= 7126 and i <= 7219): + code_point = i - 7126 + elif (i >= 7395 and i <= 7409): + code_point = i - 7395 + elif (i >= 7506 and i <= 7521): + code_point = i - 7506 + elif (i >= 7696 and i <= 7711): + code_point = i - 7696 + elif (i >= 7969 and i <= 7979): + code_point = i - 7969 + elif (i >= 8162 and i <= 8169): + code_point = i - 8162 + elif (i >= 8299 and i <= 8313): + code_point = i - 8299 + elif (i >= 8347 and i <= 8359): + code_point = i - 8347 + if code_point - previous_code_point != 1: + pointers.append(column + (row * 94)) + offsets.append(code_point) + previous_code_point = code_point + +static_u16_table("KSX1001_OTHER_POINTERS", pointers) +# Omit the last offset, because the end of the last line +# is unmapped, so we don't want to look at it. +static_u16_table("KSX1001_OTHER_UNSORTED_OFFSETS", offsets[:-1]) + +# Fast Hangul and Hanja encode +hangul_bytes = [None] * (0xD7A4 - 0xAC00) +hanja_unified_bytes = [None] * (0x9F9D - 0x4E00) +hanja_compatibility_bytes = [None] * (0xFA0C - 0xF900) +for row in xrange(0x7D): + for column in xrange(190): + pointer = column + (row * 190) + code_point = index[pointer] + if code_point: + lead = 0x81 + row + trail = 0x41 + column + if code_point >= 0xAC00 and code_point < 0xD7A4: + hangul_bytes[code_point - 0xAC00] = (lead, trail) + elif code_point >= 0x4E00 and code_point < 0x9F9D: + hanja_unified_bytes[code_point - 0x4E00] = (lead, trail) + elif code_point >= 0xF900 and code_point < 0xFA0C: + hanja_compatibility_bytes[code_point - 0xF900] = (lead, trail) + +static_u8_pair_table("CP949_HANGUL_BYTES", hangul_bytes, "fast-hangul-encode") +static_u8_pair_table("KSX1001_UNIFIED_HANJA_BYTES", hanja_unified_bytes, "fast-hanja-encode") +static_u8_pair_table("KSX1001_COMPATIBILITY_HANJA_BYTES", hanja_compatibility_bytes, "fast-hanja-encode") + +# JIS 0212 + +index = indexes["jis0212"] + +# JIS 0212 Kanji +static_u16_table("JIS0212_KANJI", index[1410:7211]) + +# JIS 0212 accented (all non-Kanji, non-range items) +symbol_index = [] +symbol_triples = [] +pointers_to_scan = [ + (0, 596), + (608, 644), + (656, 1409), +] +in_run = False +run_start_pointer = 0 +run_start_array_index = 0 +for (start, end) in pointers_to_scan: + for i in range(start, end): + code_point = index[i] + if in_run: + if code_point: + symbol_index.append(code_point) + elif index[i + 1]: + symbol_index.append(0) + else: + symbol_triples.append(run_start_pointer) + symbol_triples.append(i - run_start_pointer) + symbol_triples.append(run_start_array_index) + in_run = False + else: + if code_point: + in_run = True + run_start_pointer = i + run_start_array_index = len(symbol_index) + symbol_index.append(code_point) + if in_run: + symbol_triples.append(run_start_pointer) + symbol_triples.append(end - run_start_pointer) + symbol_triples.append(run_start_array_index) + in_run = False +if in_run: + raise Error() + +static_u16_table("JIS0212_ACCENTED", symbol_index) +static_u16_table("JIS0212_ACCENTED_TRIPLES", symbol_triples) + +# gb18030 + +index = indexes["gb18030"] + +# Unicode 1.1 ideographs above the old GB2312 block +# Compressed form takes 63% of uncompressed form +pointers = [] +offsets = [] +previous_code_point = 0 +for i in xrange(6080): + code_point = index[i] + if previous_code_point > code_point: + raise Error() + if code_point - previous_code_point != 1: + pointers.append(i) + offsets.append(code_point) + previous_code_point = code_point + +static_u16_table("GBK_TOP_IDEOGRAPH_POINTERS", pointers) +static_u16_table("GBK_TOP_IDEOGRAPH_OFFSETS", offsets) + +# Unicode 1.1 ideographs to the left of the old GB2312 block +# Compressed form takes 40% of uncompressed form +pointers = [] +offsets = [] +previous_code_point = 0 +for row in xrange(0x7D - 0x29): + for column in xrange(190 - 94): + i = 7790 + column + (row * 190) + if i > 23650: + # Exclude compatibility ideographs at the end + break + code_point = index[i] + if previous_code_point > code_point: + raise Error() + if code_point - previous_code_point != 1: + pointers.append(column + (row * (190 - 94))) + offsets.append(code_point) + previous_code_point = code_point + +static_u16_table("GBK_LEFT_IDEOGRAPH_POINTERS", pointers) +static_u16_table("GBK_LEFT_IDEOGRAPH_OFFSETS", offsets) + +# GBK other (excl. Ext A, Compat & PUA at the bottom) +pointers = [] +offsets = [] +previous_code_point = 0 +for row in xrange(0x29 - 0x20): + for column in xrange(190 - 94): + i = 6080 + column + (row * 190) + code_point = index[i] + if code_point - previous_code_point != 1: + pointers.append(column + (row * (190 - 94))) + offsets.append(code_point) + previous_code_point = code_point + +pointers.append((190 - 94) * (0x29 - 0x20)) +static_u16_table("GBK_OTHER_POINTERS", pointers) +static_u16_table("GBK_OTHER_UNSORTED_OFFSETS", offsets) + +# GBK bottom: Compatibility ideagraphs, Ext A and PUA +bottom_index = [] +# 5 compat following Unified Ideographs +for i in range(23651, 23656): + bottom_index.append(index[i]) +# Last row +for i in range(23750, 23846): + bottom_index.append(index[i]) + +static_u16_table("GBK_BOTTOM", bottom_index) + +# GB2312 Hanzi +# (and the 5 PUA code points in between Level 1 and Level 2) +hanzi_index = [] +for row in xrange(0x77 - 0x2F): + for column in xrange(94): + hanzi_index.append(index[9026 + column + (row * 190)]) + +static_u16_table("GB2312_HANZI", hanzi_index) + +# GB2312 symbols +symbol_index = [] +for i in xrange(94): + symbol_index.append(index[6176 + i]) + +static_u16_table("GB2312_SYMBOLS", symbol_index) + +# GB2312 symbols on Greek row (incl. PUA) +symbol_index = [] +for i in xrange(22): + symbol_index.append(index[7189 + i]) + +static_u16_table("GB2312_SYMBOLS_AFTER_GREEK", symbol_index) + +# GB2312 Pinyin +pinyin_index = [] +for i in xrange(32): + pinyin_index.append(index[7506 + i]) + +static_u16_table("GB2312_PINYIN", pinyin_index) + +# GB2312 other (excl. bottom PUA) +pointers = [] +offsets = [] +previous_code_point = 0 +for row in xrange(14): + for column in xrange(94): + i = 6366 + column + (row * 190) + code_point = index[i] + # Exclude the two ranges that were processed as + # lookup tables above by filling them with + # ASCII. Upon encode, ASCII code points will + # never appear as the search key. + if (i >= 7189 and i < 7189 + 22): + code_point = i - 7189 + elif (i >= 7506 and i < 7506 + 32): + code_point = i - 7506 + if code_point - previous_code_point != 1: + pointers.append(column + (row * 94)) + offsets.append(code_point) + previous_code_point = code_point + +pointers.append(14 * 94) +static_u16_table("GB2312_OTHER_POINTERS", pointers) +static_u16_table("GB2312_OTHER_UNSORTED_OFFSETS", offsets) + +# Non-gbk code points +pointers = [] +offsets = [] +for pair in indexes["gb18030-ranges"]: + if pair[1] == 0x10000: + break # the last entry doesn't fit in u16 + pointers.append(pair[0]) + offsets.append(pair[1]) + +static_u16_table("GB18030_RANGE_POINTERS", pointers) +static_u16_table("GB18030_RANGE_OFFSETS", offsets) + +# Encoder table for Level 1 Hanzi +# The units here really fit into 12 bits, but since we're +# looking for speed here, let's use 16 bits per unit. +# Once we use 16 bits per unit, we might as well precompute +# the output bytes. +level1_hanzi_index = hanzi_index[:(94 * (0xD8 - 0xB0) - 5)] +level1_hanzi_pairs = [] +for i in xrange(len(level1_hanzi_index)): + hanzi_lead = (i / 94) + 0xB0 + hanzi_trail = (i % 94) + 0xA1 + level1_hanzi_pairs.append((level1_hanzi_index[i], (hanzi_lead, hanzi_trail))) +level1_hanzi_pairs.sort(key=lambda x: x[0]) + +static_u16_table_from_indexable("GB2312_LEVEL1_HANZI_CODE_POINTS", level1_hanzi_pairs, 0, "gb-hanzi-encode") +static_u8_pair_table_from_indexable("GB2312_LEVEL1_HANZI_BYTES", level1_hanzi_pairs, 1, "gb-hanzi-encode") + +# Fast Hanzi encoder table +hanzi_bytes = [None] * (0x9FA7 - 0x4E00) +for row in xrange(126): + for column in xrange(190): + pointer = column + (row * 190) + code_point = index[pointer] + if code_point and code_point >= 0x4E00 and code_point <= 0x9FA6: + hanzi_lead = 0x81 + row + hanzi_trail = column + (0x40 if column < 0x3F else 0x41) + hanzi_bytes[code_point - 0x4E00] = (hanzi_lead, hanzi_trail) + +static_u8_pair_table("GBK_HANZI_BYTES", hanzi_bytes, "fast-gb-hanzi-encode") + +data_file.write(data_rs_end) + +data_file.close() + +# Variant + +variant_file = open("src/variant.rs", "w") +variant_file.write('''// Copyright Mozilla Foundation. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// THIS IS A GENERATED FILE. PLEASE DO NOT EDIT. +// Instead, please regenerate using generate-encoding-data.py + +//! This module provides enums that wrap the various decoders and encoders. +//! The purpose is to make `Decoder` and `Encoder` `Sized` by writing the +//! dispatch explicitly for a finite set of specialized decoders and encoders. +//! Unfortunately, this means the compiler doesn't generate the dispatch code +//! and it has to be written here instead. +//! +//! The purpose of making `Decoder` and `Encoder` `Sized` is to allow stack +//! allocation in Rust code, including the convenience methods on `Encoding`. + +''') + +encoding_variants = [u"single-byte",] +for encoding in multi_byte: + if encoding["name"] in [u"UTF-16LE", u"UTF-16BE"]: + continue + else: + encoding_variants.append(encoding["name"]) +encoding_variants.append(u"UTF-16") + +decoder_variants = [] +for variant in encoding_variants: + if variant == u"GBK": + continue + decoder_variants.append(variant) + +encoder_variants = [] +for variant in encoding_variants: + if variant in [u"replacement", u"GBK", u"UTF-16"]: + continue + encoder_variants.append(variant) + +for variant in decoder_variants: + variant_file.write("use %s::*;\n" % to_snake_name(variant)) + +variant_file.write('''use super::*; + +pub enum VariantDecoder { +''') + +for variant in decoder_variants: + variant_file.write(" %s(%sDecoder),\n" % (to_camel_name(variant), to_camel_name(variant))) + +variant_file.write('''} + +impl VariantDecoder { +''') + +def write_variant_method(name, mut, arg_list, ret, variants, excludes, kind): + variant_file.write('''pub fn %s(&''' % name) + if mut: + variant_file.write('''mut ''') + variant_file.write('''self''') + for arg in arg_list: + variant_file.write(''', %s: %s''' % (arg[0], arg[1])) + variant_file.write(''')''') + if ret: + variant_file.write(''' -> %s''' % ret) + variant_file.write(''' {\nmatch *self {\n''') + for variant in variants: + variant_file.write('''Variant%s::%s(ref ''' % (kind, to_camel_name(variant))) + if mut: + variant_file.write('''mut ''') + if variant in excludes: + variant_file.write('''v) => (),''') + continue + variant_file.write('''v) => v.%s(''' % name) + first = True + for arg in arg_list: + if not first: + variant_file.write(''', ''') + first = False + variant_file.write(arg[0]) + variant_file.write('''),\n''') + variant_file.write('''}\n}\n\n''') + +write_variant_method("max_utf16_buffer_length", False, [("byte_length", "usize")], "Option<usize>", decoder_variants, [], "Decoder") + +write_variant_method("max_utf8_buffer_length_without_replacement", False, [("byte_length", "usize")], "Option<usize>", decoder_variants, [], "Decoder") + +write_variant_method("max_utf8_buffer_length", False, [("byte_length", "usize")], "Option<usize>", decoder_variants, [], "Decoder") + +write_variant_method("decode_to_utf16_raw", True, [("src", "&[u8]"), + ("dst", "&mut [u16]"), + ("last", "bool")], "(DecoderResult, usize, usize)", decoder_variants, [], "Decoder") + +write_variant_method("decode_to_utf8_raw", True, [("src", "&[u8]"), + ("dst", "&mut [u8]"), + ("last", "bool")], "(DecoderResult, usize, usize)", decoder_variants, [], "Decoder") + +variant_file.write(''' + + pub fn latin1_byte_compatible_up_to(&self, buffer: &[u8]) -> Option<usize> { + match *self { + VariantDecoder::SingleByte(ref v) => { + return Some(v.latin1_byte_compatible_up_to(buffer)); + } + VariantDecoder::Utf8(ref v) => { + if !v.in_neutral_state() { + return None; + } + } + VariantDecoder::Gb18030(ref v) => { + if !v.in_neutral_state() { + return None; + } + } + VariantDecoder::Big5(ref v) => { + if !v.in_neutral_state() { + return None; + } + } + VariantDecoder::EucJp(ref v) => { + if !v.in_neutral_state() { + return None; + } + } + VariantDecoder::Iso2022Jp(ref v) => { + if v.in_neutral_state() { + return Some(Encoding::iso_2022_jp_ascii_valid_up_to(buffer)); + } + return None; + } + VariantDecoder::ShiftJis(ref v) => { + if !v.in_neutral_state() { + return None; + } + } + VariantDecoder::EucKr(ref v) => { + if !v.in_neutral_state() { + return None; + } + } + VariantDecoder::UserDefined(_) => {} + VariantDecoder::Replacement(_) | VariantDecoder::Utf16(_) => { + return None; + } + }; + Some(Encoding::ascii_valid_up_to(buffer)) + } +} + +pub enum VariantEncoder { +''') + +for variant in encoder_variants: + variant_file.write(" %s(%sEncoder),\n" % (to_camel_name(variant), to_camel_name(variant))) + +variant_file.write('''} + +impl VariantEncoder { + pub fn has_pending_state(&self) -> bool { + match *self { + VariantEncoder::Iso2022Jp(ref v) => { + v.has_pending_state() + } + _ => false, + } + } +''') + +write_variant_method("max_buffer_length_from_utf16_without_replacement", False, [("u16_length", "usize")], "Option<usize>", encoder_variants, [], "Encoder") + +write_variant_method("max_buffer_length_from_utf8_without_replacement", False, [("byte_length", "usize")], "Option<usize>", encoder_variants, [], "Encoder") + +write_variant_method("encode_from_utf16_raw", True, [("src", "&[u16]"), + ("dst", "&mut [u8]"), + ("last", "bool")], "(EncoderResult, usize, usize)", encoder_variants, [], "Encoder") + +write_variant_method("encode_from_utf8_raw", True, [("src", "&str"), + ("dst", "&mut [u8]"), + ("last", "bool")], "(EncoderResult, usize, usize)", encoder_variants, [], "Encoder") + + +variant_file.write('''} + +pub enum VariantEncoding { + SingleByte(&'static [u16; 128], u16, u8, u8),''') + +for encoding in multi_byte: + variant_file.write("%s,\n" % to_camel_name(encoding["name"])) + +variant_file.write('''} + +impl VariantEncoding { + pub fn new_variant_decoder(&self) -> VariantDecoder { + match *self { + VariantEncoding::SingleByte(table, _, _, _) => SingleByteDecoder::new(table), + VariantEncoding::Utf8 => Utf8Decoder::new(), + VariantEncoding::Gbk | VariantEncoding::Gb18030 => Gb18030Decoder::new(), + VariantEncoding::Big5 => Big5Decoder::new(), + VariantEncoding::EucJp => EucJpDecoder::new(), + VariantEncoding::Iso2022Jp => Iso2022JpDecoder::new(), + VariantEncoding::ShiftJis => ShiftJisDecoder::new(), + VariantEncoding::EucKr => EucKrDecoder::new(), + VariantEncoding::Replacement => ReplacementDecoder::new(), + VariantEncoding::UserDefined => UserDefinedDecoder::new(), + VariantEncoding::Utf16Be => Utf16Decoder::new(true), + VariantEncoding::Utf16Le => Utf16Decoder::new(false), + } + } + + pub fn new_encoder(&self, encoding: &'static Encoding) -> Encoder { + match *self { + VariantEncoding::SingleByte(table, run_bmp_offset, run_byte_offset, run_length) => SingleByteEncoder::new(encoding, table, run_bmp_offset, run_byte_offset, run_length), + VariantEncoding::Utf8 => Utf8Encoder::new(encoding), + VariantEncoding::Gbk => Gb18030Encoder::new(encoding, false), + VariantEncoding::Gb18030 => Gb18030Encoder::new(encoding, true), + VariantEncoding::Big5 => Big5Encoder::new(encoding), + VariantEncoding::EucJp => EucJpEncoder::new(encoding), + VariantEncoding::Iso2022Jp => Iso2022JpEncoder::new(encoding), + VariantEncoding::ShiftJis => ShiftJisEncoder::new(encoding), + VariantEncoding::EucKr => EucKrEncoder::new(encoding), + VariantEncoding::UserDefined => UserDefinedEncoder::new(encoding), + VariantEncoding::Utf16Be | VariantEncoding::Replacement | + VariantEncoding::Utf16Le => unreachable!(), + } + } + + pub fn is_single_byte(&self) -> bool { + match *self { + VariantEncoding::SingleByte(_, _, _, _) | VariantEncoding::UserDefined => true, + _ => false, + } + } +} +''') + +variant_file.close() + +(ffi_rs_begin, ffi_rs_end) = read_non_generated("../encoding_c/src/lib.rs") + +ffi_file = open("../encoding_c/src/lib.rs", "w") + +ffi_file.write(ffi_rs_begin) +ffi_file.write(""" +// Instead, please regenerate using generate-encoding-data.py + +/// The minimum length of buffers that may be passed to `encoding_name()`. +pub const ENCODING_NAME_MAX_LENGTH: usize = %d; // %s + +""" % (longest_name_length, longest_name)) + +for name in preferred: + ffi_file.write('''/// The %s encoding. +#[no_mangle] +pub static %s_ENCODING: ConstEncoding = ConstEncoding(&%s_INIT); + +''' % (to_dom_name(name), to_constant_name(name), to_constant_name(name))) + +ffi_file.write(ffi_rs_end) +ffi_file.close() + +(single_byte_rs_begin, single_byte_rs_end) = read_non_generated("src/single_byte.rs") + +single_byte_file = open("src/single_byte.rs", "w") + +single_byte_file.write(single_byte_rs_begin) +single_byte_file.write(""" +// Instead, please regenerate using generate-encoding-data.py + + #[test] + fn test_single_byte_decode() {""") + +idx = 0 # for Miri, return after 2nd test +for name in preferred: + if name == u"ISO-8859-8-I": + continue; + if is_single_byte(name): + single_byte_file.write(""" + decode_single_byte(%s, &data::SINGLE_BYTE_DATA.%s);""" % (to_constant_name(name), to_snake_name(name))) + idx += 1 + if idx == 2: + single_byte_file.write(""" + if cfg!(miri) { + // Miri is too slow + return; + }""") + +single_byte_file.write(""" + } + + #[test] + fn test_single_byte_encode() {""") + + +idx = 0 # for Miri, return after 2nd test +for name in preferred: + if name == u"ISO-8859-8-I": + continue; + if is_single_byte(name): + single_byte_file.write(""" + encode_single_byte(%s, &data::SINGLE_BYTE_DATA.%s);""" % (to_constant_name(name), to_snake_name(name))) + idx += 1 + if idx == 2: + single_byte_file.write(""" + if cfg!(miri) { + // Miri is too slow + return; + }""") + + +single_byte_file.write(""" + } +""") + +single_byte_file.write(single_byte_rs_end) +single_byte_file.close() + +static_file = open("../encoding_c/include/encoding_rs_statics.h", "w") + +static_file.write("""// Copyright Mozilla Foundation. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// THIS IS A GENERATED FILE. PLEASE DO NOT EDIT. +// Instead, please regenerate using generate-encoding-data.py + +// This file is not meant to be included directly. Instead, encoding_rs.h +// includes this file. + +#ifndef encoding_rs_statics_h_ +#define encoding_rs_statics_h_ + +#ifndef ENCODING_RS_ENCODING +#define ENCODING_RS_ENCODING Encoding +#ifndef __cplusplus +typedef struct Encoding_ Encoding; +#endif +#endif + +#ifndef ENCODING_RS_NOT_NULL_CONST_ENCODING_PTR +#define ENCODING_RS_NOT_NULL_CONST_ENCODING_PTR const ENCODING_RS_ENCODING* +#endif + +#ifndef ENCODING_RS_ENCODER +#define ENCODING_RS_ENCODER Encoder +#ifndef __cplusplus +typedef struct Encoder_ Encoder; +#endif +#endif + +#ifndef ENCODING_RS_DECODER +#define ENCODING_RS_DECODER Decoder +#ifndef __cplusplus +typedef struct Decoder_ Decoder; +#endif +#endif + +#define INPUT_EMPTY 0 + +#define OUTPUT_FULL 0xFFFFFFFF + +// %s +#define ENCODING_NAME_MAX_LENGTH %d + +""" % (longest_name, longest_name_length)) + +for name in preferred: + static_file.write('''/// The %s encoding. +extern ENCODING_RS_NOT_NULL_CONST_ENCODING_PTR const %s_ENCODING; + +''' % (to_dom_name(name), to_constant_name(name))) + +static_file.write("""#endif // encoding_rs_statics_h_ +""") +static_file.close() + +(utf_8_rs_begin, utf_8_rs_end) = read_non_generated("src/utf_8.rs") + +utf_8_file = open("src/utf_8.rs", "w") + +utf_8_file.write(utf_8_rs_begin) +utf_8_file.write(""" +// Instead, please regenerate using generate-encoding-data.py + +pub static UTF8_DATA: Utf8Data = Utf8Data { + table: [ +""") + +for i in range(256): + combined = (1 << 2) # invalid lead + if i < 0x80 or i > 0xBF: + combined |= (1 << 3) # normal trail + if i < 0xA0 or i > 0xBF: + combined |= (1 << 4) # three-byte special lower bound + if i < 0x80 or i > 0x9F: + combined |= (1 << 5) # three-byte special upper bound + if i < 0x90 or i > 0xBF: + combined |= (1 << 6) # four-byte special lower bound + if i < 0x80 or i > 0x8F: + combined |= (1 << 7) # four-byte special upper bound + utf_8_file.write("%d," % combined) + +for i in range(128, 256): + lane = (1 << 2) # invalid lead + if i >= 0xC2 and i <= 0xDF: + lane = (1 << 3) # normal trail + elif i == 0xE0: + lane = (1 << 4) # three-byte special lower bound + elif i >= 0xE1 and i <= 0xEC: + lane = (1 << 3) # normal trail + elif i == 0xED: + lane = (1 << 5) # three-byte special upper bound + elif i >= 0xEE and i <= 0xEF: + lane = (1 << 3) # normal trail + elif i == 0xF0: + lane = (1 << 6) # four-byte special lower bound + elif i >= 0xF1 and i <= 0xF3: + lane = (1 << 3) # normal trail + elif i == 0xF4: + lane = (1 << 7) # four-byte special upper bound + utf_8_file.write("%d," % lane) + +utf_8_file.write(""" + ], +}; + +""") + +utf_8_file.write(utf_8_rs_end) +utf_8_file.close() + +# Unit tests + +TEST_HEADER = '''Any copyright to the test code below this comment is dedicated to the +Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ + +This is a generated file. Please do not edit. +Instead, please regenerate using generate-encoding-data.py +''' + +index = indexes["jis0208"] + +jis0208_in_file = open("src/test_data/jis0208_in.txt", "w") +jis0208_in_file.write(TEST_HEADER) +for pointer in range(0, 94 * 94): + (lead, trail) = divmod(pointer, 94) + lead += 0xA1 + trail += 0xA1 + jis0208_in_file.write("%s%s\n" % (chr(lead), chr(trail))) +jis0208_in_file.close() + +jis0208_in_ref_file = open("src/test_data/jis0208_in_ref.txt", "w") +jis0208_in_ref_file.write(TEST_HEADER) +for pointer in range(0, 94 * 94): + code_point = index[pointer] + if code_point: + jis0208_in_ref_file.write((u"%s\n" % unichr(code_point)).encode("utf-8")) + else: + jis0208_in_ref_file.write(u"\uFFFD\n".encode("utf-8")) +jis0208_in_ref_file.close() + +jis0208_out_file = open("src/test_data/jis0208_out.txt", "w") +jis0208_out_ref_file = open("src/test_data/jis0208_out_ref.txt", "w") +jis0208_out_file.write(TEST_HEADER) +jis0208_out_ref_file.write(TEST_HEADER) +for pointer in range(0, 94 * 94): + code_point = index[pointer] + if code_point: + revised_pointer = pointer + if revised_pointer == 8644 or (revised_pointer >= 1207 and revised_pointer < 1220): + revised_pointer = index.index(code_point) + (lead, trail) = divmod(revised_pointer, 94) + lead += 0xA1 + trail += 0xA1 + jis0208_out_ref_file.write("%s%s\n" % (chr(lead), chr(trail))) + jis0208_out_file.write((u"%s\n" % unichr(code_point)).encode("utf-8")) +jis0208_out_file.close() +jis0208_out_ref_file.close() + +shift_jis_in_file = open("src/test_data/shift_jis_in.txt", "w") +shift_jis_in_file.write(TEST_HEADER) +for pointer in range(0, len(index)): + (lead, trail) = divmod(pointer, 188) + lead += 0x81 if lead < 0x1F else 0xC1 + trail += 0x40 if trail < 0x3F else 0x41 + shift_jis_in_file.write("%s%s\n" % (chr(lead), chr(trail))) +shift_jis_in_file.close() + +shift_jis_in_ref_file = open("src/test_data/shift_jis_in_ref.txt", "w") +shift_jis_in_ref_file.write(TEST_HEADER) +for pointer in range(0, len(index)): + code_point = 0xE000 - 8836 + pointer if pointer >= 8836 and pointer <= 10715 else index[pointer] + if code_point: + shift_jis_in_ref_file.write((u"%s\n" % unichr(code_point)).encode("utf-8")) + else: + trail = pointer % 188 + trail += 0x40 if trail < 0x3F else 0x41 + if trail < 0x80: + shift_jis_in_ref_file.write((u"\uFFFD%s\n" % unichr(trail)).encode("utf-8")) + else: + shift_jis_in_ref_file.write(u"\uFFFD\n".encode("utf-8")) +shift_jis_in_ref_file.close() + +shift_jis_out_file = open("src/test_data/shift_jis_out.txt", "w") +shift_jis_out_ref_file = open("src/test_data/shift_jis_out_ref.txt", "w") +shift_jis_out_file.write(TEST_HEADER) +shift_jis_out_ref_file.write(TEST_HEADER) +for pointer in range(0, 8272): + code_point = index[pointer] + if code_point: + revised_pointer = pointer + if revised_pointer >= 1207 and revised_pointer < 1220: + revised_pointer = index.index(code_point) + (lead, trail) = divmod(revised_pointer, 188) + lead += 0x81 if lead < 0x1F else 0xC1 + trail += 0x40 if trail < 0x3F else 0x41 + shift_jis_out_ref_file.write("%s%s\n" % (chr(lead), chr(trail))) + shift_jis_out_file.write((u"%s\n" % unichr(code_point)).encode("utf-8")) +for pointer in range(8836, len(index)): + code_point = index[pointer] + if code_point: + revised_pointer = index.index(code_point) + if revised_pointer >= 8272 and revised_pointer < 8836: + revised_pointer = pointer + (lead, trail) = divmod(revised_pointer, 188) + lead += 0x81 if lead < 0x1F else 0xC1 + trail += 0x40 if trail < 0x3F else 0x41 + shift_jis_out_ref_file.write("%s%s\n" % (chr(lead), chr(trail))) + shift_jis_out_file.write((u"%s\n" % unichr(code_point)).encode("utf-8")) +shift_jis_out_file.close() +shift_jis_out_ref_file.close() + +iso_2022_jp_in_file = open("src/test_data/iso_2022_jp_in.txt", "w") +iso_2022_jp_in_file.write(TEST_HEADER) +for pointer in range(0, 94 * 94): + (lead, trail) = divmod(pointer, 94) + lead += 0x21 + trail += 0x21 + iso_2022_jp_in_file.write("\x1B$B%s%s\x1B(B\n" % (chr(lead), chr(trail))) +iso_2022_jp_in_file.close() + +iso_2022_jp_in_ref_file = open("src/test_data/iso_2022_jp_in_ref.txt", "w") +iso_2022_jp_in_ref_file.write(TEST_HEADER) +for pointer in range(0, 94 * 94): + code_point = index[pointer] + if code_point: + iso_2022_jp_in_ref_file.write((u"%s\n" % unichr(code_point)).encode("utf-8")) + else: + iso_2022_jp_in_ref_file.write(u"\uFFFD\n".encode("utf-8")) +iso_2022_jp_in_ref_file.close() + +iso_2022_jp_out_file = open("src/test_data/iso_2022_jp_out.txt", "w") +iso_2022_jp_out_ref_file = open("src/test_data/iso_2022_jp_out_ref.txt", "w") +iso_2022_jp_out_file.write(TEST_HEADER) +iso_2022_jp_out_ref_file.write(TEST_HEADER) +for pointer in range(0, 94 * 94): + code_point = index[pointer] + if code_point: + revised_pointer = pointer + if revised_pointer == 8644 or (revised_pointer >= 1207 and revised_pointer < 1220): + revised_pointer = index.index(code_point) + (lead, trail) = divmod(revised_pointer, 94) + lead += 0x21 + trail += 0x21 + iso_2022_jp_out_ref_file.write("\x1B$B%s%s\x1B(B\n" % (chr(lead), chr(trail))) + iso_2022_jp_out_file.write((u"%s\n" % unichr(code_point)).encode("utf-8")) +for i in xrange(len(half_width_index)): + code_point = i + 0xFF61 + normalized_code_point = half_width_index[i] + pointer = index.index(normalized_code_point) + (lead, trail) = divmod(pointer, 94) + lead += 0x21 + trail += 0x21 + iso_2022_jp_out_ref_file.write("\x1B$B%s%s\x1B(B\n" % (chr(lead), chr(trail))) + iso_2022_jp_out_file.write((u"%s\n" % unichr(code_point)).encode("utf-8")) +iso_2022_jp_out_file.close() +iso_2022_jp_out_ref_file.close() + +index = indexes["euc-kr"] + +euc_kr_in_file = open("src/test_data/euc_kr_in.txt", "w") +euc_kr_in_file.write(TEST_HEADER) +for pointer in range(0, len(index)): + (lead, trail) = divmod(pointer, 190) + lead += 0x81 + trail += 0x41 + euc_kr_in_file.write("%s%s\n" % (chr(lead), chr(trail))) +euc_kr_in_file.close() + +euc_kr_in_ref_file = open("src/test_data/euc_kr_in_ref.txt", "w") +euc_kr_in_ref_file.write(TEST_HEADER) +for pointer in range(0, len(index)): + code_point = index[pointer] + if code_point: + euc_kr_in_ref_file.write((u"%s\n" % unichr(code_point)).encode("utf-8")) + else: + trail = pointer % 190 + trail += 0x41 + if trail < 0x80: + euc_kr_in_ref_file.write((u"\uFFFD%s\n" % unichr(trail)).encode("utf-8")) + else: + euc_kr_in_ref_file.write(u"\uFFFD\n".encode("utf-8")) +euc_kr_in_ref_file.close() + +euc_kr_out_file = open("src/test_data/euc_kr_out.txt", "w") +euc_kr_out_ref_file = open("src/test_data/euc_kr_out_ref.txt", "w") +euc_kr_out_file.write(TEST_HEADER) +euc_kr_out_ref_file.write(TEST_HEADER) +for pointer in range(0, len(index)): + code_point = index[pointer] + if code_point: + (lead, trail) = divmod(pointer, 190) + lead += 0x81 + trail += 0x41 + euc_kr_out_ref_file.write("%s%s\n" % (chr(lead), chr(trail))) + euc_kr_out_file.write((u"%s\n" % unichr(code_point)).encode("utf-8")) +euc_kr_out_file.close() +euc_kr_out_ref_file.close() + +index = indexes["gb18030"] + +gb18030_in_file = open("src/test_data/gb18030_in.txt", "w") +gb18030_in_file.write(TEST_HEADER) +for pointer in range(0, len(index)): + (lead, trail) = divmod(pointer, 190) + lead += 0x81 + trail += 0x40 if trail < 0x3F else 0x41 + gb18030_in_file.write("%s%s\n" % (chr(lead), chr(trail))) +gb18030_in_file.close() + +gb18030_in_ref_file = open("src/test_data/gb18030_in_ref.txt", "w") +gb18030_in_ref_file.write(TEST_HEADER) +for pointer in range(0, len(index)): + code_point = index[pointer] + if code_point: + gb18030_in_ref_file.write((u"%s\n" % unichr(code_point)).encode("utf-8")) + else: + trail = pointer % 190 + trail += 0x40 if trail < 0x3F else 0x41 + if trail < 0x80: + gb18030_in_ref_file.write((u"\uFFFD%s\n" % unichr(trail)).encode("utf-8")) + else: + gb18030_in_ref_file.write(u"\uFFFD\n".encode("utf-8")) +gb18030_in_ref_file.close() + +gb18030_out_file = open("src/test_data/gb18030_out.txt", "w") +gb18030_out_ref_file = open("src/test_data/gb18030_out_ref.txt", "w") +gb18030_out_file.write(TEST_HEADER) +gb18030_out_ref_file.write(TEST_HEADER) +for pointer in range(0, len(index)): + if pointer == 6555: + continue + code_point = index[pointer] + if code_point: + (lead, trail) = divmod(pointer, 190) + lead += 0x81 + trail += 0x40 if trail < 0x3F else 0x41 + gb18030_out_ref_file.write("%s%s\n" % (chr(lead), chr(trail))) + gb18030_out_file.write((u"%s\n" % unichr(code_point)).encode("utf-8")) +gb18030_out_file.close() +gb18030_out_ref_file.close() + +index = indexes["big5"] + +big5_in_file = open("src/test_data/big5_in.txt", "w") +big5_in_file.write(TEST_HEADER) +for pointer in range(0, len(index)): + (lead, trail) = divmod(pointer, 157) + lead += 0x81 + trail += 0x40 if trail < 0x3F else 0x62 + big5_in_file.write("%s%s\n" % (chr(lead), chr(trail))) +big5_in_file.close() + +big5_two_characters = { + 1133: u"\u00CA\u0304", + 1135: u"\u00CA\u030C", + 1164: u"\u00EA\u0304", + 1166: u"\u00EA\u030C", +} + +big5_in_ref_file = open("src/test_data/big5_in_ref.txt", "w") +big5_in_ref_file.write(TEST_HEADER) +for pointer in range(0, len(index)): + if pointer in big5_two_characters.keys(): + big5_in_ref_file.write((u"%s\n" % big5_two_characters[pointer]).encode("utf-8")) + continue + code_point = index[pointer] + if code_point: + big5_in_ref_file.write((u"%s\n" % unichr(code_point)).encode("utf-8")) + else: + trail = pointer % 157 + trail += 0x40 if trail < 0x3F else 0x62 + if trail < 0x80: + big5_in_ref_file.write((u"\uFFFD%s\n" % unichr(trail)).encode("utf-8")) + else: + big5_in_ref_file.write(u"\uFFFD\n".encode("utf-8")) +big5_in_ref_file.close() + +prefer_last = [ + 0x2550, + 0x255E, + 0x2561, + 0x256A, + 0x5341, + 0x5345, +] + +pointer_for_prefer_last = [] + +for code_point in prefer_last: + # Python lists don't have .rindex() :-( + for i in xrange(len(index) - 1, -1, -1): + candidate = index[i] + if candidate == code_point: + pointer_for_prefer_last.append(i) + break + +big5_out_file = open("src/test_data/big5_out.txt", "w") +big5_out_ref_file = open("src/test_data/big5_out_ref.txt", "w") +big5_out_file.write(TEST_HEADER) +big5_out_ref_file.write(TEST_HEADER) +for pointer in range(((0xA1 - 0x81) * 157), len(index)): + code_point = index[pointer] + if code_point: + if code_point in prefer_last: + if pointer != pointer_for_prefer_last[prefer_last.index(code_point)]: + continue + else: + if pointer != index.index(code_point): + continue + (lead, trail) = divmod(pointer, 157) + lead += 0x81 + trail += 0x40 if trail < 0x3F else 0x62 + big5_out_ref_file.write("%s%s\n" % (chr(lead), chr(trail))) + big5_out_file.write((u"%s\n" % unichr(code_point)).encode("utf-8")) +big5_out_file.close() +big5_out_ref_file.close() + +index = indexes["jis0212"] + +jis0212_in_file = open("src/test_data/jis0212_in.txt", "w") +jis0212_in_file.write(TEST_HEADER) +for pointer in range(0, len(index)): + (lead, trail) = divmod(pointer, 94) + lead += 0xA1 + trail += 0xA1 + jis0212_in_file.write("\x8F%s%s\n" % (chr(lead), chr(trail))) +jis0212_in_file.close() + +jis0212_in_ref_file = open("src/test_data/jis0212_in_ref.txt", "w") +jis0212_in_ref_file.write(TEST_HEADER) +for pointer in range(0, len(index)): + code_point = index[pointer] + if code_point: + jis0212_in_ref_file.write((u"%s\n" % unichr(code_point)).encode("utf-8")) + else: + jis0212_in_ref_file.write(u"\uFFFD\n".encode("utf-8")) +jis0212_in_ref_file.close() + +(codepage_begin, codepage_end) = read_non_generated("../codepage/src/lib.rs") + +codepage_file = open("../codepage/src/lib.rs", "w") + +codepage_file.write(codepage_begin) +codepage_file.write(""" +// Instead, please regenerate using generate-encoding-data.py + +/// Supported code page numbers in estimated order of usage frequency +static CODE_PAGES: [u16; %d] = [ +""" % len(code_pages)) + +for code_page in code_pages: + codepage_file.write(" %d,\n" % code_page) + +codepage_file.write("""]; + +/// Encodings corresponding to the code page numbers in the same order +static ENCODINGS: [&'static Encoding; %d] = [ +""" % len(code_pages)) + +for code_page in code_pages: + name = encodings_by_code_page[code_page] + codepage_file.write(" &%s_INIT,\n" % to_constant_name(name)) + +codepage_file.write("""]; + +""") + +codepage_file.write(codepage_end) +codepage_file.close() + +(codepage_test_begin, codepage_test_end) = read_non_generated("../codepage/src/tests.rs") + +codepage_test_file = open("../codepage/src/tests.rs", "w") + +codepage_test_file.write(codepage_test_begin) +codepage_test_file.write(""" +// Instead, please regenerate using generate-encoding-data.py + +#[test] +fn test_to_encoding() { + assert_eq!(to_encoding(0), None); + +""") + +for code_page in code_pages: + codepage_test_file.write(" assert_eq!(to_encoding(%d), Some(%s));\n" % (code_page, to_constant_name(encodings_by_code_page[code_page]))) + +codepage_test_file.write("""} + +#[test] +fn test_from_encoding() { +""") + +for name in preferred: + if code_pages_by_encoding.has_key(name): + codepage_test_file.write(" assert_eq!(from_encoding(%s), Some(%d));\n" % (to_constant_name(name), code_pages_by_encoding[name])) + else: + codepage_test_file.write(" assert_eq!(from_encoding(%s), None);\n" % to_constant_name(name)) + +codepage_test_file.write("""} +""") + +codepage_test_file.write(codepage_test_end) +codepage_test_file.close() + +subprocess.call(["cargo", "fmt"]) |