@zigc/lib 0.17.0-dev.667 → 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zigc/lib",
3
- "version": "0.17.0-dev.667",
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 {
@@ -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,
@@ -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
@@ -175,17 +175,78 @@ fn partition(a: usize, b: usize, pivot: *usize, context: anytype) bool {
175
175
  i += 1;
176
176
  j -= 1;
177
177
 
178
- while (true) {
179
- while (i <= j and context.lessThan(i, a)) i += 1;
180
- while (i <= j and !context.lessThan(j, a)) j -= 1;
181
- 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;
182
205
 
183
- context.swap(i, j);
184
- i += 1;
185
- 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
+ }
186
232
  }
187
233
 
188
- // 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
+ }
189
250
 
190
251
  context.swap(j, a);
191
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/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