@zigc/lib 0.15.1 → 0.15.2-test.99

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE +19 -0
  2. package/build-web/fuzz.zig +6 -6
  3. package/build-web/time_report.zig +14 -13
  4. package/compiler/reduce/Walk.zig +10 -9
  5. package/compiler/reduce.zig +19 -20
  6. package/compiler/resinator/compile.zig +1 -1
  7. package/compiler_rt/arm.zig +1 -1
  8. package/init/build.zig +1 -1
  9. package/libc/include/generic-glibc/arpa/inet.h +3 -0
  10. package/libc/musl/src/fenv/loongarch64/fenv-sf.c +3 -0
  11. package/package.json +1 -1
  12. package/std/Build/Step/Compile.zig +20 -1
  13. package/std/Build/Step/TranslateC.zig +6 -0
  14. package/std/Build/WebServer.zig +1 -1
  15. package/std/Build.zig +4 -1
  16. package/std/Io/Reader/Limited.zig +44 -0
  17. package/std/Io/Reader.zig +128 -28
  18. package/std/Io/Writer.zig +31 -6
  19. package/std/Target.zig +8 -0
  20. package/std/Thread.zig +3 -4
  21. package/std/c.zig +87 -26
  22. package/std/crypto/aes_ocb.zig +32 -3
  23. package/std/crypto/tls/Client.zig +19 -8
  24. package/std/debug.zig +1 -1
  25. package/std/fs/Dir.zig +2 -1
  26. package/std/fs/File.zig +105 -104
  27. package/std/http/Client.zig +3 -2
  28. package/std/json/static.zig +3 -3
  29. package/std/math/big/int.zig +3 -4
  30. package/std/math/powi.zig +1 -0
  31. package/std/mem/Allocator.zig +3 -1
  32. package/std/mem.zig +3 -1
  33. package/std/net.zig +3 -2
  34. package/std/os/linux/bpf.zig +2 -2
  35. package/std/os/linux/powerpc.zig +74 -12
  36. package/std/os/linux/powerpc64.zig +74 -12
  37. package/std/os/linux.zig +7 -2
  38. package/std/os/uefi/protocol/service_binding.zig +1 -1
  39. package/std/os/uefi/tables.zig +1 -1
  40. package/std/pie.zig +1 -1
  41. package/std/posix.zig +6 -4
  42. package/std/process/Child.zig +5 -1
  43. package/std/process.zig +16 -2
  44. package/std/sort/pdq.zig +48 -1
  45. package/std/testing.zig +60 -0
  46. package/std/zig/llvm/BitcodeReader.zig +5 -1
  47. package/std/zig/system/linux.zig +1 -4
  48. package/std/zig/system.zig +3 -2
  49. package/std/zon/parse.zig +1 -0
  50. package/ubsan_rt.zig +3 -3
