mirror of
https://codeberg.org/ziglings/exercises.git
synced 2026-03-28 21:34:52 +00:00
The first exercise introduces the `packed` keyword as an alternative for bitwise operations. Its main goals are establishing a solid understanding of field order and conveying the fact that packed containers are basically integers. It introduces the concept of container layouts and briefly explains the default `auto` layout before introducing the `packed` layout (but doesn't touch `extern` at all). The exercise also presents a real-world use case for packed containers, namely LZ4 frame descriptors. Furthermore it covers equality comparisons between packed containers. The second exercise talks about switch statements with packed containers and goes into some more detail on packed unions.
79 lines
2.0 KiB
Zig
79 lines
2.0 KiB
Zig
//
|
|
// We've already learned about switch statements in exercises 030, 031 and 108.
|
|
// They also work with packed containers:
|
|
|
|
const S = packed struct(u2) {
|
|
a: bool,
|
|
b: i1,
|
|
};
|
|
|
|
// Try to make it compile without adding an `else` prong!
|
|
|
|
comptime {
|
|
const s: S = .{ .a = true, .b = -1 };
|
|
switch (s) {
|
|
.{ .a = true, .b = -1 } => {}, // ok!
|
|
.{ .a = true, .b = ??? },
|
|
.{ .a = ???, .b = 0 },
|
|
.{ .a = ???, .b = ??? },
|
|
=> @compileError("We don't want to end up here!"),
|
|
}
|
|
}
|
|
|
|
// As we can see, switching on packed structs is pretty straightforward.
|
|
// When switching on packed unions however, we'll realize that a packed
|
|
// union never keeps track of its active tag, not even in debug mode! This
|
|
// means that packed unions compare solely by their bit pattern (again, just
|
|
// like integers).
|
|
|
|
const U = packed union(u2) {
|
|
a: u2,
|
|
b: i2,
|
|
};
|
|
|
|
// Find and remove the duplicate case!
|
|
|
|
comptime {
|
|
const u: U = .{ .a = 3 };
|
|
switch (u) {
|
|
.{ .a = 3 } => {}, // ok!
|
|
.{ .a = 2 },
|
|
.{ .b = 1 },
|
|
.{ .b = -1 },
|
|
.{ .a = 0 },
|
|
=> @compileError("We don't want to end up here!"),
|
|
}
|
|
}
|
|
|
|
// Since packed unions don't have the concept of an active tag, it's always legal
|
|
// to access any of their fields. This can be useful to view the same data from
|
|
// different perspectives seamlessly.
|
|
//
|
|
// Try to make the float below negative:
|
|
|
|
/// IEEE 754 half precision float
|
|
const Float = packed union(u16) {
|
|
value: f16,
|
|
bits: packed struct(u16) {
|
|
mantissa: u10,
|
|
exponent: u5,
|
|
sign: u1,
|
|
},
|
|
};
|
|
|
|
pub fn main() void {
|
|
// Reminder: if the sign bit of a float is set, the number is negative!
|
|
|
|
var number: Float = .{ .value = 2.34 };
|
|
number.bits.??? = ???;
|
|
if (number.value != -2.34) {
|
|
std.debug.print("Make it negative!\n", .{});
|
|
}
|
|
}
|
|
|
|
// This concludes our introduction to packed containers. The next time you need
|
|
// control over individual bits, keep them in mind as a potent alternative!
|
|
//
|
|
|
|
const std = @import("std");
|