summaryrefslogtreecommitdiff
path: root/src/code_point.zig
diff options
context:
space:
mode:
authorGravatar Sam Atman2025-05-09 12:58:23 -0400
committerGravatar Sam Atman2025-05-09 12:58:23 -0400
commit8e24f0ac6a0c9052381001688cf425f2a4017b30 (patch)
tree9c2e866c87b6b4d5a857f64f9d258c372fec7aca /src/code_point.zig
parentMake DisplayWidth.setup public (diff)
downloadzg-8e24f0ac6a0c9052381001688cf425f2a4017b30.tar.gz
zg-8e24f0ac6a0c9052381001688cf425f2a4017b30.tar.xz
zg-8e24f0ac6a0c9052381001688cf425f2a4017b30.zip
Add reverse CodePoint iterator
Diffstat (limited to 'src/code_point.zig')
-rw-r--r--src/code_point.zig81
1 files changed, 75 insertions, 6 deletions
diff --git a/src/code_point.zig b/src/code_point.zig
index 13e38bf..ed5fede 100644
--- a/src/code_point.zig
+++ b/src/code_point.zig
@@ -94,6 +94,45 @@ pub const Iterator = struct {
94 } 94 }
95}; 95};
96 96
97pub const ReverseIterator = struct {
98 bytes: []const u8,
99 i: ?u32,
100
101 pub fn init(str: []const u8) ReverseIterator {
102 var r_iter: ReverseIterator = undefined;
103 r_iter.bytes = str;
104 r_iter.i = if (str.len == 0) 0 else @intCast(str.len - 1);
105 return r_iter;
106 }
107
108 pub fn prev(iter: *ReverseIterator) ?CodePoint {
109 if (iter.i == null) return null;
110 var i_prev = iter.i.?;
111
112 while (i_prev > 0) : (i_prev -= 1) {
113 if (!followbyte(iter.bytes[i_prev])) break;
114 if (i_prev == 0) break;
115 }
116
117 if (i_prev > 0)
118 iter.i = i_prev - 1
119 else
120 iter.i = null;
121
122 return decode(iter.bytes[i_prev..], i_prev);
123 }
124
125 pub fn peek(iter: *ReverseIterator) ?CodePoint {
126 const saved_i = iter.i;
127 defer iter.i = saved_i;
128 return iter.prev();
129 }
130};
131
132inline fn followbyte(b: u8) bool {
133 return 0x80 <= b and b <= 0xbf;
134}
135
97test "decode" { 136test "decode" {
98 const bytes = "🌩️"; 137 const bytes = "🌩️";
99 const res = decode(bytes, 0); 138 const res = decode(bytes, 0);
@@ -107,12 +146,42 @@ test "decode" {
107 } 146 }
108} 147}
109 148
110test "peek" { 149test Iterator {
111 var iter = Iterator{ .bytes = "Hi" }; 150 var iter = Iterator{ .bytes = "Hi" };
112 151
113 try std.testing.expectEqual(@as(u21, 'H'), iter.next().?.code); 152 try testing.expectEqual(@as(u21, 'H'), iter.next().?.code);
114 try std.testing.expectEqual(@as(u21, 'i'), iter.peek().?.code); 153 try testing.expectEqual(@as(u21, 'i'), iter.peek().?.code);
115 try std.testing.expectEqual(@as(u21, 'i'), iter.next().?.code); 154 try testing.expectEqual(@as(u21, 'i'), iter.next().?.code);
116 try std.testing.expectEqual(@as(?CodePoint, null), iter.peek()); 155 try testing.expectEqual(@as(?CodePoint, null), iter.peek());
117 try std.testing.expectEqual(@as(?CodePoint, null), iter.next()); 156 try testing.expectEqual(@as(?CodePoint, null), iter.next());
157 try testing.expectEqual(@as(?CodePoint, null), iter.next());
118} 158}
159
160test ReverseIterator {
161 {
162 var r_iter: ReverseIterator = .init("ABC");
163 try testing.expectEqual(@as(u21, 'C'), r_iter.prev().?.code);
164 try testing.expectEqual(@as(u21, 'B'), r_iter.peek().?.code);
165 try testing.expectEqual(@as(u21, 'B'), r_iter.prev().?.code);
166 try testing.expectEqual(@as(u21, 'A'), r_iter.prev().?.code);
167 try testing.expectEqual(@as(?CodePoint, null), r_iter.peek());
168 try testing.expectEqual(@as(?CodePoint, null), r_iter.prev());
169 try testing.expectEqual(@as(?CodePoint, null), r_iter.prev());
170 }
171 {
172 var r_iter: ReverseIterator = .init("∅δq🦾ă");
173 try testing.expectEqual(@as(u21, 'ă'), r_iter.prev().?.code);
174 try testing.expectEqual(@as(u21, '🦾'), r_iter.prev().?.code);
175 try testing.expectEqual(@as(u21, 'q'), r_iter.prev().?.code);
176 try testing.expectEqual(@as(u21, 'δ'), r_iter.peek().?.code);
177 try testing.expectEqual(@as(u21, 'δ'), r_iter.prev().?.code);
178 try testing.expectEqual(@as(u21, '∅'), r_iter.peek().?.code);
179 try testing.expectEqual(@as(u21, '∅'), r_iter.peek().?.code);
180 try testing.expectEqual(@as(u21, '∅'), r_iter.prev().?.code);
181 try testing.expectEqual(@as(?CodePoint, null), r_iter.peek());
182 try testing.expectEqual(@as(?CodePoint, null), r_iter.prev());
183 try testing.expectEqual(@as(?CodePoint, null), r_iter.prev());
184 }
185}
186
187const testing = std.testing;