package/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2026 Nurul Huda (Apon).
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -101,22 +101,22 @@ const SourceLocationIndex = enum(u32) {
101
101
 
102
102
  fn sourceLocationLinkHtml(
103
103
  sli: SourceLocationIndex,
104
- out: *std.ArrayListUnmanaged(u8),
104
+ out: *std.ArrayList(u8),
105
105
  focused: bool,
106
- ) Allocator.Error!void {
106
+ ) error{OutOfMemory}!void {
107
107
  const sl = sli.ptr();
108
- try out.writer(gpa).print("<code{s}>", .{
108
+ try out.print(gpa, "<code{s}>", .{
109
109
  @as([]const u8, if (focused) " class=\"status-running\"" else ""),
110
110
  });
111
111
  try sli.appendPath(out);
112
- try out.writer(gpa).print(":{d}:{d} </code><button class=\"linkish\" onclick=\"wasm_exports.fuzzSelectSli({d});\">View</button>", .{
112
+ try out.print(gpa, ":{d}:{d} </code><button class=\"linkish\" onclick=\"wasm_exports.fuzzSelectSli({d});\">View</button>", .{
113
113
  sl.line,
114
114
  sl.column,
115
115
  @intFromEnum(sli),
116
116
  });
117
117
  }
118
118
 
119
- fn appendPath(sli: SourceLocationIndex, out: *std.ArrayListUnmanaged(u8)) Allocator.Error!void {
119
+ fn appendPath(sli: SourceLocationIndex, out: *std.ArrayList(u8)) error{OutOfMemory}!void {
120
120
  const sl = sli.ptr();
121
121
  const file = coverage.fileAt(sl.file);
122
122
  const file_name = coverage.stringAt(file.basename);
@@ -294,7 +294,7 @@ fn updateStats() error{OutOfMemory}!void {
294
294
  }
295
295
 
296
296
  fn updateEntryPoints() error{OutOfMemory}!void {
297
- var html: std.ArrayListUnmanaged(u8) = .empty;
297
+ var html: std.ArrayList(u8) = .empty;
298
298
  defer html.deinit(gpa);
299
299
  for (entry_points.items) |sli| {
300
300
  try html.appendSlice(gpa, "<li>");
@@ -44,7 +44,7 @@ pub fn genericResultMessage(msg_bytes: []u8) error{OutOfMemory}!void {
44
44
  js.updateGeneric(msg.step_idx, inner_html.ptr, inner_html.len);
45
45
  }
46
46
 
47
- pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void {
47
+ pub fn compileResultMessage(msg_bytes: []u8) error{ OutOfMemory, WriteFailed }!void {
48
48
  const max_table_rows = 500;
49
49
 
50
50
  if (msg_bytes.len < @sizeOf(abi.CompileResult)) @panic("malformed CompileResult message");
@@ -166,10 +166,11 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void {
166
166
  });
167
167
  defer gpa.free(inner_html);
168
168
 
169
- var file_table_html: std.ArrayListUnmanaged(u8) = .empty;
170
- defer file_table_html.deinit(gpa);
169
+ var file_table_html: std.Io.Writer.Allocating = .init(gpa);
170
+ defer file_table_html.deinit();
171
+
171
172
  for (slowest_files[0..@min(max_table_rows, slowest_files.len)]) |file| {
172
- try file_table_html.writer(gpa).print(
173
+ try file_table_html.writer.print(
173
174
  \\<tr>
174
175
  \\ <th scope="row"><code>{f}</code></th>
175
176
  \\ <td>{D}</td>
@@ -187,17 +188,17 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void {
187
188
  });
188
189
  }
189
190
  if (slowest_files.len > max_table_rows) {
190
- try file_table_html.writer(gpa).print(
191
+ try file_table_html.writer.print(
191
192
  \\<tr><td colspan="4">{d} more rows omitted</td></tr>
192
193
  \\
193
194
  , .{slowest_files.len - max_table_rows});
194
195
  }
195
196
 
196
- var decl_table_html: std.ArrayListUnmanaged(u8) = .empty;
197
- defer decl_table_html.deinit(gpa);
197
+ var decl_table_html: std.Io.Writer.Allocating = .init(gpa);
198
+ defer decl_table_html.deinit();
198
199
 
199
200
  for (slowest_decls[0..@min(max_table_rows, slowest_decls.len)]) |decl| {
200
- try decl_table_html.writer(gpa).print(
201
+ try decl_table_html.writer.print(
201
202
  \\<tr>
202
203
  \\ <th scope="row"><code>{f}</code></th>
203
204
  \\ <th scope="row"><code>{f}</code></th>
@@ -219,7 +220,7 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void {
219
220
  });
220
221
  }
221
222
  if (slowest_decls.len > max_table_rows) {
222
- try decl_table_html.writer(gpa).print(
223
+ try decl_table_html.writer.print(
223
224
  \\<tr><td colspan="6">{d} more rows omitted</td></tr>
224
225
  \\
225
226
  , .{slowest_decls.len - max_table_rows});
@@ -229,10 +230,10 @@ pub fn compileResultMessage(msg_bytes: []u8) error{OutOfMemory}!void {
229
230
  hdr.step_idx,
230
231
  inner_html.ptr,
231
232
  inner_html.len,
232
- file_table_html.items.ptr,
233
- file_table_html.items.len,
234
- decl_table_html.items.ptr,
235
- decl_table_html.items.len,
233
+ file_table_html.written().ptr,
234
+ file_table_html.written().len,
235
+ decl_table_html.written().ptr,
236
+ decl_table_html.written().len,
236
237
  hdr.flags.use_llvm,
237
238
  );
238
239
  }
@@ -501,6 +501,10 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
501
501
  .@"asm",
502
502
  => return walkAsm(w, ast.fullAsm(node).?),
503
503
 
504
+ .asm_legacy => {
505
+ return walkAsmLegacy(w, ast.legacyAsm(node).?);
506
+ },
507
+
504
508
  .enum_literal => {
505
509
  return walkIdentifier(w, ast.nodeMainToken(node)); // name
506
510
  },
@@ -665,7 +669,7 @@ fn walkStructInit(
665
669
 
666
670
  fn walkCall(w: *Walk, call: Ast.full.Call) Error!void {
667
671
  try walkExpression(w, call.ast.fn_expr);
668
- try walkParamList(w, call.ast.params);
672
+ try walkExpressions(w, call.ast.params);
669
673
  }
670
674
 
671
675
  fn walkSlice(
@@ -830,7 +834,7 @@ fn walkWhile(w: *Walk, node_index: Ast.Node.Index, while_node: Ast.full.While) E
830
834
  }
831
835
 
832
836
  fn walkFor(w: *Walk, for_node: Ast.full.For) Error!void {
833
- try walkParamList(w, for_node.ast.inputs);
837
+ try walkExpressions(w, for_node.ast.inputs);
834
838
  try walkExpression(w, for_node.ast.then_expr);
835
839
  if (for_node.ast.else_expr.unwrap()) |else_expr| {
836
840
  try walkExpression(w, else_expr);
@@ -874,15 +878,12 @@ fn walkIf(w: *Walk, node_index: Ast.Node.Index, if_node: Ast.full.If) Error!void
874
878
 
875
879
  fn walkAsm(w: *Walk, asm_node: Ast.full.Asm) Error!void {
876
880
  try walkExpression(w, asm_node.ast.template);
877
- for (asm_node.ast.items) |item| {
878
- try walkExpression(w, item);
879
- }
881
+ try walkExpressions(w, asm_node.ast.items);
880
882
  }
881
883
 
882
- fn walkParamList(w: *Walk, params: []const Ast.Node.Index) Error!void {
883
- for (params) |param_node| {
884
- try walkExpression(w, param_node);
885
- }
884
+ fn walkAsmLegacy(w: *Walk, asm_node: Ast.full.AsmLegacy) Error!void {
885
+ try walkExpression(w, asm_node.ast.template);
886
+ try walkExpressions(w, asm_node.ast.items);
886
887
  }
887
888
 
888
889
  /// Check if it is already gutted (i.e. its body replaced with `@trap()`).
@@ -114,10 +114,10 @@ pub fn main() !void {
114
114
  interestingness_argv.appendAssumeCapacity(checker_path);
115
115
  interestingness_argv.appendSliceAssumeCapacity(argv);
116
116
 
117
- var rendered = std.array_list.Managed(u8).init(gpa);
117
+ var rendered: std.Io.Writer.Allocating = .init(gpa);
118
118
  defer rendered.deinit();
119
119
 
120
- var astgen_input = std.array_list.Managed(u8).init(gpa);
120
+ var astgen_input: std.Io.Writer.Allocating = .init(gpa);
121
121
  defer astgen_input.deinit();
122
122
 
123
123
  var tree = try parse(gpa, root_source_file_path);
@@ -138,10 +138,10 @@ pub fn main() !void {
138
138
  }
139
139
  }
140
140
 
141
- var fixups: Ast.Fixups = .{};
141
+ var fixups: Ast.Render.Fixups = .{};
142
142
  defer fixups.deinit(gpa);
143
143
 
144
- var more_fixups: Ast.Fixups = .{};
144
+ var more_fixups: Ast.Render.Fixups = .{};
145
145
  defer more_fixups.deinit(gpa);
146
146
 
147
147
  var rng = std.Random.DefaultPrng.init(seed);
@@ -188,15 +188,14 @@ pub fn main() !void {
188
188
  try transformationsToFixups(gpa, arena, root_source_file_path, this_set, &fixups);
189
189
 
190
190
  rendered.clearRetainingCapacity();
191
- try tree.renderToArrayList(&rendered, fixups);
191
+ try tree.render(gpa, &rendered.writer, fixups);
192
192
 
193
193
  // The transformations we applied may have resulted in unused locals,
194
194
  // in which case we would like to add the respective discards.
195
195
  {
196
- try astgen_input.resize(rendered.items.len);
197
- @memcpy(astgen_input.items, rendered.items);
198
- try astgen_input.append(0);
199
- const source_with_null = astgen_input.items[0 .. astgen_input.items.len - 1 :0];
196
+ try astgen_input.writer.writeAll(rendered.written());
197
+ try astgen_input.writer.writeByte(0);
198
+ const source_with_null = astgen_input.written()[0..(astgen_input.written().len - 1) :0];
200
199
  var astgen_tree = try Ast.parse(gpa, source_with_null, .zig);
201
200
  defer astgen_tree.deinit(gpa);
202
201
  if (astgen_tree.errors.len != 0) {
@@ -228,12 +227,12 @@ pub fn main() !void {
228
227
  }
229
228
  if (more_fixups.count() != 0) {
230
229
  rendered.clearRetainingCapacity();
231
- try astgen_tree.renderToArrayList(&rendered, more_fixups);
230
+ try astgen_tree.render(gpa, &rendered.writer, more_fixups);
232
231
  }
233
232
  }
234
233
  }
235
234
 
236
- try std.fs.cwd().writeFile(.{ .sub_path = root_source_file_path, .data = rendered.items });
235
+ try std.fs.cwd().writeFile(.{ .sub_path = root_source_file_path, .data = rendered.written() });
237
236
  // std.debug.print("trying this code:\n{s}\n", .{rendered.items});
238
237
 
239
238
  const interestingness = try runCheck(arena, interestingness_argv.items);
@@ -273,8 +272,8 @@ pub fn main() !void {
273
272
  // Revert the source back to not be transformed.
274
273
  fixups.clearRetainingCapacity();
275
274
  rendered.clearRetainingCapacity();
276
- try tree.renderToArrayList(&rendered, fixups);
277
- try std.fs.cwd().writeFile(.{ .sub_path = root_source_file_path, .data = rendered.items });
275
+ try tree.render(gpa, &rendered.writer, fixups);
276
+ try std.fs.cwd().writeFile(.{ .sub_path = root_source_file_path, .data = rendered.written() });
278
277
 
279
278
  return std.process.cleanExit();
280
279
  }
@@ -318,7 +317,7 @@ fn transformationsToFixups(
318
317
  arena: Allocator,
319
318
  root_source_file_path: []const u8,
320
319
  transforms: []const Walk.Transformation,
321
- fixups: *Ast.Fixups,
320
+ fixups: *Ast.Render.Fixups,
322
321
  ) !void {
323
322
  fixups.clearRetainingCapacity();
324
323
 
@@ -359,7 +358,7 @@ fn transformationsToFixups(
359
358
  other_file_ast.deinit(gpa);
360
359
  }
361
360
 
362
- var inlined_fixups: Ast.Fixups = .{};
361
+ var inlined_fixups: Ast.Render.Fixups = .{};
363
362
  defer inlined_fixups.deinit(gpa);
364
363
  if (std.fs.path.dirname(inline_imported_file.imported_string)) |dirname| {
365
364
  inlined_fixups.rebase_imported_paths = dirname;
@@ -382,16 +381,16 @@ fn transformationsToFixups(
382
381
  }
383
382
  }
384
383
 
385
- var other_source = std.array_list.Managed(u8).init(gpa);
384
+ var other_source: std.io.Writer.Allocating = .init(gpa);
386
385
  defer other_source.deinit();
387
- try other_source.appendSlice("struct {\n");
388
- try other_file_ast.renderToArrayList(&other_source, inlined_fixups);
389
- try other_source.appendSlice("}");
386
+ try other_source.writer.writeAll("struct {\n");
387
+ try other_file_ast.render(gpa, &other_source.writer, inlined_fixups);
388
+ try other_source.writer.writeAll("}");
390
389
 
391
390
  try fixups.replace_nodes_with_string.put(
392
391
  gpa,
393
392
  inline_imported_file.builtin_call_node,
394
- try arena.dupe(u8, other_source.items),
393
+ try arena.dupe(u8, other_source.written()),
395
394
  );
396
395
  },
397
396
  };
@@ -674,7 +674,7 @@ pub const Compiler = struct {
674
674
  }
675
675
 
676
676
  try file_reader.seekTo(entry.data_offset_from_start_of_file);
677
- var header_bytes = (file_reader.interface.takeArray(16) catch {
677
+ var header_bytes: [16]u8 align(@alignOf(ico.BitmapHeader)) = (file_reader.interface.takeArray(16) catch {
678
678
  return self.iconReadError(
679
679
  error.UnexpectedEOF,
680
680
  filename_utf8,
@@ -37,7 +37,7 @@ comptime {
37
37
  @export(&__aeabi_memclr4, .{ .name = "__aeabi_memclr4", .linkage = common.linkage, .visibility = common.visibility });
38
38
  @export(&__aeabi_memclr8, .{ .name = "__aeabi_memclr8", .linkage = common.linkage, .visibility = common.visibility });
39
39
 
40
- if (builtin.os.tag == .linux) {
40
+ if (builtin.os.tag == .linux or builtin.os.tag == .freebsd) {
41
41
  @export(&__aeabi_read_tp, .{ .name = "__aeabi_read_tp", .linkage = common.linkage, .visibility = common.visibility });
42
42
  }
43
43
 
package/init/build.zig CHANGED
@@ -44,7 +44,7 @@ pub fn build(b: *std.Build) void {
44
44
  // Here we define an executable. An executable needs to have a root module
45
45
  // which needs to expose a `main` function. While we could add a main function
46
46
  // to the module defined above, it's sometimes preferable to split business
47
- // business logic and the CLI into two separate modules.
47
+ // logic and the CLI into two separate modules.
48
48
  //
49
49
  // If your goal is to create a Zig library for others to use, consider if
50
50
  // it might benefit from also exposing a CLI tool. A parser library for a
@@ -101,10 +101,13 @@ extern char *inet_nsap_ntoa (int __len, const unsigned char *__cp,
101
101
  char *__buf) __THROW;
102
102
  #endif
103
103
 
104
+ // zig patch: inet was fortified in glibc 2.42
105
+ #if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 42) || __GLIBC__ > 2
104
106
  #if __USE_FORTIFY_LEVEL > 0 && defined __fortify_function
105
107
  /* Include functions with security checks. */
106
108
  # include <bits/inet-fortified.h>
107
109
  #endif
110
+ #endif
108
111
 
109
112
  __END_DECLS
110
113
 
@@ -0,0 +1,3 @@
1
+ #ifdef __loongarch_soft_float
2
+ #include "../fenv.c"
3
+ #endif
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zigc/lib",
3
- "version": "0.15.1",
3
+ "version": "0.15.2-test.99",
4
4
  "description": "Zig standard library and libc headers (shared across all platforms)",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1827,7 +1827,26 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
1827
1827
  _ = try std.fmt.bufPrint(&args_hex_hash, "{x}", .{&args_hash});
1828
1828
 
1829
1829
  const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash;
1830
- try b.cache_root.handle.writeFile(.{ .sub_path = args_file, .data = args });
1830
+ if (b.cache_root.handle.access(args_file, .{})) |_| {
1831
+ // The args file is already present from a previous run.
1832
+ } else |err| switch (err) {
1833
+ error.FileNotFound => {
1834
+ try b.cache_root.handle.makePath("tmp");
1835
+ const rand_int = std.crypto.random.int(u64);
1836
+ const tmp_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);
1837
+ try b.cache_root.handle.writeFile(.{ .sub_path = tmp_path, .data = args });
1838
+ defer b.cache_root.handle.deleteFile(tmp_path) catch {
1839
+ // It's fine if the temporary file can't be cleaned up.
1840
+ };
1841
+ b.cache_root.handle.rename(tmp_path, args_file) catch |rename_err| switch (rename_err) {
1842
+ error.PathAlreadyExists => {
1843
+ // The args file was created by another concurrent build process.
1844
+ },
1845
+ else => |other_err| return other_err,
1846
+ };
1847
+ },
1848
+ else => |other_err| return other_err,
1849
+ }
1831
1850
 
1832
1851
  const resolved_args_file = try mem.concat(arena, u8, &.{
1833
1852
  "@",
@@ -163,6 +163,12 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
163
163
  try argv_list.append("-fno-clang");
164
164
  }
165
165
 
166
+ try argv_list.append("--cache-dir");
167
+ try argv_list.append(b.cache_root.path orelse ".");
168
+
169
+ try argv_list.append("--global-cache-dir");
170
+ try argv_list.append(b.graph.global_cache_root.path orelse ".");
171
+
166
172
  try argv_list.append("--listen=-");
167
173
 
168
174
  if (!translate_c.target.query.isNative()) {
@@ -323,7 +323,7 @@ fn serveWebSocket(ws: *WebServer, sock: *http.Server.WebSocket) !noreturn {
323
323
  // Temporarily unlock, then re-lock after the message is sent.
324
324
  ws.time_report_mutex.unlock();
325
325
  defer ws.time_report_mutex.lock();
326
- try sock.writeMessage(msg, .binary);
326
+ try sock.writeMessage(owned_msg, .binary);
327
327
  }
328
328
  }
329
329
 
package/std/Build.zig CHANGED
@@ -2524,7 +2524,10 @@ pub const LazyPath = union(enum) {
2524
2524
  .up = gen.up,
2525
2525
  .sub_path = dupePathInner(allocator, gen.sub_path),
2526
2526
  } },
2527
- .dependency => |dep| .{ .dependency = dep },
2527
+ .dependency => |dep| .{ .dependency = .{
2528
+ .dependency = dep.dependency,
2529
+ .sub_path = dupePathInner(allocator, dep.sub_path),
2530
+ } },
2528
2531
  };
2529
2532
  }
2530
2533
  };
@@ -27,6 +27,7 @@ pub fn init(reader: *Reader, limit: Limit, buffer: []u8) Limited {
27
27
 
28
28
  fn stream(r: *Reader, w: *Writer, limit: Limit) Reader.StreamError!usize {
29
29
  const l: *Limited = @fieldParentPtr("interface", r);
30
+ if (l.remaining == .nothing) return error.EndOfStream;
30
31
  const combined_limit = limit.min(l.remaining);
31
32
  const n = try l.unlimited.stream(w, combined_limit);
32
33
  l.remaining = l.remaining.subtract(n).?;
@@ -51,8 +52,51 @@ test stream {
51
52
 
52
53
  fn discard(r: *Reader, limit: Limit) Reader.Error!usize {
53
54
  const l: *Limited = @fieldParentPtr("interface", r);
55
+ if (l.remaining == .nothing) return error.EndOfStream;
54
56
  const combined_limit = limit.min(l.remaining);
55
57
  const n = try l.unlimited.discard(combined_limit);
56
58
  l.remaining = l.remaining.subtract(n).?;
57
59
  return n;
58
60
  }
61
+
62
+ test "end of stream, read, hit limit exactly" {
63
+ var f: Reader = .fixed("i'm dying");
64
+ var l = f.limited(.limited(4), &.{});
65
+ const r = &l.interface;
66
+
67
+ var buf: [2]u8 = undefined;
68
+ try r.readSliceAll(&buf);
69
+ try r.readSliceAll(&buf);
70
+ try std.testing.expectError(error.EndOfStream, l.interface.readSliceAll(&buf));
71
+ }
72
+
73
+ test "end of stream, read, hit limit after partial read" {
74
+ var f: Reader = .fixed("i'm dying");
75
+ var l = f.limited(.limited(5), &.{});
76
+ const r = &l.interface;
77
+
78
+ var buf: [2]u8 = undefined;
79
+ try r.readSliceAll(&buf);
80
+ try r.readSliceAll(&buf);
81
+ try std.testing.expectError(error.EndOfStream, l.interface.readSliceAll(&buf));
82
+ }
83
+
84
+ test "end of stream, discard, hit limit exactly" {
85
+ var f: Reader = .fixed("i'm dying");
86
+ var l = f.limited(.limited(4), &.{});
87
+ const r = &l.interface;
88
+
89
+ try r.discardAll(2);
90
+ try r.discardAll(2);
91
+ try std.testing.expectError(error.EndOfStream, l.interface.discardAll(2));
92
+ }
93
+
94
+ test "end of stream, discard, hit limit after partial read" {
95
+ var f: Reader = .fixed("i'm dying");
96
+ var l = f.limited(.limited(5), &.{});
97
+ const r = &l.interface;
98
+
99
+ try r.discardAll(2);
100
+ try r.discardAll(2);
101
+ try std.testing.expectError(error.EndOfStream, l.interface.discardAll(2));
102
+ }