const std = @import("std"); pub const Marker = union(enum) { pub const Kind = std.meta.Tag(@This()); const Tag = struct { const temporary = 0x1; const reset = .{ .start = 0xd0, .end = 0xd7 }; const start_of_frame = .{ .{ .start = 0xc0, .end = 0xc3 }, .{ .start = 0xc5, .end = 0xc7 }, .{ .start = 0xc9, .end = 0xcb }, }; const start_of_image = 0xd8; const end_of_image = 0xd9; const define_quantization_table = 0xdb; const define_huffman_table = 0xc4; const define_restart_interval = 0xdd; const define_number_of_lines = 0xdc; const define_hierarchy_progression = 0xde; const extend_huffman_table = 0xdf; const application_segment = .{ .start = 0xe0, .end = 0xef }; const comment = 0xfe; const start_of_scan = 0xda; }; temporary: void, reset: u3, start_of_image: void, end_of_image: void, start_of_frame: u4, define_quantization_table: void, define_huffman_table: void, define_restart_interval: void, define_number_of_lines: void, define_hierarchy_progression: void, extend_huffman_table: void, application_segment: u4, comment: void, start_of_scan: void, pub fn from_u8(value: u8) !@This() { return switch (value) { Tag.reset.start...Tag.reset.end => .{ .reset = @intCast(value - Tag.reset.start), }, Tag.start_of_frame[0].start...Tag.start_of_frame[0].end => .{ .start_of_frame = @intCast(value - Tag.start_of_frame[0].start), }, Tag.start_of_frame[1].start...Tag.start_of_frame[1].end => .{ .start_of_frame = @intCast(value - Tag.start_of_frame[1].start + 4), }, Tag.start_of_frame[2].start...Tag.start_of_frame[2].end => .{ .start_of_frame = @intCast(value - Tag.start_of_frame[2].start + 7), }, Tag.application_segment.start...Tag.application_segment.end => .{ .application_segment = @intCast(value - Tag.application_segment.start), }, Tag.start_of_image => .{ .start_of_image = void{} }, Tag.end_of_image => .{ .end_of_image = void{} }, Tag.define_quantization_table => .{ .define_quantization_table = void{} }, Tag.define_huffman_table => .{ .define_huffman_table = void{} }, Tag.define_restart_interval => .{ .define_restart_interval = void{} }, Tag.define_number_of_lines => .{ .define_number_of_lines = void{} }, Tag.define_hierarchy_progression => .{ .define_hierarchy_progression = void{} }, Tag.extend_huffman_table => .{ .extend_huffman_table = void{} }, Tag.comment => .{ .comment = void{} }, Tag.start_of_scan => .{ .start_of_scan = void{} }, Tag.temporary => .{ .temporary = void{} }, else => error.InvalidByte, }; } }; test "marker" { const Testing = struct { pub fn empty_tag(value: comptime_int, tag: std.meta.Tag(Marker)) !void { const marker: Marker = try .from_u8(value); try std.testing.expectEqual( tag, std.meta.activeTag(marker), ); } }; for (0..8) |offset| { const marker: Marker = try .from_u8(0xd0 + @as(u8, @intCast(offset))); try std.testing.expectEqual(offset, marker.reset); } { for (0..4) |offset| { const marker: Marker = try .from_u8(0xc0 + @as(u8, @intCast(offset))); try std.testing.expectEqual(offset, marker.start_of_frame); } for (0..3) |offset| { const marker: Marker = try .from_u8(0xc5 + @as(u8, @intCast(offset))); try std.testing.expectEqual(offset + 4, marker.start_of_frame); } for (0..3) |offset| { const marker: Marker = try .from_u8(0xc9 + @as(u8, @intCast(offset))); try std.testing.expectEqual(offset + 7, marker.start_of_frame); } } for (0..16) |offset| { const marker: Marker = try .from_u8(0xe0 + @as(u8, @intCast(offset))); try std.testing.expectEqual(offset, marker.application_segment); } try Testing.empty_tag(Marker.Tag.temporary, .temporary); try Testing.empty_tag(Marker.Tag.start_of_image, .start_of_image); try Testing.empty_tag(Marker.Tag.end_of_image, .end_of_image); try Testing.empty_tag(Marker.Tag.define_quantization_table, .define_quantization_table); try Testing.empty_tag(Marker.Tag.define_huffman_table, .define_huffman_table); try Testing.empty_tag(Marker.Tag.define_restart_interval, .define_restart_interval); try Testing.empty_tag(Marker.Tag.define_number_of_lines, .define_number_of_lines); try Testing.empty_tag(Marker.Tag.define_hierarchy_progression, .define_hierarchy_progression); }