| ... | ... | @@ -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"); |