@zigc/lib 0.17.0-dev.657 → 0.17.0-dev.690

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.
@@ -194,7 +194,7 @@ pub fn main(init: process.Init.Minimal) !void {
194
194
  var max_rss: u64 = 0;
195
195
  var skip_oom_steps = false;
196
196
  var test_timeout_ns: ?u64 = null;
197
- var color: Color = .auto;
197
+ var color: Color = .settingFromEnvironment(&graph.environ_map);
198
198
  var watch = false;
199
199
  var fuzz: ?Fuzz.Mode = null;
200
200
  var debounce_interval_ms: u16 = 50;
@@ -533,7 +533,7 @@ pub fn main(init: process.Init.Minimal) !void {
533
533
  }
534
534
 
535
535
  const main_progress_node = std.Progress.start(io, .{
536
- .disable_printing = (color == .off),
536
+ .disable_printing = (graph.stderr_mode.? == .no_color),
537
537
  });
538
538
  defer main_progress_node.end();
539
539
 
@@ -32,7 +32,7 @@ struct __pthread_mutex_s
32
32
  int __kind;
33
33
  #if __WORDSIZE == 64
34
34
  short __spins;
35
- short __unused;
35
+ short __glibc_reserved;
36
36
  __pthread_list_t __list;
37
37
  # define __PTHREAD_MUTEX_HAVE_PREV 1
38
38
  #else
@@ -59,4 +59,4 @@ struct __pthread_mutex_s
59
59
  0, 0, 0, __kind, 0, { { 0, 0 } }
60
60
  #endif
61
61
 
62
- #endif
62
+ #endif
@@ -76,7 +76,7 @@ typedef unsigned int fexcept_t; /* size of fpc */
76
76
  typedef struct
77
77
  {
78
78
  fexcept_t __fpc;
79
- void *__unused;
79
+ void *__glibc_reserved;
80
80
  /* The field __unused (formerly __ieee_instruction_pointer) is a relict from
81
81
  commit "Remove PTRACE_PEEKUSER" (87b9b50f0d4b92248905e95a06a13c513dc45e59)
82
82
  and isn't used anymore. */
@@ -96,4 +96,4 @@ typedef unsigned int femode_t;
96
96
 
97
97
  /* Default floating-point control modes. */
98
98
  # define FE_DFL_MODE ((const femode_t *) -1L)
99
- #endif
99
+ #endif
@@ -32,7 +32,7 @@ struct __pthread_mutex_s
32
32
  int __kind;
33
33
  #if __WORDSIZE == 64
34
34
  short __spins;
35
- short __unused;
35
+ short __glibc_reserved;
36
36
  __pthread_list_t __list;
37
37
  # define __PTHREAD_MUTEX_HAVE_PREV 1
38
38
  #else
@@ -59,4 +59,4 @@ struct __pthread_mutex_s
59
59
  0, 0, 0, __kind, 0, { { 0, 0 } }
60
60
  #endif
61
61
 
62
- #endif
62
+ #endif
@@ -32,7 +32,7 @@ struct __pthread_mutex_s
32
32
  int __kind;
33
33
  #ifdef __x86_64__
34
34
  short __spins;
35
- short __unused;
35
+ short __glibc_reserved;
36
36
  __pthread_list_t __list;
37
37
  # define __PTHREAD_MUTEX_HAVE_PREV 1
38
38
  #else
@@ -59,4 +59,4 @@ struct __pthread_mutex_s
59
59
  0, 0, 0, __kind, 0, { { 0, 0 } }
60
60
  #endif
61
61
 
62
- #endif
62
+ #endif
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zigc/lib",
3
- "version": "0.17.0-dev.657",
3
+ "version": "0.17.0-dev.690",
4
4
  "description": "Zig standard library and libc headers (shared across all platforms)",
5
5
  "repository": {
6
6
  "type": "git",
@@ -178,22 +178,15 @@ pub fn formatEscapeChar(path: Path, writer: *Io.Writer) Io.Writer.Error!void {
178
178
 
179
179
  pub fn format(self: Path, writer: *Io.Writer) Io.Writer.Error!void {
180
180
  if (Io.Dir.path.isAbsolute(self.sub_path)) {
181
- try writer.writeAll(self.sub_path);
182
- return;
181
+ return writer.writeAll(self.sub_path);
182
+ } else if (self.root_dir.path) |p| {
183
+ var bufs: [3][]const u8 = .{ p, Io.Dir.path.sep_str, self.sub_path };
184
+ return writer.writeVecAll(bufs[0..if (self.sub_path.len > 0) 3 else 1]);
185
+ } else if (self.sub_path.len > 0) {
186
+ return writer.writeAll(self.sub_path);
187
+ } else {
188
+ return writer.writeByte('.');
183
189
  }
184
- if (self.root_dir.path) |p| {
185
- try writer.writeAll(p);
186
- if (self.sub_path.len > 0) {
187
- try writer.writeAll(Io.Dir.path.sep_str);
188
- try writer.writeAll(self.sub_path);
189
- }
190
- return;
191
- }
192
- if (self.sub_path.len > 0) {
193
- try writer.writeAll(self.sub_path);
194
- return;
195
- }
196
- try writer.writeByte('.');
197
190
  }
198
191
 
199
192
  pub fn eql(self: Path, other: Path) bool {
@@ -312,7 +312,7 @@ pub fn BlockVec(comptime blocks_count: comptime_int) type {
312
312
  }
313
313
 
314
314
  /// Apply the bitwise OR operation to the content of two block vectors.
315
- pub fn orBlocks(block_vec1: Self, block_vec2: Block) Self {
315
+ pub fn orBlocks(block_vec1: Self, block_vec2: Self) Self {
316
316
  var out: Self = undefined;
317
317
  inline for (0..native_words) |i| {
318
318
  out.repr[i] = block_vec1.repr[i] | block_vec2.repr[i];
@@ -216,10 +216,10 @@ pub fn BlockVec(comptime blocks_count: comptime_int) type {
216
216
  }
217
217
 
218
218
  /// XOR the block vector with a byte sequence.
219
- pub fn xorBytes(block_vec: Self, bytes: *const [blocks_count * 16]u8) [32]u8 {
220
- var out: Self = undefined;
219
+ pub fn xorBytes(block_vec: Self, bytes: *const [blocks_count * 16]u8) [blocks_count * 16]u8 {
220
+ var out: [blocks_count * 16]u8 = undefined;
221
221
  inline for (0..native_words) |i| {
222
- out.repr[i] = block_vec.repr[i].xorBytes(bytes[i * native_word_size ..][0..native_word_size]);
222
+ out[i * native_word_size ..][0..native_word_size].* = block_vec.repr[i].xorBytes(bytes[i * native_word_size ..][0..native_word_size]);
223
223
  }
224
224
  return out;
225
225
  }
@@ -279,7 +279,7 @@ pub fn BlockVec(comptime blocks_count: comptime_int) type {
279
279
  }
280
280
 
281
281
  /// Apply the bitwise OR operation to the content of two block vectors.
282
- pub fn orBlocks(block_vec1: Self, block_vec2: Block) Self {
282
+ pub fn orBlocks(block_vec1: Self, block_vec2: Self) Self {
283
283
  var out: Self = undefined;
284
284
  inline for (0..native_words) |i| {
285
285
  out.repr[i] = block_vec1.repr[i].orBlocks(block_vec2.repr[i]);
@@ -391,10 +391,10 @@ pub fn BlockVec(comptime blocks_count: comptime_int) type {
391
391
  }
392
392
 
393
393
  /// XOR the block vector with a byte sequence.
394
- pub fn xorBytes(block_vec: Self, bytes: *const [blocks_count * 16]u8) [32]u8 {
395
- var out: Self = undefined;
394
+ pub fn xorBytes(block_vec: Self, bytes: *const [blocks_count * 16]u8) [blocks_count * 16]u8 {
395
+ var out: [blocks_count * 16]u8 = undefined;
396
396
  for (0..native_words) |i| {
397
- out.repr[i] = block_vec.repr[i].xorBytes(bytes[i * native_word_size ..][0..native_word_size]);
397
+ out[i * native_word_size ..][0..native_word_size].* = block_vec.repr[i].xorBytes(bytes[i * native_word_size ..][0..native_word_size]);
398
398
  }
399
399
  return out;
400
400
  }
@@ -454,7 +454,7 @@ pub fn BlockVec(comptime blocks_count: comptime_int) type {
454
454
  }
455
455
 
456
456
  /// Apply the bitwise OR operation to the content of two block vectors.
457
- pub fn orBlocks(block_vec1: Self, block_vec2: Block) Self {
457
+ pub fn orBlocks(block_vec1: Self, block_vec2: Self) Self {
458
458
  var out: Self = undefined;
459
459
  for (0..native_words) |i| {
460
460
  out.repr[i] = block_vec1.repr[i].orBlocks(block_vec2.repr[i]);
@@ -137,6 +137,18 @@ test "BlockVec invMixColumns" {
137
137
  }
138
138
  }
139
139
 
140
+ test "BlockVec bitwise operations" {
141
+ const a_bytes: [32]u8 = @splat(0xaa);
142
+ const b_bytes: [32]u8 = @splat(0xbb);
143
+ const a = BlockVec(2).fromBytes(&a_bytes);
144
+ const b = BlockVec(2).fromBytes(&b_bytes);
145
+
146
+ try testing.expectEqual(@as([32]u8, @splat(0x11)), a.xorBytes(&b_bytes));
147
+ try testing.expectEqual(@as([32]u8, @splat(0x11)), a.xorBlocks(b).toBytes());
148
+ try testing.expectEqual(@as([32]u8, @splat(0xbb)), a.orBlocks(b).toBytes());
149
+ try testing.expectEqual(@as([32]u8, @splat(0xaa)), a.andBlocks(b).toBytes());
150
+ }
151
+
140
152
  test "expand 256-bit key" {
141
153
  const key = [_]u8{
142
154
  0x60, 0x3d, 0xeb, 0x10,
@@ -226,9 +226,10 @@ fn AesSiv(comptime Aes: anytype) type {
226
226
 
227
227
  /// Encrypts plaintext with multiple associated data components.
228
228
  /// This is the most general form of AES-SIV encryption that accepts
229
- /// an arbitrary vector of associated data strings as specified in RFC 5297.
229
+ /// a vector of up to 126 associated data strings as specified in RFC 5297.
230
230
  pub fn encryptWithAdVector(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const []const u8, key: [key_length]u8) void {
231
231
  debug.assert(c.len == m.len);
232
+ debug.assert(ad.len <= 126); // AES-SIV supports at most 126 associated data components
232
233
 
233
234
  // Split key into K1 (for S2V) and K2 (for CTR)
234
235
  const k1 = key[0 .. Aes.key_bits / 8];
@@ -260,9 +261,10 @@ fn AesSiv(comptime Aes: anytype) type {
260
261
 
261
262
  /// Decrypts ciphertext with multiple associated data components.
262
263
  /// This is the most general form of AES-SIV decryption that accepts
263
- /// an arbitrary vector of associated data strings as specified in RFC 5297.
264
+ /// a vector of up to 126 associated data strings as specified in RFC 5297.
264
265
  pub fn decryptWithAdVector(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const []const u8, key: [key_length]u8) AuthenticationError!void {
265
266
  assert(c.len == m.len);
267
+ assert(ad.len <= 126); // AES-SIV supports at most 126 associated data components
266
268
 
267
269
  // Split key into K1 (for S2V) and K2 (for CTR)
268
270
  const k1 = key[0 .. Aes.key_bits / 8];
@@ -47,7 +47,7 @@ test fromDot {
47
47
  }
48
48
  }
49
49
 
50
- pub fn toDot(self: Oid, writer: anytype) @TypeOf(writer).Error!void {
50
+ pub fn toDot(self: Oid, writer: *std.Io.Writer) std.Io.Writer.Error!void {
51
51
  const encoded = self.encoded;
52
52
  const first = @divTrunc(encoded[0], 40);
53
53
  const second = encoded[0] - first * 40;
@@ -81,7 +81,7 @@ test toDot {
81
81
  for (test_cases) |t| {
82
82
  var stream: std.Io.Writer = .fixed(&buf);
83
83
  try toDot(Oid{ .encoded = t.encoded }, &stream);
84
- try std.testing.expectEqualStrings(t.dot_notation, stream.written());
84
+ try std.testing.expectEqualStrings(t.dot_notation, stream.buffered());
85
85
  }
86
86
  }
87
87
 
@@ -111,21 +111,33 @@ pub fn view(self: Decoder, elem: Element) []const u8 {
111
111
  }
112
112
 
113
113
  fn int(comptime T: type, value: []const u8) error{ NonCanonical, LargeValue }!T {
114
- if (@typeInfo(T).int.bits % 8 != 0) @compileError("T must be byte aligned");
115
-
116
- var bytes = value;
117
- if (bytes.len >= 2) {
118
- if (bytes[0] == 0) {
119
- if (@clz(bytes[1]) > 0) return error.NonCanonical;
120
- bytes.ptr += 1;
121
- }
122
- if (bytes[0] == 0xff and @clz(bytes[1]) == 0) return error.NonCanonical;
114
+ const info = @typeInfo(T).int;
115
+ if (info.bits % 8 != 0) @compileError("T must be byte aligned");
116
+
117
+ if (value.len == 0) return error.NonCanonical;
118
+ if (value.len >= 2) {
119
+ if (value[0] == 0x00 and value[1] & 0x80 == 0) return error.NonCanonical;
120
+ if (value[0] == 0xff and value[1] & 0x80 != 0) return error.NonCanonical;
123
121
  }
124
122
 
125
- if (bytes.len > @sizeOf(T)) return error.LargeValue;
126
- if (@sizeOf(T) == 1) return @bitCast(bytes[0]);
123
+ const had_sign_byte = value.len >= 2 and value[0] == 0x00;
124
+ const bytes = if (had_sign_byte) value[1..] else value;
125
+ const der_negative = !had_sign_byte and bytes[0] & 0x80 != 0;
126
+
127
+ switch (info.signedness) {
128
+ .unsigned => {
129
+ if (der_negative) return error.LargeValue;
130
+ if (bytes.len > @sizeOf(T)) return error.LargeValue;
131
+ },
132
+ .signed => {
133
+ const max_len: usize = if (had_sign_byte) @sizeOf(T) - 1 else @sizeOf(T);
134
+ if (bytes.len > max_len) return error.LargeValue;
135
+ },
136
+ }
127
137
 
128
- return std.mem.readVarInt(T, bytes, .big);
138
+ var buf: [@sizeOf(T)]u8 = @splat(if (der_negative) 0xff else 0);
139
+ @memcpy(buf[buf.len - bytes.len ..], bytes);
140
+ return std.mem.readInt(T, &buf, .big);
129
141
  }
130
142
 
131
143
  test int {
@@ -135,7 +147,26 @@ test int {
135
147
 
136
148
  const big = [_]u8{ 0xef, 0xff };
137
149
  try expectError(error.LargeValue, int(u8, &big));
138
- try expectEqual(0xefff, int(u16, &big));
150
+ try expectError(error.LargeValue, int(u16, &big));
151
+ try expectEqual(@as(i16, -4097), try int(i16, &big));
152
+
153
+ try expectEqual(@as(u16, 255), try int(u16, &.{ 0x00, 0xff }));
154
+ try expectEqual(@as(u16, 0x8000), try int(u16, &.{ 0x00, 0x80, 0x00 }));
155
+
156
+ try expectEqual(@as(i8, -1), try int(i8, &.{0xff}));
157
+ try expectEqual(@as(i16, -1), try int(i16, &.{0xff}));
158
+ try expectEqual(@as(i16, -128), try int(i16, &.{0x80}));
159
+ try expectEqual(@as(i16, -129), try int(i16, &.{ 0xff, 0x7f }));
160
+ try expectEqual(@as(i16, 255), try int(i16, &.{ 0x00, 0xff }));
161
+ try expectEqual(@as(i32, 0x7fffffff), try int(i32, &.{ 0x7f, 0xff, 0xff, 0xff }));
162
+
163
+ try expectError(error.LargeValue, int(i8, &.{ 0x00, 0xff }));
164
+ try expectError(error.LargeValue, int(i16, &.{ 0x00, 0x80, 0x00 }));
165
+ try expectError(error.LargeValue, int(i32, &.{ 0x00, 0x80, 0x00, 0x00, 0x00 }));
166
+
167
+ try expectError(error.LargeValue, int(u8, &.{0xff}));
168
+ try expectError(error.LargeValue, int(u16, &.{0x80}));
169
+ try expectError(error.LargeValue, int(u32, &.{ 0x80, 0x00, 0x00, 0x00 }));
139
170
  }
140
171
 
141
172
  test Decoder {
@@ -24,6 +24,7 @@ pub fn any(self: *Encoder, val: anytype) !void {
24
24
  fn anyTag(self: *Encoder, tag_: Tag, val: anytype) !void {
25
25
  const T = @TypeOf(val);
26
26
  if (std.meta.hasFn(T, "encodeDer")) return try val.encodeDer(self);
27
+ const outer_field_tag = self.field_tag;
27
28
  const start = self.buffer.data.len;
28
29
  const merged_tag = self.mergedTag(tag_);
29
30
 
@@ -42,8 +43,9 @@ fn anyTag(self: *Encoder, tag_: Tag, val: anytype) !void {
42
43
  const is_default = if (f_attrs.@"comptime") false else if (f_attrs.defaultValue(f_type)) |default_val| brk: {
43
44
  break :brk std.mem.eql(u8, std.mem.asBytes(&default_val), std.mem.asBytes(&field_val));
44
45
  } else false;
46
+ const is_null_optional = if (@typeInfo(f_type) == .optional) field_val == null else false;
45
47
 
46
- if (!is_default) {
48
+ if (!is_default and !is_null_optional) {
47
49
  const start2 = self.buffer.data.len;
48
50
  self.field_tag = field_tag;
49
51
  // will merge with self.field_tag.
@@ -58,6 +60,7 @@ fn anyTag(self: *Encoder, tag_: Tag, val: anytype) !void {
58
60
  }
59
61
  }
60
62
  }
63
+ self.field_tag = outer_field_tag;
61
64
  },
62
65
  .bool => try self.buffer.prependSlice(&[_]u8{if (val) 0xff else 0}),
63
66
  .int => try self.int(T, val),
@@ -68,7 +71,7 @@ fn anyTag(self: *Encoder, tag_: Tag, val: anytype) !void {
68
71
  try self.int(e.tag_type, @intFromEnum(val));
69
72
  }
70
73
  },
71
- .optional => if (val) |v| return try self.anyTag(tag_, v),
74
+ .optional => if (val) |v| return try self.anyTag(tag_, v) else return,
72
75
  .null => {},
73
76
  else => @compileError("cannot encode type " ++ @typeName(T)),
74
77
  }
@@ -80,7 +83,8 @@ fn anyTag(self: *Encoder, tag_: Tag, val: anytype) !void {
80
83
  /// Encode a tag.
81
84
  pub fn tag(self: *Encoder, tag_: Tag) !void {
82
85
  const t = self.mergedTag(tag_);
83
- try t.encode(self.writer());
86
+ var buf: [Tag.max_encoded_len]u8 = undefined;
87
+ try self.buffer.prependSlice(t.encodeToSlice(&buf));
84
88
  }
85
89
 
86
90
  fn mergedTag(self: *Encoder, tag_: Tag) Tag {
@@ -96,19 +100,14 @@ fn mergedTag(self: *Encoder, tag_: Tag) Tag {
96
100
 
97
101
  /// Encode a length.
98
102
  pub fn length(self: *Encoder, len: usize) !void {
99
- const writer_ = self.writer();
100
- if (len < 128) {
101
- try writer_.writeInt(u8, @intCast(len), .big);
102
- return;
103
- }
104
- inline for ([_]type{ u8, u16, u32 }) |T| {
105
- if (len < std.math.maxInt(T)) {
106
- try writer_.writeInt(T, @intCast(len), .big);
107
- try writer_.writeInt(u8, @sizeOf(T) | 0x80, .big);
108
- return;
109
- }
110
- }
111
- return error.InvalidLength;
103
+ if (len < 128) return self.buffer.prependSlice(&.{@intCast(len)});
104
+ const len32 = std.math.cast(u32, len) orelse return error.InvalidLength;
105
+ var buf: [@sizeOf(u32) + 1]u8 = undefined;
106
+ std.mem.writeInt(u32, buf[1..], len32, .big);
107
+ var first: usize = 1;
108
+ while (buf[first] == 0) first += 1;
109
+ buf[first - 1] = @intCast((buf.len - first) | 0x80);
110
+ return self.buffer.prependSlice(buf[first - 1 ..]);
112
111
  }
113
112
 
114
113
  /// Encode a tag and length-prefixed bytes.
@@ -118,28 +117,23 @@ pub fn tagBytes(self: *Encoder, tag_: Tag, bytes: []const u8) !void {
118
117
  try self.tag(tag_);
119
118
  }
120
119
 
121
- /// Warning: This writer writes backwards. `fn print` will NOT work as expected.
122
- pub fn writer(self: *Encoder) ArrayListReverse.Writer {
123
- return self.buffer.writer();
120
+ /// Write raw bytes. The encoder builds its output back-to-front, so chained
121
+ /// calls should be made in reverse of the desired on-wire order.
122
+ pub fn prependBytes(self: *Encoder, bytes: []const u8) !void {
123
+ return self.buffer.prependSlice(bytes);
124
124
  }
125
125
 
126
126
  fn int(self: *Encoder, comptime T: type, value: T) !void {
127
- const big = std.mem.nativeTo(T, value, .big);
128
- const big_bytes = std.mem.asBytes(&big);
129
-
130
- const bits_needed = @bitSizeOf(T) - @clz(value);
131
- const needs_padding: u1 = if (value == 0)
132
- 1
133
- else if (bits_needed > 8) brk: {
134
- const RightShift = @Int(.unsigned, @bitSizeOf(@TypeOf(bits_needed)) - 1);
135
- const right_shift: RightShift = @intCast(bits_needed - 9);
136
- break :brk if (value >> right_shift == 0x1ff) 1 else 0;
137
- } else 0;
138
- const bytes_needed = try std.math.divCeil(usize, bits_needed, 8) + needs_padding;
139
-
140
- const writer_ = self.writer();
141
- for (0..bytes_needed - needs_padding) |i| try writer_.writeByte(big_bytes[big_bytes.len - i - 1]);
142
- if (needs_padding == 1) try writer_.writeByte(0);
127
+ const info = @typeInfo(T).int;
128
+ const Unsigned = @Int(.unsigned, info.bits);
129
+ const pad: u8 = if (info.signedness == .signed and value < 0) 0xff else 0;
130
+ var buf: [@sizeOf(Unsigned) + 1]u8 = undefined;
131
+ buf[0] = pad;
132
+ std.mem.writeInt(Unsigned, buf[1..], @bitCast(value), .big);
133
+
134
+ var first: usize = 0;
135
+ while (first + 1 < buf.len and buf[first] == pad and (buf[first + 1] ^ pad) & 0x80 == 0) first += 1;
136
+ try self.buffer.prependSlice(buf[first..]);
143
137
  }
144
138
 
145
139
  test int {
@@ -148,15 +142,84 @@ test int {
148
142
  defer encoder.deinit();
149
143
 
150
144
  try encoder.int(u8, 0);
151
- try std.testing.expectEqualSlices(u8, &[_]u8{0}, encoder.buffer.data);
145
+ try std.testing.expectEqualSlices(u8, &.{0}, encoder.buffer.data);
152
146
 
153
147
  encoder.buffer.clearAndFree();
154
148
  try encoder.int(u16, 0x00ff);
155
- try std.testing.expectEqualSlices(u8, &[_]u8{0xff}, encoder.buffer.data);
149
+ try std.testing.expectEqualSlices(u8, &.{ 0, 0xff }, encoder.buffer.data);
156
150
 
157
151
  encoder.buffer.clearAndFree();
158
152
  try encoder.int(u32, 0xffff);
159
- try std.testing.expectEqualSlices(u8, &[_]u8{ 0, 0xff, 0xff }, encoder.buffer.data);
153
+ try std.testing.expectEqualSlices(u8, &.{ 0, 0xff, 0xff }, encoder.buffer.data);
154
+
155
+ encoder.buffer.clearAndFree();
156
+ try encoder.int(u32, 0x01020304);
157
+ try std.testing.expectEqualSlices(u8, &.{ 0x01, 0x02, 0x03, 0x04 }, encoder.buffer.data);
158
+
159
+ encoder.buffer.clearAndFree();
160
+ try encoder.int(u8, 127);
161
+ try std.testing.expectEqualSlices(u8, &.{0x7f}, encoder.buffer.data);
162
+
163
+ encoder.buffer.clearAndFree();
164
+ try encoder.int(u16, 128);
165
+ try std.testing.expectEqualSlices(u8, &.{ 0, 0x80 }, encoder.buffer.data);
166
+
167
+ encoder.buffer.clearAndFree();
168
+ try encoder.int(u16, 256);
169
+ try std.testing.expectEqualSlices(u8, &.{ 0x01, 0x00 }, encoder.buffer.data);
170
+
171
+ encoder.buffer.clearAndFree();
172
+ try encoder.int(u8, 128);
173
+ try std.testing.expectEqualSlices(u8, &.{ 0, 0x80 }, encoder.buffer.data);
174
+
175
+ encoder.buffer.clearAndFree();
176
+ try encoder.int(u8, 255);
177
+ try std.testing.expectEqualSlices(u8, &.{ 0, 0xff }, encoder.buffer.data);
178
+
179
+ encoder.buffer.clearAndFree();
180
+ try encoder.int(u16, 0x8000);
181
+ try std.testing.expectEqualSlices(u8, &.{ 0, 0x80, 0 }, encoder.buffer.data);
182
+
183
+ encoder.buffer.clearAndFree();
184
+ try encoder.int(i8, -1);
185
+ try std.testing.expectEqualSlices(u8, &.{0xff}, encoder.buffer.data);
186
+
187
+ encoder.buffer.clearAndFree();
188
+ try encoder.int(i8, -128);
189
+ try std.testing.expectEqualSlices(u8, &.{0x80}, encoder.buffer.data);
190
+
191
+ encoder.buffer.clearAndFree();
192
+ try encoder.int(i16, -129);
193
+ try std.testing.expectEqualSlices(u8, &.{ 0xff, 0x7f }, encoder.buffer.data);
194
+ }
195
+
196
+ test length {
197
+ const allocator = std.testing.allocator;
198
+ var encoder = Encoder.init(allocator);
199
+ defer encoder.deinit();
200
+
201
+ try encoder.length(127);
202
+ try std.testing.expectEqualSlices(u8, &.{0x7f}, encoder.buffer.data);
203
+
204
+ encoder.buffer.clearAndFree();
205
+ try encoder.length(128);
206
+ try std.testing.expectEqualSlices(u8, &.{ 0x81, 0x80 }, encoder.buffer.data);
207
+
208
+ encoder.buffer.clearAndFree();
209
+ try encoder.length(255);
210
+ try std.testing.expectEqualSlices(u8, &.{ 0x81, 0xff }, encoder.buffer.data);
211
+
212
+ encoder.buffer.clearAndFree();
213
+ try encoder.length(256);
214
+ try std.testing.expectEqualSlices(u8, &.{ 0x82, 0x01, 0x00 }, encoder.buffer.data);
215
+
216
+ encoder.buffer.clearAndFree();
217
+ try encoder.length(65535);
218
+ try std.testing.expectEqualSlices(u8, &.{ 0x82, 0xff, 0xff }, encoder.buffer.data);
219
+
220
+ encoder.buffer.clearAndFree();
221
+ try encoder.length(65536);
222
+ try std.testing.expectEqualSlices(u8, &.{ 0x83, 0x01, 0x00, 0x00 }, encoder.buffer.data);
160
223
  }
161
224
 
162
225
  const std = @import("std");
@@ -49,6 +49,48 @@ test decode {
49
49
  try std.testing.expectEqualDeep(test_case.value, decoded);
50
50
  }
51
51
 
52
+ test "integer round trip across signed and unsigned boundaries" {
53
+ const allocator = std.testing.allocator;
54
+ inline for (.{ u8, u16, u32, i8, i16, i32 }) |T| {
55
+ const cases = comptime blk: {
56
+ const min = std.math.minInt(T);
57
+ const max = std.math.maxInt(T);
58
+ break :blk [_]T{ 0, 1, max, min, @divTrunc(max, 2), @divTrunc(min, 2) };
59
+ };
60
+ for (cases) |value| {
61
+ const buf = try encode(allocator, value);
62
+ defer allocator.free(buf);
63
+ const decoded = try decode(T, buf);
64
+ try std.testing.expectEqual(value, decoded);
65
+ }
66
+ }
67
+ }
68
+
69
+ test "encode skips null optional fields" {
70
+ const Value = struct { a: ?u8, b: u8 };
71
+ const allocator = std.testing.allocator;
72
+ const actual = try encode(allocator, Value{ .a = null, .b = 5 });
73
+ defer allocator.free(actual);
74
+
75
+ try std.testing.expectEqualSlices(u8, &.{ 0x30, 0x03, 0x02, 0x01, 0x05 }, actual);
76
+ }
77
+
78
+ test "encode preserves outer sequence tag after implicit field tags" {
79
+ const Value = struct {
80
+ a: u8,
81
+ b: u8,
82
+
83
+ pub const asn1_tags = .{
84
+ .a = asn1.FieldTag.initImplicit(0, .context_specific),
85
+ };
86
+ };
87
+ const allocator = std.testing.allocator;
88
+ const actual = try encode(allocator, Value{ .a = 1, .b = 2 });
89
+ defer allocator.free(actual);
90
+
91
+ try std.testing.expectEqualSlices(u8, &.{ 0x30, 0x06, 0x80, 0x01, 0x01, 0x02, 0x01, 0x02 }, actual);
92
+ }
93
+
52
94
  test {
53
95
  _ = Decoder;
54
96
  _ = Encoder;
@@ -71,16 +71,18 @@ pub const Tag = struct {
71
71
 
72
72
  pub fn decode(reader: *std.Io.Reader) !Tag {
73
73
  const tag1: FirstTag = @bitCast(try reader.takeByte());
74
- var number: u14 = tag1.number;
75
-
76
- if (tag1.number == 31) {
77
- const tag2: NextTag = @bitCast(try reader.takeByte());
78
- number = tag2.number;
79
- if (tag2.continues) {
80
- const tag3: NextTag = @bitCast(try reader.takeByte());
81
- number = (number << 7) + tag3.number;
82
- if (tag3.continues) return error.EndOfStream;
83
- }
74
+ var number: std.meta.Tag(Tag.Number) = tag1.number;
75
+
76
+ if (tag1.number == high_tag_marker) {
77
+ number = 0;
78
+ for (0..max_continuations) |i| {
79
+ const next: NextTag = @bitCast(try reader.takeByte());
80
+ if (i == 0 and next.number == 0) return error.InvalidEncoding;
81
+ number = std.math.shlExact(@TypeOf(number), number, 7) catch return error.InvalidEncoding;
82
+ number |= next.number;
83
+ if (!next.continues) break;
84
+ } else return error.InvalidEncoding;
85
+ if (number < high_tag_marker) return error.InvalidEncoding;
84
86
  }
85
87
 
86
88
  return Tag{
@@ -90,40 +92,51 @@ pub const Tag = struct {
90
92
  };
91
93
  }
92
94
 
93
- pub fn encode(self: Tag, writer: *std.Io.Writer) @TypeOf(writer).Error!void {
94
- var tag1 = FirstTag{
95
+ pub fn encodeToSlice(self: Tag, buf: *[max_encoded_len]u8) []const u8 {
96
+ const n = @intFromEnum(self.number);
97
+ var tag1: FirstTag = .{
95
98
  .number = undefined,
96
99
  .constructed = self.constructed,
97
100
  .class = self.class,
98
101
  };
99
102
 
100
- var buffer: [3]u8 = undefined;
101
- var writer2: std.Io.Writer = .init(&buffer);
103
+ if (n < high_tag_marker) {
104
+ tag1.number = @intCast(n);
105
+ buf[0] = @bitCast(tag1);
106
+ return buf[0..1];
107
+ }
102
108
 
103
- switch (@intFromEnum(self.number)) {
104
- 0...std.math.maxInt(u5) => |n| {
105
- tag1.number = @intCast(n);
106
- writer2.writeByte(@bitCast(tag1)) catch unreachable;
107
- },
108
- std.math.maxInt(u5) + 1...std.math.maxInt(u7) => |n| {
109
- tag1.number = 15;
110
- const tag2 = NextTag{ .number = @intCast(n), .continues = false };
111
- writer2.writeByte(@bitCast(tag1)) catch unreachable;
112
- writer2.writeByte(@bitCast(tag2)) catch unreachable;
113
- },
114
- else => |n| {
115
- tag1.number = 15;
116
- const tag2 = NextTag{ .number = @intCast(n >> 7), .continues = true };
117
- const tag3 = NextTag{ .number = @truncate(n), .continues = false };
118
- writer2.writeByte(@bitCast(tag1)) catch unreachable;
119
- writer2.writeByte(@bitCast(tag2)) catch unreachable;
120
- writer2.writeByte(@bitCast(tag3)) catch unreachable;
121
- },
109
+ tag1.number = high_tag_marker;
110
+ buf[0] = @bitCast(tag1);
111
+
112
+ const bits_used = @bitSizeOf(@TypeOf(n)) - @clz(n);
113
+ const len = std.math.divCeil(usize, bits_used, 7) catch unreachable;
114
+
115
+ var remaining = n;
116
+ var i = len;
117
+ while (i > 0) : (i -= 1) {
118
+ buf[i] = @bitCast(NextTag{
119
+ .number = @truncate(remaining),
120
+ .continues = i != len,
121
+ });
122
+ remaining >>= 7;
122
123
  }
124
+ return buf[0 .. 1 + len];
125
+ }
123
126
 
124
- _ = try writer.write(writer2.buffered());
127
+ pub fn encode(self: Tag, writer: *std.Io.Writer) std.Io.Writer.Error!void {
128
+ var buf: [max_encoded_len]u8 = undefined;
129
+ try writer.writeAll(self.encodeToSlice(&buf));
125
130
  }
126
131
 
132
+ pub const max_encoded_len = 1 + (std.math.divCeil(
133
+ comptime_int,
134
+ @bitSizeOf(std.meta.Tag(Tag.Number)),
135
+ 7,
136
+ ) catch unreachable);
137
+ const max_continuations = max_encoded_len - 1;
138
+ const high_tag_marker = std.math.maxInt(u5);
139
+
127
140
  const FirstTag = packed struct(u8) { number: u5, constructed: bool, class: Tag.Class };
128
141
  const NextTag = packed struct(u8) { number: u7, continues: bool };
129
142
 
@@ -165,6 +178,42 @@ test Tag {
165
178
  try std.testing.expectEqual(Tag.init(@enumFromInt(3), true, .context_specific), t);
166
179
  }
167
180
 
181
+ test "Tag.encode produces the exact bytes from X.690" {
182
+ const cases = [_]struct { number: u16, expected: []const u8 }{
183
+ .{ .number = 0, .expected = &.{0x00} },
184
+ .{ .number = 30, .expected = &.{0x1e} },
185
+ .{ .number = 31, .expected = &.{ 0x1f, 0x1f } },
186
+ .{ .number = 127, .expected = &.{ 0x1f, 0x7f } },
187
+ .{ .number = 128, .expected = &.{ 0x1f, 0x81, 0x00 } },
188
+ .{ .number = 16383, .expected = &.{ 0x1f, 0xff, 0x7f } },
189
+ .{ .number = 16384, .expected = &.{ 0x1f, 0x81, 0x80, 0x00 } },
190
+ .{ .number = 65535, .expected = &.{ 0x1f, 0x83, 0xff, 0x7f } },
191
+ };
192
+ for (cases) |c| {
193
+ const tag = Tag.init(@enumFromInt(c.number), false, .universal);
194
+ var buf: [Tag.max_encoded_len]u8 = undefined;
195
+ try std.testing.expectEqualSlices(u8, c.expected, tag.encodeToSlice(&buf));
196
+ }
197
+ }
198
+
199
+ test "Tag.encode/decode round trip" {
200
+ for ([_]u16{ 0, 30, 31, 32, 127, 128, 16383, 16384, 65535 }) |n| {
201
+ const tag = Tag.init(@enumFromInt(n), false, .universal);
202
+ var buf: [Tag.max_encoded_len]u8 = undefined;
203
+ const encoded = tag.encodeToSlice(&buf);
204
+ var reader: std.Io.Reader = .fixed(encoded);
205
+ try std.testing.expectEqual(tag, try Tag.decode(&reader));
206
+ try std.testing.expectEqual(encoded.len, reader.seek);
207
+ }
208
+ }
209
+
210
+ test "Tag.decode rejects non-minimal high-tag form" {
211
+ for ([_][]const u8{ &.{ 0x1f, 0x1e }, &.{ 0x1f, 0x80, 0x01 } }) |bytes| {
212
+ var reader: std.Io.Reader = .fixed(bytes);
213
+ try std.testing.expectError(error.InvalidEncoding, Tag.decode(&reader));
214
+ }
215
+ }
216
+
168
217
  /// A decoded view.
169
218
  pub const Element = struct {
170
219
  tag: Tag,
@@ -183,13 +232,14 @@ pub const Element = struct {
183
232
  }
184
233
  };
185
234
 
186
- pub const DecodeError = error{EndOfStream};
235
+ pub const DecodeError = error{ EndOfStream, InvalidEncoding };
187
236
 
188
237
  /// Safely decode a DER/BER/CER element at `index`:
189
238
  /// - Ensures length uses shortest form
190
239
  /// - Ensures length is within `bytes`
191
240
  /// - Ensures length is less than `std.math.maxInt(Index)`
192
241
  pub fn decode(bytes: []const u8, index: Index) DecodeError!Element {
242
+ if (index > bytes.len) return error.EndOfStream;
193
243
  var reader: std.Io.Reader = .fixed(bytes[index..]);
194
244
 
195
245
  const tag = Tag.decode(&reader) catch |err| switch (err) {
@@ -327,13 +377,22 @@ pub const BitString = struct {
327
377
  }
328
378
 
329
379
  pub fn encodeDer(self: BitString, encoder: *der.Encoder) !void {
330
- try encoder.writer().writeAll(self.bytes);
331
- try encoder.writer().writeByte(self.right_padding);
380
+ try encoder.prependBytes(self.bytes);
381
+ try encoder.prependBytes(&.{self.right_padding});
332
382
  try encoder.length(self.bytes.len + 1);
333
383
  try encoder.tag(asn1_tag);
334
384
  }
335
385
  };
336
386
 
387
+ test BitString {
388
+ const bs = BitString{ .bytes = &.{ 0x6e, 0x5d, 0xc0 }, .right_padding = 6 };
389
+ const allocator = std.testing.allocator;
390
+ const buf = try der.encode(allocator, bs);
391
+ defer allocator.free(buf);
392
+ try std.testing.expectEqualSlices(u8, &.{ 0x03, 0x04, 0x06, 0x6e, 0x5d, 0xc0 }, buf);
393
+ try std.testing.expectEqualDeep(bs, try der.decode(BitString, buf));
394
+ }
395
+
337
396
  pub fn Opaque(comptime tag: Tag) type {
338
397
  return struct {
339
398
  bytes: []const u8,
@@ -1,3 +1,9 @@
1
1
  pub const asn1 = @import("codecs/asn1.zig");
2
2
  pub const base64 = @import("codecs/base64_hex_ct.zig").base64;
3
3
  pub const hex = @import("codecs/base64_hex_ct.zig").hex;
4
+
5
+ test {
6
+ _ = asn1;
7
+ _ = base64;
8
+ _ = hex;
9
+ }
@@ -116,7 +116,7 @@ pub const can_unwind: bool = s: {
116
116
  .x86,
117
117
  .x86_64,
118
118
  },
119
- // Not supported yet: arm/armeb/thumb/thumbeb, hppa, hppa64, microblaze/microblazeel, xtensa/xtensaeb
119
+ // Not supported yet: arm/armeb/thumb/thumbeb, hppa, hppa64, microblaze/microblazeel
120
120
  .linux => &.{
121
121
  .aarch64,
122
122
  .aarch64_be,
@@ -93,12 +93,28 @@ pub fn getSymbols(
93
93
  pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
94
94
  _ = si;
95
95
  _ = io;
96
- // This function is marked as deprecated; however, it is significantly more
97
- // performant than `dladdr` (since the latter also does a very slow symbol
98
- // lookup), so let's use it since it's still available.
99
- return std.mem.span(std.c.dyld_image_path_containing_address(
100
- @ptrFromInt(address),
101
- ) orelse return error.MissingDebugInfo);
96
+ return getModuleNameInner(address) orelse return error.MissingDebugInfo;
97
+ }
98
+ fn getModuleNameInner(address: usize) ?[]const u8 {
99
+ switch (builtin.target.os.tag) {
100
+ .macos => {
101
+ // This function is marked as deprecated; however, it is significantly more performant
102
+ // than `dladdr` (since the latter also does a very slow symbol lookup), so let's just
103
+ // use it for the better performance since it's still available.
104
+ return std.mem.span(std.c.dyld_image_path_containing_address(
105
+ @ptrFromInt(address),
106
+ ) orelse return null);
107
+ },
108
+ else => {
109
+ // On other Darwin systems, the function used above is entirely unavailable, so we have
110
+ // no choice but to use the slow `dladdr`.
111
+ var info: std.c.dl_info = undefined;
112
+ if (std.c.dladdr(@ptrFromInt(address), &info) == 0) {
113
+ return null;
114
+ }
115
+ return std.mem.span(info.fname);
116
+ },
117
+ }
102
118
  }
103
119
  pub fn getModuleSlide(si: *SelfInfo, io: Io, address: usize) Error!usize {
104
120
  const gpa = std.debug.getDebugInfoAllocator();
@@ -446,12 +462,25 @@ fn unwindFrameInner(si: *SelfInfo, io: Io, context: *UnwindContext) !usize {
446
462
 
447
463
  /// Acquires the mutex on success.
448
464
  fn findModule(si: *SelfInfo, gpa: Allocator, io: Io, address: usize) Error!*Module {
449
- // This function is marked as deprecated; however, it is significantly more
450
- // performant than `dladdr` (since the latter also does a very slow symbol
451
- // lookup), so let's use it since it's still available.
452
- const text_base = std.c._dyld_get_image_header_containing_address(
453
- @ptrFromInt(address),
454
- ) orelse return error.MissingDebugInfo;
465
+ const text_base: *anyopaque = switch (builtin.target.os.tag) {
466
+ .macos => base: {
467
+ // This function is marked as deprecated; however, it is significantly more performant
468
+ // than `dladdr` (since the latter also does a very slow symbol lookup), so let's just
469
+ // use it for the better performance since it's still available.
470
+ break :base std.c._dyld_get_image_header_containing_address(
471
+ @ptrFromInt(address),
472
+ ) orelse return error.MissingDebugInfo;
473
+ },
474
+ else => base: {
475
+ // On other Darwin systems, the function used above is entirely unavailable, so we have
476
+ // no choice but to use the slow `dladdr`.
477
+ var info: std.c.dl_info = undefined;
478
+ if (std.c.dladdr(@ptrFromInt(address), &info) == 0) {
479
+ return error.MissingDebugInfo;
480
+ }
481
+ break :base info.fbase;
482
+ },
483
+ };
455
484
  try si.mutex.lock(io);
456
485
  errdefer si.mutex.unlock(io);
457
486
  const gop = try si.modules.getOrPutAdapted(gpa, @intFromPtr(text_base), Module.Adapter{});
@@ -563,9 +592,7 @@ const Module = struct {
563
592
 
564
593
  fn getFile(module: *Module, gpa: Allocator, io: Io) Error!*MachOFile {
565
594
  if (module.file == null) {
566
- const path = std.mem.span(
567
- std.c.dyld_image_path_containing_address(@ptrFromInt(module.text_base)).?,
568
- );
595
+ const path = getModuleNameInner(module.text_base).?;
569
596
  module.file = MachOFile.load(gpa, io, path, builtin.cpu.arch) catch |err| switch (err) {
570
597
  error.InvalidMachO, error.InvalidDwarf => error.InvalidDebugInfo,
571
598
  error.MissingDebugInfo, error.OutOfMemory, error.UnsupportedDebugInfo, error.ReadFailed => |e| e,
@@ -115,9 +115,10 @@ pub fn clone() callconv(.naked) u32 {
115
115
  //
116
116
  // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
117
117
  // a2 a6, a3, a4, a5, a8
118
- asm volatile (
118
+ if (builtin.abi != .call0) asm volatile (
119
119
  \\ entry sp, 16
120
- \\
120
+ );
121
+ asm volatile (
121
122
  \\ movi a8, -16
122
123
  \\ and a3, a3, a8
123
124
  \\
@@ -128,10 +129,29 @@ pub fn clone() callconv(.naked) u32 {
128
129
  \\ mov a8, a6
129
130
  \\ mov a6, a4
130
131
  \\ mov a4, a8
132
+ );
133
+ if (builtin.abi == .call0) asm volatile (
134
+ \\ l32i a8, sp, 0
135
+ ) else asm volatile (
131
136
  \\ l32i a8, sp, 16
137
+ );
138
+ asm volatile (
132
139
  \\ movi a2, 116 // SYS_clone
133
140
  \\ syscall
141
+ );
142
+ if (builtin.abi == .call0) asm volatile (
143
+ \\ beqz a2, 1f
144
+ \\ // parent
145
+ \\ ret
134
146
  \\
147
+ \\ // child
148
+ \\1:
149
+ \\ movi a15, 0
150
+ \\ movi a0, 0
151
+ \\
152
+ \\ mov a2, a10
153
+ \\ callx0 a9
154
+ ) else asm volatile (
135
155
  \\ beqz a2, 1f
136
156
  \\ // parent
137
157
  \\ retw
@@ -141,8 +161,10 @@ pub fn clone() callconv(.naked) u32 {
141
161
  \\ movi a7, 0
142
162
  \\ movi a0, 0
143
163
  \\
144
- \\ mov a2, a10
145
- \\ callx0 a9
164
+ \\ mov a6, a10
165
+ \\ callx4 a9
166
+ );
167
+ asm volatile (
146
168
  \\ movi a2, 118 // SYS_exit
147
169
  \\ syscall
148
170
  );
package/std/pie.zig CHANGED
@@ -270,9 +270,7 @@ inline fn getDynamicSymbol() [*]const elf.Dyn {
270
270
  // embedded constant. Note that `call0` is a 3-byte instruction, so we need both
271
271
  // `.balign` directives to be safe.
272
272
  \\ .balign 4
273
- \\ .begin no-transform
274
273
  \\ call0 1f
275
- \\ .end no-transform
276
274
  \\ .balign 4
277
275
  \\ .word _DYNAMIC - .
278
276
  \\1:
package/std/sort/pdq.zig CHANGED
@@ -47,10 +47,10 @@ pub fn pdqContext(a: usize, b: usize, context: anytype) void {
47
47
  const max_limit = std.math.floorPowerOfTwo(usize, b - a) + 1;
48
48
 
49
49
  // set upper bound on stack memory usage.
50
- const Range = struct { a: usize, b: usize, limit: usize };
50
+ const Range = struct { a: usize, b: usize, limit: usize, leftmost: bool };
51
51
  const stack_size = math.log2(math.maxInt(usize) + 1);
52
52
  var stack: [stack_size]Range = undefined;
53
- var range = Range{ .a = a, .b = b, .limit = max_limit };
53
+ var range = Range{ .a = a, .b = b, .limit = max_limit, .leftmost = true };
54
54
  var top: usize = 0;
55
55
 
56
56
  while (true) {
@@ -62,7 +62,11 @@ pub fn pdqContext(a: usize, b: usize, context: anytype) void {
62
62
 
63
63
  // very short slices get sorted using insertion sort.
64
64
  if (len <= max_insertion) {
65
- break sort.insertionContext(range.a, range.b, context);
65
+ if (range.leftmost) {
66
+ break sort.insertionContext(range.a, range.b, context);
67
+ } else {
68
+ break unguardedInsertionContext(range.a, range.b, context);
69
+ }
66
70
  }
67
71
 
68
72
  // if too many bad pivot choices were made, simply fall back to heapsort in order to
@@ -115,12 +119,13 @@ pub fn pdqContext(a: usize, b: usize, context: anytype) void {
115
119
  const balanced_threshold = len / 8;
116
120
  if (left_len < right_len) {
117
121
  was_balanced = left_len >= balanced_threshold;
118
- stack[top] = .{ .a = range.a, .b = mid, .limit = range.limit };
122
+ stack[top] = .{ .a = range.a, .b = mid, .limit = range.limit, .leftmost = range.leftmost };
119
123
  top += 1;
120
124
  range.a = mid + 1;
125
+ range.leftmost = false;
121
126
  } else {
122
127
  was_balanced = right_len >= balanced_threshold;
123
- stack[top] = .{ .a = mid + 1, .b = range.b, .limit = range.limit };
128
+ stack[top] = .{ .a = mid + 1, .b = range.b, .limit = range.limit, .leftmost = false };
124
129
  top += 1;
125
130
  range.b = mid;
126
131
  }
@@ -131,6 +136,18 @@ pub fn pdqContext(a: usize, b: usize, context: anytype) void {
131
136
  }
132
137
  }
133
138
 
139
+ /// Insertion sort that assumes `items[a-1]` exists and is <= all elements in `[a, b)`,
140
+ /// allowing the inner loop to skip the bounds check.
141
+ fn unguardedInsertionContext(a: usize, b: usize, context: anytype) void {
142
+ var i = a + 1;
143
+ while (i < b) : (i += 1) {
144
+ var j = i;
145
+ while (context.lessThan(j, j - 1)) : (j -= 1) {
146
+ context.swap(j, j - 1);
147
+ }
148
+ }
149
+ }
150
+
134
151
  /// partitions `items[a..b]` into elements smaller than `items[pivot]`,
135
152
  /// followed by elements greater than or equal to `items[pivot]`.
136
153
  ///
@@ -158,17 +175,78 @@ fn partition(a: usize, b: usize, pivot: *usize, context: anytype) bool {
158
175
  i += 1;
159
176
  j -= 1;
160
177
 
161
- while (true) {
162
- while (i <= j and context.lessThan(i, a)) i += 1;
163
- while (i <= j and !context.lessThan(j, a)) j -= 1;
164
- if (i > j) break;
178
+ const block_size = 64;
179
+ var offsets_l: [block_size]u8 align(std.atomic.cache_line) = undefined;
180
+ var offsets_r: [block_size]u8 align(std.atomic.cache_line) = undefined;
181
+
182
+ var offsets_l_base = i;
183
+ var offsets_r_base = j;
184
+ var num_l: usize = 0;
185
+ var num_r: usize = 0;
186
+ var start_l: usize = 0;
187
+ var start_r: usize = 0;
188
+
189
+ while (i <= j) {
190
+ const num_unknown = j + 1 - i;
191
+ const left_split = if (num_l == 0)
192
+ @min(block_size, if (num_r == 0) num_unknown / 2 else num_unknown)
193
+ else
194
+ 0;
195
+ const right_split = if (num_r == 0)
196
+ @min(block_size, num_unknown - left_split)
197
+ else
198
+ 0;
199
+
200
+ for (0..left_split) |k| {
201
+ offsets_l[num_l] = @intCast(k);
202
+ num_l += @intFromBool(!context.lessThan(i + k, a));
203
+ }
204
+ i += left_split;
165
205
 
166
- context.swap(i, j);
167
- i += 1;
168
- j -= 1;
206
+ for (0..right_split) |k| {
207
+ offsets_r[num_r] = @intCast(k);
208
+ num_r += @intFromBool(context.lessThan(j - k, a));
209
+ }
210
+ j -= right_split;
211
+
212
+ const num = @min(num_l, num_r);
213
+ for (0..num) |m| {
214
+ context.swap(
215
+ offsets_l_base + offsets_l[start_l + m],
216
+ offsets_r_base - offsets_r[start_r + m],
217
+ );
218
+ }
219
+ num_l -= num;
220
+ num_r -= num;
221
+ start_l += num;
222
+ start_r += num;
223
+
224
+ if (num_l == 0) {
225
+ start_l = 0;
226
+ offsets_l_base = i;
227
+ }
228
+ if (num_r == 0) {
229
+ start_r = 0;
230
+ offsets_r_base = j;
231
+ }
169
232
  }
170
233
 
171
- // TODO: Enable the BlockQuicksort optimization
234
+ if (num_l > 0) {
235
+ while (num_l > 0) {
236
+ num_l -= 1;
237
+ context.swap(offsets_l_base + offsets_l[start_l + num_l], j);
238
+ j -= 1;
239
+ }
240
+ i = j + 1;
241
+ }
242
+ if (num_r > 0) {
243
+ while (num_r > 0) {
244
+ num_r -= 1;
245
+ context.swap(offsets_r_base - offsets_r[start_r + num_r], i);
246
+ i += 1;
247
+ }
248
+ j = i - 1;
249
+ }
172
250
 
173
251
  context.swap(j, a);
174
252
  pivot.* = j;
package/std/start.zig CHANGED
@@ -487,14 +487,22 @@ fn _start() callconv(.naked) noreturn {
487
487
  \\ sub %%sp, 2047, %%sp
488
488
  \\ ba,a %[posixCallMainAndExit]
489
489
  ,
490
- .xtensa, .xtensaeb =>
491
- // a0 = LR, a7 = FP, a1 = SP
492
- \\ movi a0, 0
493
- \\ movi a7, 0
494
- \\ mov a2, sp
495
- \\ movi a8, -16
496
- \\ and sp, sp, a8
497
- \\ callx0 %[posixCallMainAndExit]
490
+ .xtensa, .xtensaeb => if (builtin.abi == .call0)
491
+ // a0 = LR, a15 = FP, a1 = SP
492
+ \\ movi a0, 0
493
+ \\ movi a15, 0
494
+ \\ mov a2, sp
495
+ \\ movi a8, -16
496
+ \\ and sp, sp, a8
497
+ \\ call0 %[posixCallMainAndExit]
498
+ else
499
+ // a0 = LR, a7 = FP, a1 = SP
500
+ \\ movi a0, 0
501
+ \\ movi a7, 0
502
+ \\ mov a6, sp
503
+ \\ movi a8, -16
504
+ \\ and sp, sp, a8
505
+ \\ call4 %[posixCallMainAndExit]
498
506
  ,
499
507
  else => @compileError("unsupported arch"),
500
508
  }
package/std/zon/parse.zig CHANGED
@@ -1094,6 +1094,7 @@ const Parser = struct {
1094
1094
  name: []const u8,
1095
1095
  ) error{ OutOfMemory, ParseZon } {
1096
1096
  @branchHint(.cold);
1097
+ if (self.diag == null) return error.ParseZon;
1097
1098
  const gpa = self.gpa;
1098
1099
  const token = if (field) |f| b: {
1099
1100
  var buf: [2]Ast.Node.Index = undefined;
@@ -1150,6 +1151,7 @@ const Parser = struct {
1150
1151
  field: usize,
1151
1152
  ) error{ OutOfMemory, ParseZon } {
1152
1153
  @branchHint(.cold);
1154
+ if (self.diag == null) return error.ParseZon;
1153
1155
  const ast_node = node.getAstNode(self.zoir);
1154
1156
  var buf: [2]Ast.Node.Index = undefined;
1155
1157
  const token = if (self.ast.fullStructInit(&buf, ast_node)) |struct_init| b: {
@@ -3540,3 +3542,26 @@ test "std.zon no alloc" {
3540
3542
  try fromZoirNode(Nested, ast, zoir, .root, null, .{}),
3541
3543
  );
3542
3544
  }
3545
+
3546
+ test "std.zon errors without diagnostics" {
3547
+ const gpa = std.testing.allocator;
3548
+
3549
+ const Enum = enum {
3550
+ foo,
3551
+ bar,
3552
+ baz,
3553
+ };
3554
+ try std.testing.expectError(error.ParseZon, fromSliceAlloc(Enum, gpa, ".nothing", null, .{}));
3555
+
3556
+ const Struct = struct {
3557
+ name: []const u8,
3558
+ };
3559
+ try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, gpa, ".{ .name = \"Alice\", .age = 25 }", null, .{}));
3560
+
3561
+ const Union = union(enum) {
3562
+ x,
3563
+ y: u32,
3564
+ };
3565
+ try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, gpa, ".a", null, .{}));
3566
+ try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, gpa, ".{ .b = 8 }", null, .{}));
3567
+ }
package/ubsan_rt.zig CHANGED
@@ -492,10 +492,10 @@ const NonNullReturnData = extern struct {
492
492
  attribute_loc: SourceLocation,
493
493
  };
494
494
 
495
- fn nonNullReturnAbort(data: *const NonNullReturnData) callconv(.c) noreturn {
496
- nonNullReturn(data);
495
+ fn nonNullReturnAbort(data: *const NonNullReturnData, where: *const SourceLocation) callconv(.c) noreturn {
496
+ nonNullReturn(data, where);
497
497
  }
498
- fn nonNullReturn(_: *const NonNullReturnData) callconv(.c) noreturn {
498
+ fn nonNullReturn(_: *const NonNullReturnData, _: *const SourceLocation) callconv(.c) noreturn {
499
499
  panic(@returnAddress(), "null pointer returned from function declared to never return null", .{});
500
500
  }
501
501