mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-12 02:07:33 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
1
third_party/rust/idna/.cargo-checksum.json
vendored
Normal file
1
third_party/rust/idna/.cargo-checksum.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","Cargo.toml":"d0aad6852ec947597bab20cd85ad15e014044cb7717c9a91f4b8da0081b7134d","src/IdnaMappingTable.txt":"5e9f5929130b713e698162ac5b60a99ccfb831606686b1c50777cd920b55dee2","src/lib.rs":"b8e85707a40e8472d2e90849a1e0e24a7442f3c4614a57a60125f87d11e985a4","src/make_uts46_mapping_table.py":"36fa77c443672f15872d60438f96b7302eae28ec506b60a892579debc79b8e39","src/punycode.rs":"df883ec00b35cab38f96992667eef0767d8587746bfdab0613a03e4c49a26c16","src/uts46.rs":"c879570c511f210565ac0dbce8a212be6503e62cd7ed698830804bb7729e98de","src/uts46_mapping_table.rs":"daa59e4b6399a738f73967b222b7dce6c9706a471d306330d77380c89089fa24","tests/IdnaTest.txt":"12e7e150b04a7a2cb1f9b72222174844342218807126e9dbc53069505a5f6000","tests/punycode.rs":"2f4086411c00b0641377afe81071e51a695110a0cce474287557738c07f74322","tests/punycode_tests.json":"3d4ac0cf25984c37b9ce197f5df680a0136f728fb8ec82bc76624e42139eb3a8","tests/tests.rs":"bb92e129dc5e17e9a86ec6062dd7b3f4c905c4af69e773d7c70efea177654c7b","tests/uts46.rs":"be9f928c60b88a8e277ddfb1769f09a8cd273e2e120e8450fb9b34f4dc852b37"},"package":"1053236e00ce4f668aeca4a769a09b3bf5a682d802abd6f3cb39374f6b162c11"}
|
||||
0
third_party/rust/idna/.cargo-ok
vendored
Normal file
0
third_party/rust/idna/.cargo-ok
vendored
Normal file
24
third_party/rust/idna/Cargo.toml
vendored
Normal file
24
third_party/rust/idna/Cargo.toml
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
[package]
|
||||
name = "idna"
|
||||
version = "0.1.0"
|
||||
authors = ["Simon Sapin <simon.sapin@exyr.org>"]
|
||||
description = "IDNA (Internationalizing Domain Names in Applications) and Punycode."
|
||||
repository = "https://github.com/servo/rust-url/"
|
||||
license = "MIT/Apache-2.0"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
test = false
|
||||
|
||||
[[test]]
|
||||
name = "tests"
|
||||
harness = false
|
||||
|
||||
[dev-dependencies]
|
||||
rustc-test = "0.1"
|
||||
rustc-serialize = "0.3"
|
||||
|
||||
[dependencies]
|
||||
unicode-bidi = "0.2.3"
|
||||
unicode-normalization = "0.1.2"
|
||||
matches = "0.1"
|
||||
8190
third_party/rust/idna/src/IdnaMappingTable.txt
vendored
Normal file
8190
third_party/rust/idna/src/IdnaMappingTable.txt
vendored
Normal file
File diff suppressed because it is too large
Load diff
73
third_party/rust/idna/src/lib.rs
vendored
Normal file
73
third_party/rust/idna/src/lib.rs
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
// Copyright 2016 Simon Sapin.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! This Rust crate implements IDNA
|
||||
//! [per the WHATWG URL Standard](https://url.spec.whatwg.org/#idna).
|
||||
//!
|
||||
//! It also exposes the underlying algorithms from [*Unicode IDNA Compatibility Processing*
|
||||
//! (Unicode Technical Standard #46)](http://www.unicode.org/reports/tr46/)
|
||||
//! and [Punycode (RFC 3492)](https://tools.ietf.org/html/rfc3492).
|
||||
//!
|
||||
//! Quoting from [UTS #46’s introduction](http://www.unicode.org/reports/tr46/#Introduction):
|
||||
//!
|
||||
//! > Initially, domain names were restricted to ASCII characters.
|
||||
//! > A system was introduced in 2003 for internationalized domain names (IDN).
|
||||
//! > This system is called Internationalizing Domain Names for Applications,
|
||||
//! > or IDNA2003 for short.
|
||||
//! > This mechanism supports IDNs by means of a client software transformation
|
||||
//! > into a format known as Punycode.
|
||||
//! > A revision of IDNA was approved in 2010 (IDNA2008).
|
||||
//! > This revision has a number of incompatibilities with IDNA2003.
|
||||
//! >
|
||||
//! > The incompatibilities force implementers of client software,
|
||||
//! > such as browsers and emailers,
|
||||
//! > to face difficult choices during the transition period
|
||||
//! > as registries shift from IDNA2003 to IDNA2008.
|
||||
//! > This document specifies a mechanism
|
||||
//! > that minimizes the impact of this transition for client software,
|
||||
//! > allowing client software to access domains that are valid under either system.
|
||||
|
||||
#[macro_use] extern crate matches;
|
||||
extern crate unicode_bidi;
|
||||
extern crate unicode_normalization;
|
||||
|
||||
pub mod punycode;
|
||||
pub mod uts46;
|
||||
|
||||
/// The [domain to ASCII](https://url.spec.whatwg.org/#concept-domain-to-ascii) algorithm.
|
||||
///
|
||||
/// Return the ASCII representation a domain name,
|
||||
/// normalizing characters (upper-case to lower-case and other kinds of equivalence)
|
||||
/// and using Punycode as necessary.
|
||||
///
|
||||
/// This process may fail.
|
||||
pub fn domain_to_ascii(domain: &str) -> Result<String, uts46::Errors> {
|
||||
uts46::to_ascii(domain, uts46::Flags {
|
||||
use_std3_ascii_rules: false,
|
||||
transitional_processing: true, // XXX: switch when Firefox does
|
||||
verify_dns_length: false,
|
||||
})
|
||||
}
|
||||
|
||||
/// The [domain to Unicode](https://url.spec.whatwg.org/#concept-domain-to-unicode) algorithm.
|
||||
///
|
||||
/// Return the Unicode representation of a domain name,
|
||||
/// normalizing characters (upper-case to lower-case and other kinds of equivalence)
|
||||
/// and decoding Punycode as necessary.
|
||||
///
|
||||
/// This may indicate [syntax violations](https://url.spec.whatwg.org/#syntax-violation)
|
||||
/// but always returns a string for the mapped domain.
|
||||
pub fn domain_to_unicode(domain: &str) -> (String, Result<(), uts46::Errors>) {
|
||||
uts46::to_unicode(domain, uts46::Flags {
|
||||
use_std3_ascii_rules: false,
|
||||
|
||||
// Unused:
|
||||
transitional_processing: true,
|
||||
verify_dns_length: false,
|
||||
})
|
||||
}
|
||||
56
third_party/rust/idna/src/make_uts46_mapping_table.py
vendored
Normal file
56
third_party/rust/idna/src/make_uts46_mapping_table.py
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# Copyright 2013-2014 Valentin Gosu.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
# option. This file may not be copied, modified, or distributed
|
||||
# except according to those terms.
|
||||
|
||||
# Run as: python make_uts46_mapping_table.py IdnaMappingTable.txt > uts46_mapping_table.rs
|
||||
# You can get the latest idna table from
|
||||
# http://www.unicode.org/Public/idna/latest/IdnaMappingTable.txt
|
||||
|
||||
print('''\
|
||||
// Copyright 2013-2014 Valentin Gosu.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Generated by make_idna_table.py
|
||||
|
||||
static TABLE: &'static [Range] = &[
|
||||
''')
|
||||
|
||||
txt = open("IdnaMappingTable.txt")
|
||||
|
||||
def char(s):
|
||||
return (unichr(int(s, 16))
|
||||
.encode('utf8')
|
||||
.replace('\\', '\\\\')
|
||||
.replace('"', '\\"')
|
||||
.replace('\0', '\\0'))
|
||||
|
||||
for line in txt:
|
||||
# remove comments
|
||||
line, _, _ = line.partition('#')
|
||||
# skip empty lines
|
||||
if len(line.strip()) == 0:
|
||||
continue
|
||||
fields = line.split(';')
|
||||
if fields[0].strip() == 'D800..DFFF':
|
||||
continue # Surrogates don't occur in Rust strings.
|
||||
first, _, last = fields[0].strip().partition('..')
|
||||
if not last:
|
||||
last = first
|
||||
mapping = fields[1].strip().replace('_', ' ').title().replace(' ', '')
|
||||
if len(fields) > 2:
|
||||
if fields[2].strip():
|
||||
mapping += '("%s")' % ''.join(char(c) for c in fields[2].strip().split(' '))
|
||||
elif mapping == "Deviation":
|
||||
mapping += '("")'
|
||||
print(" Range { from: '%s', to: '%s', mapping: %s }," % (char(first), char(last), mapping))
|
||||
|
||||
print("];")
|
||||
213
third_party/rust/idna/src/punycode.rs
vendored
Normal file
213
third_party/rust/idna/src/punycode.rs
vendored
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
// Copyright 2013 Simon Sapin.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Punycode ([RFC 3492](http://tools.ietf.org/html/rfc3492)) implementation.
|
||||
//!
|
||||
//! Since Punycode fundamentally works on unicode code points,
|
||||
//! `encode` and `decode` take and return slices and vectors of `char`.
|
||||
//! `encode_str` and `decode_to_string` provide convenience wrappers
|
||||
//! that convert from and to Rust’s UTF-8 based `str` and `String` types.
|
||||
|
||||
use std::u32;
|
||||
use std::char;
|
||||
use std::ascii::AsciiExt;
|
||||
|
||||
// Bootstring parameters for Punycode
|
||||
static BASE: u32 = 36;
|
||||
static T_MIN: u32 = 1;
|
||||
static T_MAX: u32 = 26;
|
||||
static SKEW: u32 = 38;
|
||||
static DAMP: u32 = 700;
|
||||
static INITIAL_BIAS: u32 = 72;
|
||||
static INITIAL_N: u32 = 0x80;
|
||||
static DELIMITER: char = '-';
|
||||
|
||||
|
||||
#[inline]
|
||||
fn adapt(mut delta: u32, num_points: u32, first_time: bool) -> u32 {
|
||||
delta /= if first_time { DAMP } else { 2 };
|
||||
delta += delta / num_points;
|
||||
let mut k = 0;
|
||||
while delta > ((BASE - T_MIN) * T_MAX) / 2 {
|
||||
delta /= BASE - T_MIN;
|
||||
k += BASE;
|
||||
}
|
||||
k + (((BASE - T_MIN + 1) * delta) / (delta + SKEW))
|
||||
}
|
||||
|
||||
|
||||
/// Convert Punycode to an Unicode `String`.
|
||||
///
|
||||
/// This is a convenience wrapper around `decode`.
|
||||
#[inline]
|
||||
pub fn decode_to_string(input: &str) -> Option<String> {
|
||||
decode(input).map(|chars| chars.into_iter().collect())
|
||||
}
|
||||
|
||||
|
||||
/// Convert Punycode to Unicode.
|
||||
///
|
||||
/// Return None on malformed input or overflow.
|
||||
/// Overflow can only happen on inputs that take more than
|
||||
/// 63 encoded bytes, the DNS limit on domain name labels.
|
||||
pub fn decode(input: &str) -> Option<Vec<char>> {
|
||||
// Handle "basic" (ASCII) code points.
|
||||
// They are encoded as-is before the last delimiter, if any.
|
||||
let (mut output, input) = match input.rfind(DELIMITER) {
|
||||
None => (Vec::new(), input),
|
||||
Some(position) => (
|
||||
input[..position].chars().collect(),
|
||||
if position > 0 { &input[position + 1..] } else { input }
|
||||
)
|
||||
};
|
||||
let mut code_point = INITIAL_N;
|
||||
let mut bias = INITIAL_BIAS;
|
||||
let mut i = 0;
|
||||
let mut iter = input.bytes();
|
||||
loop {
|
||||
let previous_i = i;
|
||||
let mut weight = 1;
|
||||
let mut k = BASE;
|
||||
let mut byte = match iter.next() {
|
||||
None => break,
|
||||
Some(byte) => byte,
|
||||
};
|
||||
// Decode a generalized variable-length integer into delta,
|
||||
// which gets added to i.
|
||||
loop {
|
||||
let digit = match byte {
|
||||
byte @ b'0' ... b'9' => byte - b'0' + 26,
|
||||
byte @ b'A' ... b'Z' => byte - b'A',
|
||||
byte @ b'a' ... b'z' => byte - b'a',
|
||||
_ => return None
|
||||
} as u32;
|
||||
if digit > (u32::MAX - i) / weight {
|
||||
return None // Overflow
|
||||
}
|
||||
i += digit * weight;
|
||||
let t = if k <= bias { T_MIN }
|
||||
else if k >= bias + T_MAX { T_MAX }
|
||||
else { k - bias };
|
||||
if digit < t {
|
||||
break
|
||||
}
|
||||
if weight > u32::MAX / (BASE - t) {
|
||||
return None // Overflow
|
||||
}
|
||||
weight *= BASE - t;
|
||||
k += BASE;
|
||||
byte = match iter.next() {
|
||||
None => return None, // End of input before the end of this delta
|
||||
Some(byte) => byte,
|
||||
};
|
||||
}
|
||||
let length = output.len() as u32;
|
||||
bias = adapt(i - previous_i, length + 1, previous_i == 0);
|
||||
if i / (length + 1) > u32::MAX - code_point {
|
||||
return None // Overflow
|
||||
}
|
||||
// i was supposed to wrap around from length+1 to 0,
|
||||
// incrementing code_point each time.
|
||||
code_point += i / (length + 1);
|
||||
i %= length + 1;
|
||||
let c = match char::from_u32(code_point) {
|
||||
Some(c) => c,
|
||||
None => return None
|
||||
};
|
||||
output.insert(i as usize, c);
|
||||
i += 1;
|
||||
}
|
||||
Some(output)
|
||||
}
|
||||
|
||||
|
||||
/// Convert an Unicode `str` to Punycode.
|
||||
///
|
||||
/// This is a convenience wrapper around `encode`.
|
||||
#[inline]
|
||||
pub fn encode_str(input: &str) -> Option<String> {
|
||||
encode(&input.chars().collect::<Vec<char>>())
|
||||
}
|
||||
|
||||
|
||||
/// Convert Unicode to Punycode.
|
||||
///
|
||||
/// Return None on overflow, which can only happen on inputs that would take more than
|
||||
/// 63 encoded bytes, the DNS limit on domain name labels.
|
||||
pub fn encode(input: &[char]) -> Option<String> {
|
||||
// Handle "basic" (ASCII) code points. They are encoded as-is.
|
||||
let output_bytes = input.iter().filter_map(|&c|
|
||||
if c.is_ascii() { Some(c as u8) } else { None }
|
||||
).collect();
|
||||
let mut output = unsafe { String::from_utf8_unchecked(output_bytes) };
|
||||
let basic_length = output.len() as u32;
|
||||
if basic_length > 0 {
|
||||
output.push_str("-")
|
||||
}
|
||||
let mut code_point = INITIAL_N;
|
||||
let mut delta = 0;
|
||||
let mut bias = INITIAL_BIAS;
|
||||
let mut processed = basic_length;
|
||||
let input_length = input.len() as u32;
|
||||
while processed < input_length {
|
||||
// All code points < code_point have been handled already.
|
||||
// Find the next larger one.
|
||||
let min_code_point = input.iter().map(|&c| c as u32)
|
||||
.filter(|&c| c >= code_point).min().unwrap();
|
||||
if min_code_point - code_point > (u32::MAX - delta) / (processed + 1) {
|
||||
return None // Overflow
|
||||
}
|
||||
// Increase delta to advance the decoder’s <code_point,i> state to <min_code_point,0>
|
||||
delta += (min_code_point - code_point) * (processed + 1);
|
||||
code_point = min_code_point;
|
||||
for &c in input {
|
||||
let c = c as u32;
|
||||
if c < code_point {
|
||||
delta += 1;
|
||||
if delta == 0 {
|
||||
return None // Overflow
|
||||
}
|
||||
}
|
||||
if c == code_point {
|
||||
// Represent delta as a generalized variable-length integer:
|
||||
let mut q = delta;
|
||||
let mut k = BASE;
|
||||
loop {
|
||||
let t = if k <= bias { T_MIN }
|
||||
else if k >= bias + T_MAX { T_MAX }
|
||||
else { k - bias };
|
||||
if q < t {
|
||||
break
|
||||
}
|
||||
let value = t + ((q - t) % (BASE - t));
|
||||
value_to_digit(value, &mut output);
|
||||
q = (q - t) / (BASE - t);
|
||||
k += BASE;
|
||||
}
|
||||
value_to_digit(q, &mut output);
|
||||
bias = adapt(delta, processed + 1, processed == basic_length);
|
||||
delta = 0;
|
||||
processed += 1;
|
||||
}
|
||||
}
|
||||
delta += 1;
|
||||
code_point += 1;
|
||||
}
|
||||
Some(output)
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
fn value_to_digit(value: u32, output: &mut String) {
|
||||
let code_point = match value {
|
||||
0 ... 25 => value + 0x61, // a..z
|
||||
26 ... 35 => value - 26 + 0x30, // 0..9
|
||||
_ => panic!()
|
||||
};
|
||||
unsafe { output.as_mut_vec().push(code_point as u8) }
|
||||
}
|
||||
322
third_party/rust/idna/src/uts46.rs
vendored
Normal file
322
third_party/rust/idna/src/uts46.rs
vendored
Normal file
|
|
@ -0,0 +1,322 @@
|
|||
// Copyright 2013-2014 Valentin Gosu.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! [*Unicode IDNA Compatibility Processing*
|
||||
//! (Unicode Technical Standard #46)](http://www.unicode.org/reports/tr46/)
|
||||
|
||||
use self::Mapping::*;
|
||||
use punycode;
|
||||
use std::ascii::AsciiExt;
|
||||
use unicode_normalization::UnicodeNormalization;
|
||||
use unicode_normalization::char::is_combining_mark;
|
||||
use unicode_bidi::{BidiClass, bidi_class};
|
||||
|
||||
include!("uts46_mapping_table.rs");
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Mapping {
|
||||
Valid,
|
||||
Ignored,
|
||||
Mapped(&'static str),
|
||||
Deviation(&'static str),
|
||||
Disallowed,
|
||||
DisallowedStd3Valid,
|
||||
DisallowedStd3Mapped(&'static str),
|
||||
}
|
||||
|
||||
struct Range {
|
||||
from: char,
|
||||
to: char,
|
||||
mapping: Mapping,
|
||||
}
|
||||
|
||||
fn find_char(codepoint: char) -> &'static Mapping {
|
||||
let mut min = 0;
|
||||
let mut max = TABLE.len() - 1;
|
||||
while max > min {
|
||||
let mid = (min + max) >> 1;
|
||||
if codepoint > TABLE[mid].to {
|
||||
min = mid;
|
||||
} else if codepoint < TABLE[mid].from {
|
||||
max = mid;
|
||||
} else {
|
||||
min = mid;
|
||||
max = mid;
|
||||
}
|
||||
}
|
||||
&TABLE[min].mapping
|
||||
}
|
||||
|
||||
fn map_char(codepoint: char, flags: Flags, output: &mut String, errors: &mut Vec<Error>) {
|
||||
match *find_char(codepoint) {
|
||||
Mapping::Valid => output.push(codepoint),
|
||||
Mapping::Ignored => {},
|
||||
Mapping::Mapped(mapping) => output.push_str(mapping),
|
||||
Mapping::Deviation(mapping) => {
|
||||
if flags.transitional_processing {
|
||||
output.push_str(mapping)
|
||||
} else {
|
||||
output.push(codepoint)
|
||||
}
|
||||
}
|
||||
Mapping::Disallowed => {
|
||||
errors.push(Error::DissallowedCharacter);
|
||||
output.push(codepoint);
|
||||
}
|
||||
Mapping::DisallowedStd3Valid => {
|
||||
if flags.use_std3_ascii_rules {
|
||||
errors.push(Error::DissallowedByStd3AsciiRules);
|
||||
}
|
||||
output.push(codepoint)
|
||||
}
|
||||
Mapping::DisallowedStd3Mapped(mapping) => {
|
||||
if flags.use_std3_ascii_rules {
|
||||
errors.push(Error::DissallowedMappedInStd3);
|
||||
}
|
||||
output.push_str(mapping)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// http://tools.ietf.org/html/rfc5893#section-2
|
||||
fn passes_bidi(label: &str, transitional_processing: bool) -> bool {
|
||||
let mut chars = label.chars();
|
||||
let class = match chars.next() {
|
||||
Some(c) => bidi_class(c),
|
||||
None => return true, // empty string
|
||||
};
|
||||
|
||||
if class == BidiClass::L
|
||||
|| (class == BidiClass::ON && transitional_processing) // starts with \u200D
|
||||
|| (class == BidiClass::ES && transitional_processing) // hack: 1.35.+33.49
|
||||
|| class == BidiClass::EN // hack: starts with number 0à.\u05D0
|
||||
{ // LTR
|
||||
// Rule 5
|
||||
loop {
|
||||
match chars.next() {
|
||||
Some(c) => {
|
||||
let c = bidi_class(c);
|
||||
if !matches!(c, BidiClass::L | BidiClass::EN |
|
||||
BidiClass::ES | BidiClass::CS |
|
||||
BidiClass::ET | BidiClass::ON |
|
||||
BidiClass::BN | BidiClass::NSM) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
None => { break; },
|
||||
}
|
||||
}
|
||||
|
||||
// Rule 6
|
||||
let mut rev_chars = label.chars().rev();
|
||||
let mut last = rev_chars.next();
|
||||
loop { // must end in L or EN followed by 0 or more NSM
|
||||
match last {
|
||||
Some(c) if bidi_class(c) == BidiClass::NSM => {
|
||||
last = rev_chars.next();
|
||||
continue;
|
||||
}
|
||||
_ => { break; },
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: does not pass for àˇ.\u05D0
|
||||
// match last {
|
||||
// Some(c) if bidi_class(c) == BidiClass::L
|
||||
// || bidi_class(c) == BidiClass::EN => {},
|
||||
// Some(c) => { return false; },
|
||||
// _ => {}
|
||||
// }
|
||||
|
||||
} else if class == BidiClass::R || class == BidiClass::AL { // RTL
|
||||
let mut found_en = false;
|
||||
let mut found_an = false;
|
||||
|
||||
// Rule 2
|
||||
loop {
|
||||
match chars.next() {
|
||||
Some(c) => {
|
||||
let char_class = bidi_class(c);
|
||||
|
||||
if char_class == BidiClass::EN {
|
||||
found_en = true;
|
||||
}
|
||||
if char_class == BidiClass::AN {
|
||||
found_an = true;
|
||||
}
|
||||
|
||||
if !matches!(char_class, BidiClass::R | BidiClass::AL |
|
||||
BidiClass::AN | BidiClass::EN |
|
||||
BidiClass::ES | BidiClass::CS |
|
||||
BidiClass::ET | BidiClass::ON |
|
||||
BidiClass::BN | BidiClass::NSM) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
None => { break; },
|
||||
}
|
||||
}
|
||||
// Rule 3
|
||||
let mut rev_chars = label.chars().rev();
|
||||
let mut last = rev_chars.next();
|
||||
loop { // must end in L or EN followed by 0 or more NSM
|
||||
match last {
|
||||
Some(c) if bidi_class(c) == BidiClass::NSM => {
|
||||
last = rev_chars.next();
|
||||
continue;
|
||||
}
|
||||
_ => { break; },
|
||||
}
|
||||
}
|
||||
match last {
|
||||
Some(c) if matches!(bidi_class(c), BidiClass::R | BidiClass::AL |
|
||||
BidiClass::EN | BidiClass::AN) => {},
|
||||
_ => { return false; }
|
||||
}
|
||||
|
||||
// Rule 4
|
||||
if found_an && found_en {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Rule 2: Should start with L or R/AL
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// http://www.unicode.org/reports/tr46/#Validity_Criteria
|
||||
fn validate(label: &str, flags: Flags, errors: &mut Vec<Error>) {
|
||||
if label.nfc().ne(label.chars()) {
|
||||
errors.push(Error::ValidityCriteria);
|
||||
}
|
||||
|
||||
// Can not contain '.' since the input is from .split('.')
|
||||
if {
|
||||
let mut chars = label.chars().skip(2);
|
||||
let third = chars.next();
|
||||
let fourth = chars.next();
|
||||
(third, fourth) == (Some('-'), Some('-'))
|
||||
} || label.starts_with("-")
|
||||
|| label.ends_with("-")
|
||||
|| label.chars().next().map_or(false, is_combining_mark)
|
||||
|| label.chars().any(|c| match *find_char(c) {
|
||||
Mapping::Valid => false,
|
||||
Mapping::Deviation(_) => flags.transitional_processing,
|
||||
Mapping::DisallowedStd3Valid => flags.use_std3_ascii_rules,
|
||||
_ => true,
|
||||
})
|
||||
|| !passes_bidi(label, flags.transitional_processing)
|
||||
{
|
||||
errors.push(Error::ValidityCriteria)
|
||||
}
|
||||
}
|
||||
|
||||
/// http://www.unicode.org/reports/tr46/#Processing
|
||||
fn processing(domain: &str, flags: Flags, errors: &mut Vec<Error>) -> String {
|
||||
let mut mapped = String::new();
|
||||
for c in domain.chars() {
|
||||
map_char(c, flags, &mut mapped, errors)
|
||||
}
|
||||
let normalized: String = mapped.nfc().collect();
|
||||
let mut validated = String::new();
|
||||
for label in normalized.split('.') {
|
||||
if validated.len() > 0 {
|
||||
validated.push('.');
|
||||
}
|
||||
if label.starts_with("xn--") {
|
||||
match punycode::decode_to_string(&label["xn--".len()..]) {
|
||||
Some(decoded_label) => {
|
||||
let flags = Flags { transitional_processing: false, ..flags };
|
||||
validate(&decoded_label, flags, errors);
|
||||
validated.push_str(&decoded_label)
|
||||
}
|
||||
None => errors.push(Error::PunycodeError)
|
||||
}
|
||||
} else {
|
||||
validate(label, flags, errors);
|
||||
validated.push_str(label)
|
||||
}
|
||||
}
|
||||
validated
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Flags {
|
||||
pub use_std3_ascii_rules: bool,
|
||||
pub transitional_processing: bool,
|
||||
pub verify_dns_length: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
enum Error {
|
||||
PunycodeError,
|
||||
ValidityCriteria,
|
||||
DissallowedByStd3AsciiRules,
|
||||
DissallowedMappedInStd3,
|
||||
DissallowedCharacter,
|
||||
TooLongForDns,
|
||||
}
|
||||
|
||||
/// Errors recorded during UTS #46 processing.
|
||||
///
|
||||
/// This is opaque for now, only indicating the precense of at least one error.
|
||||
/// More details may be exposed in the future.
|
||||
#[derive(Debug)]
|
||||
pub struct Errors(Vec<Error>);
|
||||
|
||||
/// http://www.unicode.org/reports/tr46/#ToASCII
|
||||
pub fn to_ascii(domain: &str, flags: Flags) -> Result<String, Errors> {
|
||||
let mut errors = Vec::new();
|
||||
let mut result = String::new();
|
||||
for label in processing(domain, flags, &mut errors).split('.') {
|
||||
if result.len() > 0 {
|
||||
result.push('.');
|
||||
}
|
||||
if label.is_ascii() {
|
||||
result.push_str(label);
|
||||
} else {
|
||||
match punycode::encode_str(label) {
|
||||
Some(x) => {
|
||||
result.push_str("xn--");
|
||||
result.push_str(&x);
|
||||
},
|
||||
None => errors.push(Error::PunycodeError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if flags.verify_dns_length {
|
||||
let domain = if result.ends_with(".") { &result[..result.len()-1] } else { &*result };
|
||||
if domain.len() < 1 || domain.len() > 253 ||
|
||||
domain.split('.').any(|label| label.len() < 1 || label.len() > 63) {
|
||||
errors.push(Error::TooLongForDns)
|
||||
}
|
||||
}
|
||||
if errors.is_empty() {
|
||||
Ok(result)
|
||||
} else {
|
||||
Err(Errors(errors))
|
||||
}
|
||||
}
|
||||
|
||||
/// http://www.unicode.org/reports/tr46/#ToUnicode
|
||||
///
|
||||
/// Only `use_std3_ascii_rules` is used in `flags`.
|
||||
pub fn to_unicode(domain: &str, mut flags: Flags) -> (String, Result<(), Errors>) {
|
||||
flags.transitional_processing = false;
|
||||
let mut errors = Vec::new();
|
||||
let domain = processing(domain, flags, &mut errors);
|
||||
let errors = if errors.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Errors(errors))
|
||||
};
|
||||
(domain, errors)
|
||||
}
|
||||
8191
third_party/rust/idna/src/uts46_mapping_table.rs
vendored
Normal file
8191
third_party/rust/idna/src/uts46_mapping_table.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
5097
third_party/rust/idna/tests/IdnaTest.txt
vendored
Normal file
5097
third_party/rust/idna/tests/IdnaTest.txt
vendored
Normal file
File diff suppressed because it is too large
Load diff
65
third_party/rust/idna/tests/punycode.rs
vendored
Normal file
65
third_party/rust/idna/tests/punycode.rs
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright 2013 Simon Sapin.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use idna::punycode::{decode, encode_str};
|
||||
use rustc_serialize::json::{Json, Object};
|
||||
use test::TestFn;
|
||||
|
||||
fn one_test(decoded: &str, encoded: &str) {
|
||||
match decode(encoded) {
|
||||
None => panic!("Decoding {} failed.", encoded),
|
||||
Some(result) => {
|
||||
let result = result.into_iter().collect::<String>();
|
||||
assert!(result == decoded,
|
||||
format!("Incorrect decoding of \"{}\":\n \"{}\"\n!= \"{}\"\n",
|
||||
encoded, result, decoded))
|
||||
}
|
||||
}
|
||||
|
||||
match encode_str(decoded) {
|
||||
None => panic!("Encoding {} failed.", decoded),
|
||||
Some(result) => {
|
||||
assert!(result == encoded,
|
||||
format!("Incorrect encoding of \"{}\":\n \"{}\"\n!= \"{}\"\n",
|
||||
decoded, result, encoded))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_string<'a>(map: &'a Object, key: &str) -> &'a str {
|
||||
match map.get(&key.to_string()) {
|
||||
Some(&Json::String(ref s)) => s,
|
||||
None => "",
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn collect_tests<F: FnMut(String, TestFn)>(add_test: &mut F) {
|
||||
match Json::from_str(include_str!("punycode_tests.json")) {
|
||||
Ok(Json::Array(tests)) => for (i, test) in tests.into_iter().enumerate() {
|
||||
match test {
|
||||
Json::Object(o) => {
|
||||
let test_name = {
|
||||
let desc = get_string(&o, "description");
|
||||
if desc.is_empty() {
|
||||
format!("Punycode {}", i + 1)
|
||||
} else {
|
||||
format!("Punycode {}: {}", i + 1, desc)
|
||||
}
|
||||
};
|
||||
add_test(test_name, TestFn::dyn_test_fn(move || one_test(
|
||||
get_string(&o, "decoded"),
|
||||
get_string(&o, "encoded"),
|
||||
)))
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
},
|
||||
other => panic!("{:?}", other)
|
||||
}
|
||||
}
|
||||
120
third_party/rust/idna/tests/punycode_tests.json
vendored
Normal file
120
third_party/rust/idna/tests/punycode_tests.json
vendored
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
[
|
||||
{
|
||||
"description": "These tests are copied from https://github.com/bestiejs/punycode.js/blob/master/tests/tests.js , used under the MIT license.",
|
||||
"decoded": "",
|
||||
"encoded": ""
|
||||
},
|
||||
{
|
||||
"description": "a single basic code point",
|
||||
"decoded": "Bach",
|
||||
"encoded": "Bach-"
|
||||
},
|
||||
{
|
||||
"description": "a single non-ASCII character",
|
||||
"decoded": "\u00FC",
|
||||
"encoded": "tda"
|
||||
},
|
||||
{
|
||||
"description": "multiple non-ASCII characters",
|
||||
"decoded": "\u00FC\u00EB\u00E4\u00F6\u2665",
|
||||
"encoded": "4can8av2009b"
|
||||
},
|
||||
{
|
||||
"description": "mix of ASCII and non-ASCII characters",
|
||||
"decoded": "b\u00FCcher",
|
||||
"encoded": "bcher-kva"
|
||||
},
|
||||
{
|
||||
"description": "long string with both ASCII and non-ASCII characters",
|
||||
"decoded": "Willst du die Bl\u00FCthe des fr\u00FChen, die Fr\u00FCchte des sp\u00E4teren Jahres",
|
||||
"encoded": "Willst du die Blthe des frhen, die Frchte des spteren Jahres-x9e96lkal"
|
||||
},
|
||||
{
|
||||
"description": "Arabic (Egyptian)",
|
||||
"decoded": "\u0644\u064A\u0647\u0645\u0627\u0628\u062A\u0643\u0644\u0645\u0648\u0634\u0639\u0631\u0628\u064A\u061F",
|
||||
"encoded": "egbpdaj6bu4bxfgehfvwxn"
|
||||
},
|
||||
{
|
||||
"description": "Chinese (simplified)",
|
||||
"decoded": "\u4ED6\u4EEC\u4E3A\u4EC0\u4E48\u4E0D\u8BF4\u4E2d\u6587",
|
||||
"encoded": "ihqwcrb4cv8a8dqg056pqjye"
|
||||
},
|
||||
{
|
||||
"description": "Chinese (traditional)",
|
||||
"decoded": "\u4ED6\u5011\u7232\u4EC0\u9EBD\u4E0D\u8AAA\u4E2D\u6587",
|
||||
"encoded": "ihqwctvzc91f659drss3x8bo0yb"
|
||||
},
|
||||
{
|
||||
"description": "Czech",
|
||||
"decoded": "Pro\u010Dprost\u011Bnemluv\u00ED\u010Desky",
|
||||
"encoded": "Proprostnemluvesky-uyb24dma41a"
|
||||
},
|
||||
{
|
||||
"description": "Hebrew",
|
||||
"decoded": "\u05DC\u05DE\u05D4\u05D4\u05DD\u05E4\u05E9\u05D5\u05D8\u05DC\u05D0\u05DE\u05D3\u05D1\u05E8\u05D9\u05DD\u05E2\u05D1\u05E8\u05D9\u05EA",
|
||||
"encoded": "4dbcagdahymbxekheh6e0a7fei0b"
|
||||
},
|
||||
{
|
||||
"description": "Hindi (Devanagari)",
|
||||
"decoded": "\u092F\u0939\u0932\u094B\u0917\u0939\u093F\u0928\u094D\u0926\u0940\u0915\u094D\u092F\u094B\u0902\u0928\u0939\u0940\u0902\u092C\u094B\u0932\u0938\u0915\u0924\u0947\u0939\u0948\u0902",
|
||||
"encoded": "i1baa7eci9glrd9b2ae1bj0hfcgg6iyaf8o0a1dig0cd"
|
||||
},
|
||||
{
|
||||
"description": "Japanese (kanji and hiragana)",
|
||||
"decoded": "\u306A\u305C\u307F\u3093\u306A\u65E5\u672C\u8A9E\u3092\u8A71\u3057\u3066\u304F\u308C\u306A\u3044\u306E\u304B",
|
||||
"encoded": "n8jok5ay5dzabd5bym9f0cm5685rrjetr6pdxa"
|
||||
},
|
||||
{
|
||||
"description": "Korean (Hangul syllables)",
|
||||
"decoded": "\uC138\uACC4\uC758\uBAA8\uB4E0\uC0AC\uB78C\uB4E4\uC774\uD55C\uAD6D\uC5B4\uB97C\uC774\uD574\uD55C\uB2E4\uBA74\uC5BC\uB9C8\uB098\uC88B\uC744\uAE4C",
|
||||
"encoded": "989aomsvi5e83db1d2a355cv1e0vak1dwrv93d5xbh15a0dt30a5jpsd879ccm6fea98c"
|
||||
},
|
||||
{
|
||||
"description": "Russian (Cyrillic)",
|
||||
"decoded": "\u043F\u043E\u0447\u0435\u043C\u0443\u0436\u0435\u043E\u043D\u0438\u043D\u0435\u0433\u043E\u0432\u043E\u0440\u044F\u0442\u043F\u043E\u0440\u0443\u0441\u0441\u043A\u0438",
|
||||
"encoded": "b1abfaaepdrnnbgefbadotcwatmq2g4l"
|
||||
},
|
||||
{
|
||||
"description": "Spanish",
|
||||
"decoded": "Porqu\u00E9nopuedensimplementehablarenEspa\u00F1ol",
|
||||
"encoded": "PorqunopuedensimplementehablarenEspaol-fmd56a"
|
||||
},
|
||||
{
|
||||
"description": "Vietnamese",
|
||||
"decoded": "T\u1EA1isaoh\u1ECDkh\u00F4ngth\u1EC3ch\u1EC9n\u00F3iti\u1EBFngVi\u1EC7t",
|
||||
"encoded": "TisaohkhngthchnitingVit-kjcr8268qyxafd2f1b9g"
|
||||
},
|
||||
{
|
||||
"decoded": "3\u5E74B\u7D44\u91D1\u516B\u5148\u751F",
|
||||
"encoded": "3B-ww4c5e180e575a65lsy2b"
|
||||
},
|
||||
{
|
||||
"decoded": "\u5B89\u5BA4\u5948\u7F8E\u6075-with-SUPER-MONKEYS",
|
||||
"encoded": "-with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n"
|
||||
},
|
||||
{
|
||||
"decoded": "Hello-Another-Way-\u305D\u308C\u305E\u308C\u306E\u5834\u6240",
|
||||
"encoded": "Hello-Another-Way--fc4qua05auwb3674vfr0b"
|
||||
},
|
||||
{
|
||||
"decoded": "\u3072\u3068\u3064\u5C4B\u6839\u306E\u4E0B2",
|
||||
"encoded": "2-u9tlzr9756bt3uc0v"
|
||||
},
|
||||
{
|
||||
"decoded": "Maji\u3067Koi\u3059\u308B5\u79D2\u524D",
|
||||
"encoded": "MajiKoi5-783gue6qz075azm5e"
|
||||
},
|
||||
{
|
||||
"decoded": "\u30D1\u30D5\u30A3\u30FCde\u30EB\u30F3\u30D0",
|
||||
"encoded": "de-jg4avhby1noc0d"
|
||||
},
|
||||
{
|
||||
"decoded": "\u305D\u306E\u30B9\u30D4\u30FC\u30C9\u3067",
|
||||
"encoded": "d9juau41awczczp"
|
||||
},
|
||||
{
|
||||
"description": "ASCII string that breaks the existing rules for host-name labels (It's not a realistic example for IDNA, because IDNA never encodes pure ASCII labels.)",
|
||||
"decoded": "-> $1.00 <-",
|
||||
"encoded": "-> $1.00 <--"
|
||||
}
|
||||
]
|
||||
25
third_party/rust/idna/tests/tests.rs
vendored
Normal file
25
third_party/rust/idna/tests/tests.rs
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
extern crate idna;
|
||||
extern crate rustc_serialize;
|
||||
extern crate test;
|
||||
|
||||
mod punycode;
|
||||
mod uts46;
|
||||
|
||||
fn main() {
|
||||
let mut tests = Vec::new();
|
||||
{
|
||||
let mut add_test = |name, run| {
|
||||
tests.push(test::TestDescAndFn {
|
||||
desc: test::TestDesc {
|
||||
name: test::DynTestName(name),
|
||||
ignore: false,
|
||||
should_panic: test::ShouldPanic::No,
|
||||
},
|
||||
testfn: run,
|
||||
})
|
||||
};
|
||||
punycode::collect_tests(&mut add_test);
|
||||
uts46::collect_tests(&mut add_test);
|
||||
}
|
||||
test::test_main(&std::env::args().collect::<Vec<_>>(), tests)
|
||||
}
|
||||
117
third_party/rust/idna/tests/uts46.rs
vendored
Normal file
117
third_party/rust/idna/tests/uts46.rs
vendored
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
// Copyright 2013-2014 Valentin Gosu.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::char;
|
||||
use idna::uts46;
|
||||
use test::TestFn;
|
||||
|
||||
pub fn collect_tests<F: FnMut(String, TestFn)>(add_test: &mut F) {
|
||||
// http://www.unicode.org/Public/idna/latest/IdnaTest.txt
|
||||
for (i, line) in include_str!("IdnaTest.txt").lines().enumerate() {
|
||||
if line == "" || line.starts_with("#") {
|
||||
continue
|
||||
}
|
||||
// Remove comments
|
||||
let mut line = match line.find("#") {
|
||||
Some(index) => &line[0..index],
|
||||
None => line
|
||||
};
|
||||
|
||||
let mut expected_failure = false;
|
||||
if line.starts_with("XFAIL") {
|
||||
expected_failure = true;
|
||||
line = &line[5..line.len()];
|
||||
};
|
||||
|
||||
let mut pieces = line.split(';').map(|x| x.trim()).collect::<Vec<&str>>();
|
||||
|
||||
let test_type = pieces.remove(0);
|
||||
let original = pieces.remove(0);
|
||||
let source = unescape(original);
|
||||
let to_unicode = pieces.remove(0);
|
||||
let to_ascii = pieces.remove(0);
|
||||
let nv8 = if pieces.len() > 0 { pieces.remove(0) } else { "" };
|
||||
|
||||
if expected_failure {
|
||||
continue;
|
||||
}
|
||||
|
||||
let test_name = format!("UTS #46 line {}", i + 1);
|
||||
add_test(test_name, TestFn::dyn_test_fn(move || {
|
||||
let result = uts46::to_ascii(&source, uts46::Flags {
|
||||
use_std3_ascii_rules: true,
|
||||
transitional_processing: test_type == "T",
|
||||
verify_dns_length: true,
|
||||
});
|
||||
|
||||
if to_ascii.starts_with("[") {
|
||||
if to_ascii.starts_with("[C") {
|
||||
// http://unicode.org/reports/tr46/#Deviations
|
||||
// applications that perform IDNA2008 lookup are not required to check
|
||||
// for these contexts
|
||||
return;
|
||||
}
|
||||
let res = result.ok();
|
||||
assert!(res == None, "Expected error. result: {} | original: {} | source: {}",
|
||||
res.unwrap(), original, source);
|
||||
return;
|
||||
}
|
||||
|
||||
let to_ascii = if to_ascii.len() > 0 {
|
||||
to_ascii.to_string()
|
||||
} else {
|
||||
if to_unicode.len() > 0 {
|
||||
to_unicode.to_string()
|
||||
} else {
|
||||
source.clone()
|
||||
}
|
||||
};
|
||||
|
||||
if nv8 == "NV8" {
|
||||
// This result isn't valid under IDNA2008. Skip it
|
||||
return;
|
||||
}
|
||||
|
||||
assert!(result.is_ok(), "Couldn't parse {} | original: {} | error: {:?}",
|
||||
source, original, result.err());
|
||||
let output = result.ok().unwrap();
|
||||
assert!(output == to_ascii, "result: {} | expected: {} | original: {} | source: {}",
|
||||
output, to_ascii, original, source);
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
fn unescape(input: &str) -> String {
|
||||
let mut output = String::new();
|
||||
let mut chars = input.chars();
|
||||
loop {
|
||||
match chars.next() {
|
||||
None => return output,
|
||||
Some(c) =>
|
||||
if c == '\\' {
|
||||
match chars.next().unwrap() {
|
||||
'\\' => output.push('\\'),
|
||||
'u' => {
|
||||
let c1 = chars.next().unwrap().to_digit(16).unwrap();
|
||||
let c2 = chars.next().unwrap().to_digit(16).unwrap();
|
||||
let c3 = chars.next().unwrap().to_digit(16).unwrap();
|
||||
let c4 = chars.next().unwrap().to_digit(16).unwrap();
|
||||
match char::from_u32((((c1 * 16 + c2) * 16 + c3) * 16 + c4))
|
||||
{
|
||||
Some(c) => output.push(c),
|
||||
None => { output.push_str(&format!("\\u{:X}{:X}{:X}{:X}",c1,c2,c3,c4)); }
|
||||
};
|
||||
}
|
||||
_ => panic!("Invalid test data input"),
|
||||
}
|
||||
} else {
|
||||
output.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue