summaryrefslogtreecommitdiff
path: root/src/GeneralCategories.zig
diff options
context:
space:
mode:
authorGravatar Sam Atman2025-04-30 20:30:39 -0400
committerGravatar Sam Atman2025-04-30 20:30:39 -0400
commit10048b0d31d0db923ae39c6bbd67139ed6252f6f (patch)
tree65df1666aacd102f59b4ac0844ccc7f7ddda91db /src/GeneralCategories.zig
parentSetup variants for all allocating modules (diff)
downloadzg-10048b0d31d0db923ae39c6bbd67139ed6252f6f.tar.gz
zg-10048b0d31d0db923ae39c6bbd67139ed6252f6f.tar.xz
zg-10048b0d31d0db923ae39c6bbd67139ed6252f6f.zip
Allocation Failure Tests
These turned up an excessive amount of allocations in CanonData and CompatData, which have been reduced to two through the somewhat squirrely use of 'magic numbers'. There are now allocation tests for every allocated structure in the library, and they run to completion in a reasonable amount of time. So, that's nice.
Diffstat (limited to 'src/GeneralCategories.zig')
-rw-r--r--src/GeneralCategories.zig79
1 files changed, 49 insertions, 30 deletions
diff --git a/src/GeneralCategories.zig b/src/GeneralCategories.zig
index b7c82c0..3e76d82 100644
--- a/src/GeneralCategories.zig
+++ b/src/GeneralCategories.zig
@@ -46,7 +46,16 @@ pub fn init(allocator: Allocator) Allocator.Error!GeneralCategories {
46 return gencat; 46 return gencat;
47} 47}
48 48
49pub fn setup(self: *GeneralCategories, allocator: Allocator) Allocator.Error!void { 49pub fn setup(gencat: *GeneralCategories, allocator: Allocator) Allocator.Error!void {
50 gencat.setupInner(allocator) catch |err| {
51 switch (err) {
52 error.OutOfMemory => |e| return e,
53 else => unreachable,
54 }
55 };
56}
57
58inline fn setupInner(gencat: *GeneralCategories, allocator: Allocator) !void {
50 const decompressor = compress.flate.inflate.decompressor; 59 const decompressor = compress.flate.inflate.decompressor;
51 const in_bytes = @embedFile("gencat"); 60 const in_bytes = @embedFile("gencat");
52 var in_fbs = std.io.fixedBufferStream(in_bytes); 61 var in_fbs = std.io.fixedBufferStream(in_bytes);
@@ -56,35 +65,35 @@ pub fn setup(self: *GeneralCategories, allocator: Allocator) Allocator.Error!voi
56 const endian = builtin.cpu.arch.endian(); 65 const endian = builtin.cpu.arch.endian();
57 66
58 const s1_len: u16 = reader.readInt(u16, endian) catch unreachable; 67 const s1_len: u16 = reader.readInt(u16, endian) catch unreachable;
59 self.s1 = try allocator.alloc(u16, s1_len); 68 gencat.s1 = try allocator.alloc(u16, s1_len);
60 errdefer allocator.free(self.s1); 69 errdefer allocator.free(gencat.s1);
61 for (0..s1_len) |i| self.s1[i] = try reader.readInt(u16, endian); 70 for (0..s1_len) |i| gencat.s1[i] = try reader.readInt(u16, endian);
62 71
63 const s2_len: u16 = reader.readInt(u16, endian) catch unreachable; 72 const s2_len: u16 = reader.readInt(u16, endian) catch unreachable;
64 self.s2 = try allocator.alloc(u5, s2_len); 73 gencat.s2 = try allocator.alloc(u5, s2_len);
65 errdefer allocator.free(self.s2); 74 errdefer allocator.free(gencat.s2);
66 for (0..s2_len) |i| self.s2[i] = @intCast(reader.readInt(u8, endian) catch unreachable); 75 for (0..s2_len) |i| gencat.s2[i] = @intCast(reader.readInt(u8, endian) catch unreachable);
67 76
68 const s3_len: u16 = reader.readInt(u8, endian) catch unreachable; 77 const s3_len: u16 = reader.readInt(u8, endian) catch unreachable;
69 self.s3 = try allocator.alloc(u5, s3_len); 78 gencat.s3 = try allocator.alloc(u5, s3_len);
70 errdefer allocator.free(self.s3); 79 errdefer allocator.free(gencat.s3);
71 for (0..s3_len) |i| self.s3[i] = @intCast(reader.readInt(u8, endian) catch unreachable); 80 for (0..s3_len) |i| gencat.s3[i] = @intCast(reader.readInt(u8, endian) catch unreachable);
72} 81}
73 82
74pub fn deinit(self: *const GeneralCategories, allocator: mem.Allocator) void { 83pub fn deinit(gencat: *const GeneralCategories, allocator: mem.Allocator) void {
75 allocator.free(self.s1); 84 allocator.free(gencat.s1);
76 allocator.free(self.s2); 85 allocator.free(gencat.s2);
77 allocator.free(self.s3); 86 allocator.free(gencat.s3);
78} 87}
79 88
80/// Lookup the General Category for `cp`. 89/// Lookup the General Category for `cp`.
81pub fn gc(self: GeneralCategories, cp: u21) Gc { 90pub fn gc(gencat: GeneralCategories, cp: u21) Gc {
82 return @enumFromInt(self.s3[self.s2[self.s1[cp >> 8] + (cp & 0xff)]]); 91 return @enumFromInt(gencat.s3[gencat.s2[gencat.s1[cp >> 8] + (cp & 0xff)]]);
83} 92}
84 93
85/// True if `cp` has an C general category. 94/// True if `cp` has an C general category.
86pub fn isControl(self: GeneralCategories, cp: u21) bool { 95pub fn isControl(gencat: GeneralCategories, cp: u21) bool {
87 return switch (self.gc(cp)) { 96 return switch (gencat.gc(cp)) {
88 .Cc, 97 .Cc,
89 .Cf, 98 .Cf,
90 .Cn, 99 .Cn,
@@ -96,8 +105,8 @@ pub fn isControl(self: GeneralCategories, cp: u21) bool {
96} 105}
97 106
98/// True if `cp` has an L general category. 107/// True if `cp` has an L general category.
99pub fn isLetter(self: GeneralCategories, cp: u21) bool { 108pub fn isLetter(gencat: GeneralCategories, cp: u21) bool {
100 return switch (self.gc(cp)) { 109 return switch (gencat.gc(cp)) {
101 .Ll, 110 .Ll,
102 .Lm, 111 .Lm,
103 .Lo, 112 .Lo,
@@ -109,8 +118,8 @@ pub fn isLetter(self: GeneralCategories, cp: u21) bool {
109} 118}
110 119
111/// True if `cp` has an M general category. 120/// True if `cp` has an M general category.
112pub fn isMark(self: GeneralCategories, cp: u21) bool { 121pub fn isMark(gencat: GeneralCategories, cp: u21) bool {
113 return switch (self.gc(cp)) { 122 return switch (gencat.gc(cp)) {
114 .Mc, 123 .Mc,
115 .Me, 124 .Me,
116 .Mn, 125 .Mn,
@@ -120,8 +129,8 @@ pub fn isMark(self: GeneralCategories, cp: u21) bool {
120} 129}
121 130
122/// True if `cp` has an N general category. 131/// True if `cp` has an N general category.
123pub fn isNumber(self: GeneralCategories, cp: u21) bool { 132pub fn isNumber(gencat: GeneralCategories, cp: u21) bool {
124 return switch (self.gc(cp)) { 133 return switch (gencat.gc(cp)) {
125 .Nd, 134 .Nd,
126 .Nl, 135 .Nl,
127 .No, 136 .No,
@@ -131,8 +140,8 @@ pub fn isNumber(self: GeneralCategories, cp: u21) bool {
131} 140}
132 141
133/// True if `cp` has an P general category. 142/// True if `cp` has an P general category.
134pub fn isPunctuation(self: GeneralCategories, cp: u21) bool { 143pub fn isPunctuation(gencat: GeneralCategories, cp: u21) bool {
135 return switch (self.gc(cp)) { 144 return switch (gencat.gc(cp)) {
136 .Pc, 145 .Pc,
137 .Pd, 146 .Pd,
138 .Pe, 147 .Pe,
@@ -146,8 +155,8 @@ pub fn isPunctuation(self: GeneralCategories, cp: u21) bool {
146} 155}
147 156
148/// True if `cp` has an S general category. 157/// True if `cp` has an S general category.
149pub fn isSymbol(self: GeneralCategories, cp: u21) bool { 158pub fn isSymbol(gencat: GeneralCategories, cp: u21) bool {
150 return switch (self.gc(cp)) { 159 return switch (gencat.gc(cp)) {
151 .Sc, 160 .Sc,
152 .Sk, 161 .Sk,
153 .Sm, 162 .Sm,
@@ -158,8 +167,8 @@ pub fn isSymbol(self: GeneralCategories, cp: u21) bool {
158} 167}
159 168
160/// True if `cp` has an Z general category. 169/// True if `cp` has an Z general category.
161pub fn isSeparator(self: GeneralCategories, cp: u21) bool { 170pub fn isSeparator(gencat: GeneralCategories, cp: u21) bool {
162 return switch (self.gc(cp)) { 171 return switch (gencat.gc(cp)) {
163 .Zl, 172 .Zl,
164 .Zp, 173 .Zp,
165 .Zs, 174 .Zs,
@@ -168,8 +177,18 @@ pub fn isSeparator(self: GeneralCategories, cp: u21) bool {
168 }; 177 };
169} 178}
170 179
180fn testAllocator(allocator: Allocator) !void {
181 var gen_cat = try GeneralCategories.init(allocator);
182 gen_cat.deinit(allocator);
183}
184
185test "Allocation failure" {
186 try testing.checkAllAllocationFailures(testing.allocator, testAllocator, .{});
187}
188
171const std = @import("std"); 189const std = @import("std");
172const builtin = @import("builtin"); 190const builtin = @import("builtin");
173const compress = std.compress; 191const compress = std.compress;
174const mem = std.mem; 192const mem = std.mem;
193const testing = std.testing;
175const Allocator = mem.Allocator; 194const Allocator = mem.Allocator;