summaryrefslogtreecommitdiff
path: root/src/Folder.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/Folder.zig')
-rw-r--r--src/Folder.zig182
1 files changed, 182 insertions, 0 deletions
diff --git a/src/Folder.zig b/src/Folder.zig
new file mode 100644
index 0000000..43a3a5b
--- /dev/null
+++ b/src/Folder.zig
@@ -0,0 +1,182 @@
1const std = @import("std");
2const mem = std.mem;
3const testing = std.testing;
4
5const ascii = @import("ascii");
6pub const FoldData = @import("FoldData");
7const Normalizer = @import("Normalizer");
8
9fold_data: *const FoldData,
10
11const Self = @This();
12
13fn caseFold(
14 self: Self,
15 allocator: mem.Allocator,
16 cps: []const u21,
17) ![]const u21 {
18 var cfcps = std.ArrayList(u21).init(allocator);
19 defer cfcps.deinit();
20
21 for (cps) |cp| {
22 const cf = self.fold_data.caseFold(cp);
23
24 if (cf.len == 0) {
25 try cfcps.append(cp);
26 } else {
27 try cfcps.appendSlice(cf);
28 }
29 }
30
31 return try cfcps.toOwnedSlice();
32}
33
34fn changesWhenCaseFolded(self: Self, cps: []const u21) bool {
35 return for (cps) |cp| {
36 if (self.fold_data.changesWhenCaseFolded(cp)) break true;
37 } else false;
38}
39
40pub fn compatCaselessMatch(
41 self: Self,
42 allocator: mem.Allocator,
43 normalizer: *const Normalizer,
44 a: []const u8,
45 b: []const u8,
46) !bool {
47 if (ascii.isAsciiOnly(a) and ascii.isAsciiOnly(b)) return std.ascii.eqlIgnoreCase(a, b);
48
49 // Process a
50 const nfd_a = try normalizer.nfxdCodePoints(allocator, a, .nfd);
51 defer allocator.free(nfd_a);
52
53 var need_free_cf_nfd_a = false;
54 var cf_nfd_a: []const u21 = nfd_a;
55 if (self.changesWhenCaseFolded(nfd_a)) {
56 cf_nfd_a = try self.caseFold(allocator, nfd_a);
57 need_free_cf_nfd_a = true;
58 }
59 defer if (need_free_cf_nfd_a) allocator.free(cf_nfd_a);
60
61 const nfkd_cf_nfd_a = try normalizer.nfkdCodePoints(allocator, cf_nfd_a);
62 defer allocator.free(nfkd_cf_nfd_a);
63 const cf_nfkd_cf_nfd_a = try self.caseFold(allocator, nfkd_cf_nfd_a);
64 defer allocator.free(cf_nfkd_cf_nfd_a);
65 const nfkd_cf_nfkd_cf_nfd_a = try normalizer.nfkdCodePoints(allocator, cf_nfkd_cf_nfd_a);
66 defer allocator.free(nfkd_cf_nfkd_cf_nfd_a);
67
68 // Process b
69 const nfd_b = try normalizer.nfxdCodePoints(allocator, b, .nfd);
70 defer allocator.free(nfd_b);
71
72 var need_free_cf_nfd_b = false;
73 var cf_nfd_b: []const u21 = nfd_b;
74 if (self.changesWhenCaseFolded(nfd_b)) {
75 cf_nfd_b = try self.caseFold(allocator, nfd_b);
76 need_free_cf_nfd_b = true;
77 }
78 defer if (need_free_cf_nfd_b) allocator.free(cf_nfd_b);
79
80 const nfkd_cf_nfd_b = try normalizer.nfkdCodePoints(allocator, cf_nfd_b);
81 defer allocator.free(nfkd_cf_nfd_b);
82 const cf_nfkd_cf_nfd_b = try self.caseFold(allocator, nfkd_cf_nfd_b);
83 defer allocator.free(cf_nfkd_cf_nfd_b);
84 const nfkd_cf_nfkd_cf_nfd_b = try normalizer.nfkdCodePoints(allocator, cf_nfkd_cf_nfd_b);
85 defer allocator.free(nfkd_cf_nfkd_cf_nfd_b);
86
87 return mem.eql(u21, nfkd_cf_nfkd_cf_nfd_a, nfkd_cf_nfkd_cf_nfd_b);
88}
89
90test "compatCaselessMatch" {
91 const allocator = testing.allocator;
92
93 var norm_data = try Normalizer.NormData.init(allocator);
94 defer norm_data.deinit();
95 const n = Normalizer{ .norm_data = &norm_data };
96
97 var fold_data = try FoldData.init(allocator);
98 defer fold_data.deinit();
99 const caser = Self{ .fold_data = &fold_data };
100
101 try testing.expect(try caser.compatCaselessMatch(allocator, &n, "ascii only!", "ASCII Only!"));
102
103 const a = "Héllo World! \u{3d3}";
104 const b = "He\u{301}llo World! \u{3a5}\u{301}";
105 try testing.expect(try caser.compatCaselessMatch(allocator, &n, a, b));
106
107 const c = "He\u{301}llo World! \u{3d2}\u{301}";
108 try testing.expect(try caser.compatCaselessMatch(allocator, &n, a, c));
109}
110
111pub fn canonCaselessMatch(
112 self: Self,
113 allocator: mem.Allocator,
114 normalizer: *const Normalizer,
115 a: []const u8,
116 b: []const u8,
117) !bool {
118 if (ascii.isAsciiOnly(a) and ascii.isAsciiOnly(b)) return std.ascii.eqlIgnoreCase(a, b);
119
120 // Process a
121 const nfd_a = try normalizer.nfxdCodePoints(allocator, a, .nfd);
122 defer allocator.free(nfd_a);
123
124 var need_free_cf_nfd_a = false;
125 var cf_nfd_a: []const u21 = nfd_a;
126 if (self.changesWhenCaseFolded(nfd_a)) {
127 cf_nfd_a = try self.caseFold(allocator, nfd_a);
128 need_free_cf_nfd_a = true;
129 }
130 defer if (need_free_cf_nfd_a) allocator.free(cf_nfd_a);
131
132 var need_free_nfd_cf_nfd_a = false;
133 var nfd_cf_nfd_a = cf_nfd_a;
134 if (!need_free_cf_nfd_a) {
135 nfd_cf_nfd_a = try normalizer.nfdCodePoints(allocator, cf_nfd_a);
136 need_free_nfd_cf_nfd_a = true;
137 }
138 defer if (need_free_nfd_cf_nfd_a) allocator.free(nfd_cf_nfd_a);
139
140 // Process b
141 const nfd_b = try normalizer.nfxdCodePoints(allocator, b, .nfd);
142 defer allocator.free(nfd_b);
143
144 var need_free_cf_nfd_b = false;
145 var cf_nfd_b: []const u21 = nfd_b;
146 if (self.changesWhenCaseFolded(nfd_b)) {
147 cf_nfd_b = try self.caseFold(allocator, nfd_b);
148 need_free_cf_nfd_b = true;
149 }
150 defer if (need_free_cf_nfd_b) allocator.free(cf_nfd_b);
151
152 var need_free_nfd_cf_nfd_b = false;
153 var nfd_cf_nfd_b = cf_nfd_b;
154 if (!need_free_cf_nfd_b) {
155 nfd_cf_nfd_b = try normalizer.nfdCodePoints(allocator, cf_nfd_b);
156 need_free_nfd_cf_nfd_b = true;
157 }
158 defer if (need_free_nfd_cf_nfd_b) allocator.free(nfd_cf_nfd_b);
159
160 return mem.eql(u21, nfd_cf_nfd_a, nfd_cf_nfd_b);
161}
162
163test "canonCaselessMatch" {
164 const allocator = testing.allocator;
165
166 var norm_data = try Normalizer.NormData.init(allocator);
167 defer norm_data.deinit();
168 const n = Normalizer{ .norm_data = &norm_data };
169
170 var fold_data = try FoldData.init(allocator);
171 defer fold_data.deinit();
172 const caser = Self{ .fold_data = &fold_data };
173
174 try testing.expect(try caser.canonCaselessMatch(allocator, &n, "ascii only!", "ASCII Only!"));
175
176 const a = "Héllo World! \u{3d3}";
177 const b = "He\u{301}llo World! \u{3a5}\u{301}";
178 try testing.expect(!try caser.canonCaselessMatch(allocator, &n, a, b));
179
180 const c = "He\u{301}llo World! \u{3d2}\u{301}";
181 try testing.expect(try caser.canonCaselessMatch(allocator, &n, a, c));
182}