| ... | @@ -24,6 +24,7 @@ pub fn any(self: *Encoder, val: anytype) !void { | ... | @@ -24,6 +24,7 @@ pub fn any(self: *Encoder, val: anytype) !void { |
| 24 | fn anyTag(self: *Encoder, tag_: Tag, val: anytype) !void { | 24 | fn anyTag(self: *Encoder, tag_: Tag, val: anytype) !void { |
| 25 | const T = @TypeOf(val); | 25 | const T = @TypeOf(val); |
| 26 | if (std.meta.hasFn(T, "encodeDer")) return try val.encodeDer(self); | 26 | if (std.meta.hasFn(T, "encodeDer")) return try val.encodeDer(self); |
| | 27 | const outer_field_tag = self.field_tag; |
| 27 | const start = self.buffer.data.len; | 28 | const start = self.buffer.data.len; |
| 28 | const merged_tag = self.mergedTag(tag_); | 29 | const merged_tag = self.mergedTag(tag_); |
| 29 | | 30 | |
| ... | @@ -42,8 +43,9 @@ fn anyTag(self: *Encoder, tag_: Tag, val: anytype) !void { | ... | @@ -42,8 +43,9 @@ fn anyTag(self: *Encoder, tag_: Tag, val: anytype) !void { |
| 42 | const is_default = if (f_attrs.@"comptime") false else if (f_attrs.defaultValue(f_type)) |default_val| brk: { | 43 | const is_default = if (f_attrs.@"comptime") false else if (f_attrs.defaultValue(f_type)) |default_val| brk: { |
| 43 | break :brk std.mem.eql(u8, std.mem.asBytes(&default_val), std.mem.asBytes(&field_val)); | 44 | break :brk std.mem.eql(u8, std.mem.asBytes(&default_val), std.mem.asBytes(&field_val)); |
| 44 | } else false; | 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 | const start2 = self.buffer.data.len; | 49 | const start2 = self.buffer.data.len; |
| 48 | self.field_tag = field_tag; | 50 | self.field_tag = field_tag; |
| 49 | // will merge with self.field_tag. | 51 | // will merge with self.field_tag. |
| ... | @@ -58,6 +60,7 @@ fn anyTag(self: *Encoder, tag_: Tag, val: anytype) !void { | ... | @@ -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 | .bool => try self.buffer.prependSlice(&[_]u8{if (val) 0xff else 0}), | 65 | .bool => try self.buffer.prependSlice(&[_]u8{if (val) 0xff else 0}), |
| 63 | .int => try self.int(T, val), | 66 | .int => try self.int(T, val), |
| ... | @@ -68,7 +71,7 @@ fn anyTag(self: *Encoder, tag_: Tag, val: anytype) !void { | ... | @@ -68,7 +71,7 @@ fn anyTag(self: *Encoder, tag_: Tag, val: anytype) !void { |
| 68 | try self.int(e.tag_type, @intFromEnum(val)); | 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 | .null => {}, | 75 | .null => {}, |
| 73 | else => @compileError("cannot encode type " ++ @typeName(T)), | 76 | else => @compileError("cannot encode type " ++ @typeName(T)), |
| 74 | } | 77 | } |
| ... | @@ -80,7 +83,8 @@ fn anyTag(self: *Encoder, tag_: Tag, val: anytype) !void { | ... | @@ -80,7 +83,8 @@ fn anyTag(self: *Encoder, tag_: Tag, val: anytype) !void { |
| 80 | /// Encode a tag. | 83 | /// Encode a tag. |
| 81 | pub fn tag(self: *Encoder, tag_: Tag) !void { | 84 | pub fn tag(self: *Encoder, tag_: Tag) !void { |
| 82 | const t = self.mergedTag(tag_); | 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 | fn mergedTag(self: *Encoder, tag_: Tag) Tag { | 90 | fn mergedTag(self: *Encoder, tag_: Tag) Tag { |
| ... | @@ -96,19 +100,14 @@ fn mergedTag(self: *Encoder, tag_: Tag) Tag { | ... | @@ -96,19 +100,14 @@ fn mergedTag(self: *Encoder, tag_: Tag) Tag { |
| 96 | | 100 | |
| 97 | /// Encode a length. | 101 | /// Encode a length. |
| 98 | pub fn length(self: *Encoder, len: usize) !void { | 102 | pub fn length(self: *Encoder, len: usize) !void { |
| 99 | const writer_ = self.writer(); | 103 | if (len < 128) return self.buffer.prependSlice(&.{@intCast(len)}); |
| 100 | if (len < 128) { | 104 | const len32 = std.math.cast(u32, len) orelse return error.InvalidLength; |
| 101 | try writer_.writeInt(u8, @intCast(len), .big); | 105 | var buf: [@sizeOf(u32) + 1]u8 = undefined; |
| 102 | return; | 106 | std.mem.writeInt(u32, buf[1..], len32, .big); |
| 103 | } | 107 | var first: usize = 1; |
| 104 | inline for ([_]type{ u8, u16, u32 }) |T| { | 108 | while (buf[first] == 0) first += 1; |
| 105 | if (len < std.math.maxInt(T)) { | 109 | buf[first - 1] = @intCast((buf.len - first) | 0x80); |
| 106 | try writer_.writeInt(T, @intCast(len), .big); | 110 | return self.buffer.prependSlice(buf[first - 1 ..]); |
| 107 | try writer_.writeInt(u8, @sizeOf(T) | 0x80, .big); | | |
| 108 | return; | | |
| 109 | } | | |
| 110 | } | | |
| 111 | return error.InvalidLength; | | |
| 112 | } | 111 | } |
| 113 | | 112 | |
| 114 | /// Encode a tag and length-prefixed bytes. | 113 | /// Encode a tag and length-prefixed bytes. |
| ... | @@ -118,28 +117,23 @@ pub fn tagBytes(self: *Encoder, tag_: Tag, bytes: []const u8) !void { | ... | @@ -118,28 +117,23 @@ pub fn tagBytes(self: *Encoder, tag_: Tag, bytes: []const u8) !void { |
| 118 | try self.tag(tag_); | 117 | try self.tag(tag_); |
| 119 | } | 118 | } |
| 120 | | 119 | |
| 121 | /// Warning: This writer writes backwards. `fn print` will NOT work as expected. | 120 | /// Write raw bytes. The encoder builds its output back-to-front, so chained |
| 122 | pub fn writer(self: *Encoder) ArrayListReverse.Writer { | 121 | /// calls should be made in reverse of the desired on-wire order. |
| 123 | return self.buffer.writer(); | 122 | pub fn prependBytes(self: *Encoder, bytes: []const u8) !void { |
| | 123 | return self.buffer.prependSlice(bytes); |
| 124 | } | 124 | } |
| 125 | | 125 | |
| 126 | fn int(self: *Encoder, comptime T: type, value: T) !void { | 126 | fn int(self: *Encoder, comptime T: type, value: T) !void { |
| 127 | const big = std.mem.nativeTo(T, value, .big); | 127 | const info = @typeInfo(T).int; |
| 128 | const big_bytes = std.mem.asBytes(&big); | 128 | const Unsigned = @Int(.unsigned, info.bits); |
| 129 | | 129 | const pad: u8 = if (info.signedness == .signed and value < 0) 0xff else 0; |
| 130 | const bits_needed = @bitSizeOf(T) - @clz(value); | 130 | var buf: [@sizeOf(Unsigned) + 1]u8 = undefined; |
| 131 | const needs_padding: u1 = if (value == 0) | 131 | buf[0] = pad; |
| 132 | 1 | 132 | std.mem.writeInt(Unsigned, buf[1..], @bitCast(value), .big); |
| 133 | else if (bits_needed > 8) brk: { | 133 | |
| 134 | const RightShift = @Int(.unsigned, @bitSizeOf(@TypeOf(bits_needed)) - 1); | 134 | var first: usize = 0; |
| 135 | const right_shift: RightShift = @intCast(bits_needed - 9); | 135 | while (first + 1 < buf.len and buf[first] == pad and (buf[first + 1] ^ pad) & 0x80 == 0) first += 1; |
| 136 | break :brk if (value >> right_shift == 0x1ff) 1 else 0; | 136 | try self.buffer.prependSlice(buf[first..]); |
| 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); | | |
| 143 | } | 137 | } |
| 144 | | 138 | |
| 145 | test int { | 139 | test int { |
| ... | @@ -148,15 +142,84 @@ test int { | ... | @@ -148,15 +142,84 @@ test int { |
| 148 | defer encoder.deinit(); | 142 | defer encoder.deinit(); |
| 149 | | 143 | |
| 150 | try encoder.int(u8, 0); | 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 | encoder.buffer.clearAndFree(); | 147 | encoder.buffer.clearAndFree(); |
| 154 | try encoder.int(u16, 0x00ff); | 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 | encoder.buffer.clearAndFree(); | 151 | encoder.buffer.clearAndFree(); |
| 158 | try encoder.int(u32, 0xffff); | 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 | const std = @import("std"); | 225 | const std = @import("std"); |