authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-01-20 23:47:35 -08:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-01-20 23:47:35 -08:00
logf7a805254e559c21b3fc11f78b46221c0a4b42ab
tree531b8f81fa8c6dbfeb6868eeb314620d64269785
parent889939330ae5cff12cc54e0f0df4dc59808f1fce

move CheckBidi into inner iteration


1 files changed, 47 insertions(+), 41 deletions(-)

idna.zig+47-41
......@@ -249,6 +249,24 @@ fn Validity_Criteria(
249249 var zwnj_state: enum { start, dual_joining, transparent1, zwnj, transparent2, right_joining } = .start;
250250 var check_zwnj_state: ?bool = null;
251251
252 // 9. If CheckBidi, and if the domain name is a Bidi domain name, then the label must satisfy all six of the numbered conditions in RFC 5893 Section 2.
253 // 9.1. The first character must be a character with Bidi property L, R, or AL. If it has the R or AL property, it is an RTL label; if it has the L property, it is an LTR label.
254 // 9.2. In an RTL label, only characters with the Bidi properties R, AL, AN, EN, ES, CS, ET, ON, BN, or NSM are allowed.
255 // 9.3. In an RTL label, the end of the label must be a character with Bidi property R, AL, EN, or AN, followed by zero or more characters with Bidi property NSM.
256 // 9.4. In an RTL label, if an EN is present, no AN may be present, and vice versa.
257 // 9.5. In an LTR label, only characters with the Bidi properties L, EN, ES, CS, ET, ON, BN, or NSM are allowed.
258 // 9.6. In an LTR label, the end of the label must be a character with Bidi property L or EN, followed by zero or more characters with Bidi property NSM.
259 var seen_en = false;
260 var seen_an = false;
261 var last_bidi_class: ucd.BidiClass = .NSM;
262 if (CheckBidi) {
263 switch (first_p[4]) {
264 .L => {},
265 .R, .AL => {},
266 else => return error.IDNAFailure,
267 }
268 }
269
252270 while (it.nextCodepointSlice()) |sl| {
253271 const cp = std.unicode.utf8Decode(sl) catch unreachable;
254272 const status, _, _ = mappingRow(cp);
......@@ -293,10 +311,10 @@ fn Validity_Criteria(
293311
294312 // 3. In addition, if UseSTD3ASCIIRules=true and the code point is an ASCII code point (U+0000..U+007F), then it must be a lowercase letter (a-z), a digit (0-9), or a hyphen-minus (U+002D).
295313 // (Note: This excludes uppercase ASCII A-Z which are mapped in UTS #46 and disallowed in IDNA2008.)
296 if (UseSTD3ASCIIRules and std.ascii.isAscii(sl[0])) {
297 if (std.ascii.isLower(sl[0])) continue;
298 if (std.ascii.isDigit(sl[0])) continue;
299 if (sl[0] == '-') continue;
314 if (UseSTD3ASCIIRules and std.ascii.isAscii(sl[0])) blk: {
315 if (std.ascii.isLower(sl[0])) break :blk;
316 if (std.ascii.isDigit(sl[0])) break :blk;
317 if (sl[0] == '-') break :blk;
300318 return error.IDNAFailure;
301319 }
302320
......@@ -380,61 +398,49 @@ fn Validity_Criteria(
380398 }
381399 point_2before = point_before;
382400 point_before = p;
401
402 if (CheckBidi) {
403 switch (first_p[4]) {
404 .L => {
405 switch (p[4]) {
406 .L, .EN, .ES, .CS, .ET, .ON, .BN => |cl| last_bidi_class = cl,
407 .NSM => {},
408 else => return error.IDNAFailure,
409 }
410 },
411 .R, .AL => {
412 switch (p[4]) {
413 .R, .AL, .AN, .EN, .ES, .CS, .ET, .ON, .BN => |cl| last_bidi_class = cl,
414 .NSM => {},
415 else => return error.IDNAFailure,
416 }
417 if (p[4] == .EN and seen_an) return error.IDNAFailure;
418 if (p[4] == .EN) seen_en = true;
419 if (p[4] == .AN and seen_en) return error.IDNAFailure;
420 if (p[4] == .AN) seen_an = true;
421 },
422 else => unreachable,
423 }
424 }
383425 }
384426 if (CheckJoiners) {
385427 if (check_zwnj_state == true and zwnj_state != .right_joining) return error.IDNAFailure;
386428 }
387
388 // 9. If CheckBidi, and if the domain name is a Bidi domain name, then the label must satisfy all six of the numbered conditions in RFC 5893 Section 2.
389429 if (CheckBidi) {
390 // 1. The first character must be a character with Bidi property L, R, or AL. If it has the R or AL property, it is an RTL label; if it has the L property, it is an LTR label.
391 // 2. In an RTL label, only characters with the Bidi properties R, AL, AN, EN, ES, CS, ET, ON, BN, or NSM are allowed.
392 // 3. In an RTL label, the end of the label must be a character with Bidi property R, AL, EN, or AN, followed by zero or more characters with Bidi property NSM.
393 // 4. In an RTL label, if an EN is present, no AN may be present, and vice versa.
394 // 5. In an LTR label, only characters with the Bidi properties L, EN, ES, CS, ET, ON, BN, or NSM are allowed.
395 // 6. In an LTR label, the end of the label must be a character with Bidi property L or EN, followed by zero or more characters with Bidi property NSM.
396430 switch (first_p[4]) {
397431 .L => {
398 it.i = 0;
399 var last_bidi_class: ucd.BidiClass = .NSM;
400 while (it.nextCodepointSlice()) |sl| {
401 const cp = std.unicode.utf8Decode(sl) catch unreachable;
402 const point = ucd.unicode_data.find(cp);
403 switch (point[4]) {
404 .L, .EN, .ES, .CS, .ET, .ON, .BN => |cl| last_bidi_class = cl,
405 .NSM => {},
406 else => return error.IDNAFailure,
407 }
408 }
409432 switch (last_bidi_class) {
410433 .L, .EN => {},
411434 else => return error.IDNAFailure,
412435 }
413436 },
414437 .R, .AL => {
415 it.i = 0;
416 var seen_en = false;
417 var seen_an = false;
418 var last_bidi_class: ucd.BidiClass = .NSM;
419 while (it.nextCodepointSlice()) |sl| {
420 const cp = std.unicode.utf8Decode(sl) catch unreachable;
421 const point = ucd.unicode_data.find(cp);
422 switch (point[4]) {
423 .R, .AL, .AN, .EN, .ES, .CS, .ET, .ON, .BN => |cl| last_bidi_class = cl,
424 .NSM => {},
425 else => return error.IDNAFailure,
426 }
427 if (point[4] == .EN and seen_an) return error.IDNAFailure;
428 if (point[4] == .EN) seen_en = true;
429 if (point[4] == .AN and seen_en) return error.IDNAFailure;
430 if (point[4] == .AN) seen_an = true;
431 }
432438 switch (last_bidi_class) {
433439 .R, .AL, .EN, .AN => {},
434440 else => return error.IDNAFailure,
435441 }
436442 },
437 else => return error.IDNAFailure,
443 else => unreachable,
438444 }
439445 }
440446}