authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-01-20 23:34:41 -08:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-01-20 23:34:41 -08:00
log889939330ae5cff12cc54e0f0df4dc59808f1fce
treec24cd7dc3b23acf6712e2aaa6c70e18dca76f3d1
parent5507a56c5dc2d5f1264acafb094c4f7697ecd5f0

move CheckJoiners into the inner iteration


1 files changed, 83 insertions(+), 85 deletions(-)

idna.zig+83-85
......@@ -242,6 +242,13 @@ fn Validity_Criteria(
242242 var it = std.unicode.Utf8View.initUnchecked(label).iterator();
243243 var prev_ccc: u8 = 0;
244244 var prev_cp: u21 = 0;
245
246 // 8. If CheckJoiners, the label must satisify the ContextJ rules from RFC 5892 Appendix A.
247 var point_2before: ucd.unicode_data.Codepoint = ucd.unicode_data.data[0];
248 var point_before: ucd.unicode_data.Codepoint = ucd.unicode_data.data[0];
249 var zwnj_state: enum { start, dual_joining, transparent1, zwnj, transparent2, right_joining } = .start;
250 var check_zwnj_state: ?bool = null;
251
245252 while (it.nextCodepointSlice()) |sl| {
246253 const cp = std.unicode.utf8Decode(sl) catch unreachable;
247254 const status, _, _ = mappingRow(cp);
......@@ -292,98 +299,89 @@ fn Validity_Criteria(
292299 if (sl[0] == '-') continue;
293300 return error.IDNAFailure;
294301 }
295 }
296302
297 // 8. If CheckJoiners, the label must satisify the ContextJ rules from RFC 5892 Appendix A.
298 if (CheckJoiners) {
299 it.i = 0;
300 var point_2before: ucd.unicode_data.Codepoint = ucd.unicode_data.data[0];
301 var point_before: ucd.unicode_data.Codepoint = ucd.unicode_data.data[0];
302 var zwnj_state: enum { start, dual_joining, transparent1, zwnj, transparent2, right_joining } = .start;
303 var check_zwnj_state: ?bool = null;
303 const p = ucd.unicode_data.find(cp);
304304
305 while (it.nextCodepointSlice()) |s| {
306 const c = std.unicode.utf8Decode(s) catch unreachable;
307 const p = ucd.unicode_data.find(c);
308 {
309 // Appendix A.1. ZERO WIDTH NON-JOINER
310 if (c == 0x200C) {
311 if (check_zwnj_state == null) check_zwnj_state = true;
312 if (point_before[3] == 9) check_zwnj_state = false;
313 }
314 switch (zwnj_state) {
315 .start => {
316 if (Joining_Type(c)) |t| {
317 if (t == .L) zwnj_state = .dual_joining;
318 if (t == .D) zwnj_state = .dual_joining;
319 }
320 },
321 .dual_joining => {
322 if (Joining_Type(c)) |t| {
323 switch (t) {
324 .T => zwnj_state = .transparent1,
325 .L, .D => {},
326 else => zwnj_state = .transparent1,
327 }
328 } else {
329 zwnj_state = .transparent1;
330 }
331 if (c == 0x200C) zwnj_state = .zwnj;
332 },
333 .transparent1 => {
334 if (c == 0x200C) {
335 zwnj_state = .zwnj;
336 } else {
337 zwnj_state = .start;
305 if (CheckJoiners) {
306 // Appendix A.1. ZERO WIDTH NON-JOINER
307 if (cp == 0x200C) {
308 if (check_zwnj_state == null) check_zwnj_state = true;
309 if (point_before[3] == 9) check_zwnj_state = false;
310 }
311 switch (zwnj_state) {
312 .start => {
313 if (Joining_Type(cp)) |t| {
314 if (t == .L) zwnj_state = .dual_joining;
315 if (t == .D) zwnj_state = .dual_joining;
316 }
317 },
318 .dual_joining => {
319 if (Joining_Type(cp)) |t| {
320 switch (t) {
321 .T => zwnj_state = .transparent1,
322 .L, .D => {},
323 else => zwnj_state = .transparent1,
338324 }
339 },
340 .zwnj => {
341 if (Joining_Type(c)) |t| {
342 switch (t) {
343 .T => zwnj_state = .transparent2,
344 .R, .D => zwnj_state = .right_joining,
345 .L => zwnj_state = .dual_joining,
346 else => zwnj_state = .transparent2,
347 }
348 } else {
349 zwnj_state = .transparent2;
325 } else {
326 zwnj_state = .transparent1;
327 }
328 if (cp == 0x200C) zwnj_state = .zwnj;
329 },
330 .transparent1 => {
331 if (cp == 0x200C) {
332 zwnj_state = .zwnj;
333 } else {
334 zwnj_state = .start;
335 }
336 },
337 .zwnj => {
338 if (Joining_Type(cp)) |t| {
339 switch (t) {
340 .T => zwnj_state = .transparent2,
341 .R, .D => zwnj_state = .right_joining,
342 .L => zwnj_state = .dual_joining,
343 else => zwnj_state = .transparent2,
350344 }
351 },
352 .transparent2 => {
353 if (Joining_Type(c)) |t| {
354 switch (t) {
355 .R, .D => zwnj_state = .right_joining,
356 .L => zwnj_state = .dual_joining,
357 else => zwnj_state = .start,
358 }
359 } else {
360 zwnj_state = .start;
345 } else {
346 zwnj_state = .transparent2;
347 }
348 },
349 .transparent2 => {
350 if (Joining_Type(cp)) |t| {
351 switch (t) {
352 .R, .D => zwnj_state = .right_joining,
353 .L => zwnj_state = .dual_joining,
354 else => zwnj_state = .start,
361355 }
362 },
363 .right_joining => {
364 //rule passed
365 },
366 }
367 // Appendix A.2. ZERO WIDTH JOINER
368 if (c == 0x200D and point_before[3] != 9) return error.IDNAFailure;
369 // Appendix A.3. MIDDLE DOT
370 if (point_before[0] == 0x00B7 and !(point_2before[0] == 0x006C and c == 0x006C)) return error.IDNAFailure;
371 // Appendix A.4. GREEK LOWER NUMERAL SIGN (KERAIA)
372 if (point_before[0] == 0x0375 and !(Script(c) == .Greek)) return error.IDNAFailure;
373 // Appendix A.5. HEBREW PUNCTUATION GERESH
374 if (c == 0x05F3 and !(Script(point_before[0]) == .Hebrew)) return error.IDNAFailure;
375 // Appendix A.6. HEBREW PUNCTUATION GERSHAYIM
376 if (c == 0x05F4 and !(Script(point_before[0]) == .Hebrew)) return error.IDNAFailure;
377 // Appendix A.7. KATAKANA MIDDLE DOT
378 if (false) return error.IDNAFailure;
379 // Appendix A.8. ARABIC-INDIC DIGITS
380 if (false) return error.IDNAFailure;
381 // Appendix A.9. EXTENDED ARABIC-INDIC DIGITS
382 if (false) return error.IDNAFailure;
356 } else {
357 zwnj_state = .start;
358 }
359 },
360 .right_joining => {
361 //rule passed
362 },
383363 }
384 point_2before = point_before;
385 point_before = p;
364 // Appendix A.2. ZERO WIDTH JOINER
365 if (cp == 0x200D and point_before[3] != 9) return error.IDNAFailure;
366 // Appendix A.3. MIDDLE DOT
367 if (point_before[0] == 0x00B7 and !(point_2before[0] == 0x006C and cp == 0x006C)) return error.IDNAFailure;
368 // Appendix A.4. GREEK LOWER NUMERAL SIGN (KERAIA)
369 if (point_before[0] == 0x0375 and !(Script(cp) == .Greek)) return error.IDNAFailure;
370 // Appendix A.5. HEBREW PUNCTUATION GERESH
371 if (cp == 0x05F3 and !(Script(point_before[0]) == .Hebrew)) return error.IDNAFailure;
372 // Appendix A.6. HEBREW PUNCTUATION GERSHAYIM
373 if (cp == 0x05F4 and !(Script(point_before[0]) == .Hebrew)) return error.IDNAFailure;
374 // Appendix A.7. KATAKANA MIDDLE DOT
375 if (false) return error.IDNAFailure;
376 // Appendix A.8. ARABIC-INDIC DIGITS
377 if (false) return error.IDNAFailure;
378 // Appendix A.9. EXTENDED ARABIC-INDIC DIGITS
379 if (false) return error.IDNAFailure;
386380 }
381 point_2before = point_before;
382 point_before = p;
383 }
384 if (CheckJoiners) {
387385 if (check_zwnj_state == true and zwnj_state != .right_joining) return error.IDNAFailure;
388386 }
389387