From 2354ec05219e0707dafe3989580b7e279ab5efc5 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Wed, 28 Jun 2023 22:10:25 +1000 Subject: [PATCH 01/36] initial specification --- spec.md | 373 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 373 insertions(+) create mode 100644 spec.md diff --git a/spec.md b/spec.md new file mode 100644 index 00000000..7bcc33f7 --- /dev/null +++ b/spec.md @@ -0,0 +1,373 @@ +# Bitflags + +`bitflags` generates flags enums with well-defined semantics and ergonimic end-user APIs. + +You can use `bitflags` to: + +- provide more user-friendly bindings to C APIs where flags may or may not be fully known in advance. +- generate efficient options types with string parsing and formatting support. + +You can't use `bitflags` to: + +- guarantee only bits corresponding to defined flags will ever be set. + +## Definitions + +This section formally defines the terminology and semantics of `bitflags`. It's organized so more fundamental concepts are introduced before those that build on them. It may be helpful to start from the bottom of the section and refer back up to concepts defined earlier. + +### Bits type + +> A type that stores a fixed number bits. + +Bits types are typically fixed-width unsigned integers, like `u32`, but may be more exotic. + +#### Bit + +> A value at a specific location within a bits type that may be set or unset. + +### Flag + +> A uniquely named set of bits in a bits type. + +Names must be unique, but bits are not required to be exclusive to a flag. Bits are not required to be contiguous. + +#### Zero-bit flag + +> A flag with a set of zero bits. + +#### Single-bit flag + +> A flag with a set of one bit. + +#### Multi-bit flag + +> A flag with a set of more than one bit. + +### Flags type + +> A set of defined flags over a specific bits type. + +#### Known bit + +> A bit in any defined flag. + +#### Unknown bit + +> A bit not in any defined flag. + +#### Normal + +> A flags type that defines no zero-bit flags and all known bits are in at least one corresponding single-bit flag. + +A flags type may still be normal if it defines multi-bit flags so long as each of its bits is also in a defined single-bit flag. + +In cases where flags are defined by an external source, a flags type can define a single flag with all bits set. This flags type is not normal, but guarantees no bits will ever be truncated. + +### Flags value + +> An instance of a flags type using its bits type for storage. + +#### Empty + +> Whether all bits in a flags value are unset. + +If any known or unknown bits are set then the flags value is considered not empty. + +#### All + +> Whether all defined flags are contained in a flags value. + +Unknown bits don't affect whether a flags value is all. It's not a strict equality condition like empty. + +#### Contains + +> Whether all set bits in a source flags value are also set in a target flags value. + +If the tatget is empty then the source will always contain it. A flag is contained in a flags value if all bits in the flag are set in the flags value. + +#### Intersects + +> Whether any set bits in a source flags value are also set in a target flags value. + +An empty flags value never intersects any other flags value. A flag intersects a flags value if any bits in the flag are set in the flags value. + +#### Normalized + +> Whether all set bits in a flags value are known bits and all defined flags that intersect are also contained. + +A consequence of zero-bit flags always being contained but never intersected means a flags type that defines one can never be normalized. + +### Operations + +#### Truncate + +> Unset all unknown bits in a flags value. + +If the flags type is normal then the result is normalized. If the flags type is not normal then truncating doesn't guarantee a normalized result; only one with no unknown bits set. This allows truncation to be implemented efficiently. + +#### Union + +> The bitwise or (`|`) of the bits in two flags values, truncating the result. + +#### Intersection + +> The bitwise and (`&`) of the bits in two flags values, truncating the result. + +#### Symmetric difference + +> The bitwise exclusive-or (`^`) of the bits in two flags values, truncating the result. + +#### Complement + +> The bitwise negation (`!`) of the bits in a flags value, truncating the result. + +#### Difference + +> The intersection of a source flags value with the negation of a target flags value (`&!`), truncating the result. + +### Formatting + +Flags values can be formatted and parsed using the following *whitespace-insensitive*, *case-sensitive* grammar: + +- _Flags:_ (_Flag_)`|`* +- _Flag:_ _Name_ | _HexNumber_ +- _Name:_ The name of any defined flag +- _HexNumber_: `0x`([0-9a-fA-F])* + +If a source flags value is formatted and then parsed the result will exactly reproduce the source. + +## Implementation + +The specification is implemented through the `Flags` trait. An implementor of the `Flags` trait is a flags type. An instance of the implementor is a flags value. + +### `type Bits` + +```rust +type Bits: Bits; +``` + +The bits type used. + +### `const FLAGS` + +```rust +const FLAGS: &'static [Flag]; +``` + +Defines the set of flags. + +### `fn bits` + +```rust +fn bits(&self) -> Self::Bits; +``` + +Get the value of the underlying bits type. + +The result won't be truncated. + +### `fn from_bits_truncate` + +```rust +fn from_bits_truncate(bits: Self::Bits) -> Self; +``` + +Get a flags value with only the known bits in `bits` set. + +### `fn from_bits` + +```rust +fn from_bits(bits: Self::Bits) -> Option; +``` + +Get a flags value with only the known bits in `bits` set. + +If the result is non-empty this function will return `Some`, otherwise it will return `None`. + +### `fn from_bits_retain` + +```rust +fn from_bits_retain(bits: Self::Bits) -> Self; +``` + +Get a flags value with exactly the bits in `bits` set. + +Prefer `from_bits_truncate` where possible; this method is necessary as a building block, but not intended for end-users. If `bits` has any unknown bits set then they'll be truncated by any operations on the returned flags type. + +### `fn from_name` + +```rust +fn from_name(name: &str) -> Option; +``` + +Get a flags value with the bits for a defined flag with the given name set. + +If there is a flag defined with `name` this function will return `Some`, otherwise it will return `None`. Names are case-sensitive. + +### `fn empty` + +```rust +fn empty() -> Self; +``` + +Get a flags value with all bits unset. + +The returned flags value will satisfy `is_empty`. + +### `fn all` + +```rust +fn all() -> Self; +``` + +Get a flags value with all known bits set and all unknown bits unset. + +The returned flags value will satisfy `is_all`. + +### `fn is_empty` + +```rust +fn is_empty(&self) -> bool; +``` + +Whether all bits in the flags value are unset. + +### `fn is_all` + +```rust +fn is_all(&self) -> bool; +``` + +Whether all defined flags are contained in the flags value. + +### `fn intersection` + +```rust +fn intersection(self, other: Self) -> Self; +``` + +Calculates the intersection of the bits in `self` and `other`. + +The result will be truncated. + +### `fn intersects` + +```rust +fn intersects(&self, other: Self) -> bool; +``` + +Whether `self` and `other` intersect. + +### `fn contains` + +```rust +fn contains(&self, other: Self) -> bool; +``` + +Whether `self` contains `other`. + +### `fn union` + +```rust +fn union(self, other: Self) -> Self; +``` + +Calculates the union of the bits in `self` and `other`. + +The result will be truncated. + +### `fn insert` + +```rust +fn insert(&mut self, other: Self); +``` + +Assigns the union of the bits in `self` and `other`. + +The result will be truncated. + +### `fn difference` + +```rust +fn difference(self, other: Self) -> Self; +``` + +Calculates the difference between the bits in `self` and `other`. + +The result will be truncated. + +### `fn remove` + +```rust +fn remove(&mut self, other: Self); +``` + +Assigns the difference between the bits in `self` and `other`. + +The result will be truncated. + +### `fn set` + +```rust +fn set(&mut self, other: Self, value: bool); +``` + +Assigns the union of `self` and `other` if `value` is `true`, or the difference between `self` and `other` if `value` is `false`. + +The result will be truncated. + +### `fn symmetric_difference` + +```rust +fn symmetric_difference(self, other: Self) -> Self; +``` + +Calculates the symmetric difference between the bits in `self` and `other`. + +The result will be truncated. + +### `fn toggle` + +```rust +fn toggle(&mut self, other: Self); +``` + +Calculates the symmetric difference between the bits in `self` and `other`. + +The result will be truncated. + +### `fn complement` + +```rust +fn complement(self) -> Self; +``` + +Calculates the complement of the bits in `self`. + +The result will be truncated. + +### `fn iter` + +```rust +fn iter(&self) -> iter::Iter; +``` + +Iterate over defined flags contained in `self`. + +The result of unioning all yielded flags will exactly reproduce `self`. + +Each yielded flags value will correspond to a single flag. Not all flags contained in `self` are guaranteed to be yielded; only enough to exactly reproduce `self`. Overlapping flags may be omitted. + +If `self` is not normalized then any remaining bits will be yielded as a final result. + +### `fn iter_names` + +```rust +fn iter_names(&self) -> iter::IterNames; +``` + +Iterate over defined flags and their names contained in `self`. + +The result of unioning all yielded flags will lossily normalize `self`. + +If `self` is normalized then the result of unioning all yielded flags will exactly reproduce `self`. If `self` is not normalized then any remaining bits will not be yielded. Not all flags contained in `self` are guaranteed to be yielded; only enough to lossily normalize `self`. From 61f56e517501b5d7fea914d3dbbac246b4f1e006 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Thu, 29 Jun 2023 00:11:30 +1000 Subject: [PATCH 02/36] Update spec.md --- spec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec.md b/spec.md index 7bcc33f7..6b3fe868 100644 --- a/spec.md +++ b/spec.md @@ -1,6 +1,6 @@ # Bitflags -`bitflags` generates flags enums with well-defined semantics and ergonimic end-user APIs. +`bitflags` generates flags enums with well-defined semantics and ergonomic end-user APIs. You can use `bitflags` to: From 1abf2a78b95739f85ce92516fc2b78241a070330 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Thu, 29 Jun 2023 08:41:31 +1000 Subject: [PATCH 03/36] add sections for iteration and formatting --- spec.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/spec.md b/spec.md index 6b3fe868..a632ecfb 100644 --- a/spec.md +++ b/spec.md @@ -9,7 +9,7 @@ You can use `bitflags` to: You can't use `bitflags` to: -- guarantee only bits corresponding to defined flags will ever be set. +- guarantee only bits corresponding to defined flags will ever be set. `bitflags` is a light wrapper over an integer type, not a language feature pollyfill. ## Definitions @@ -125,16 +125,24 @@ If the flags type is normal then the result is normalized. If the flags type is > The intersection of a source flags value with the negation of a target flags value (`&!`), truncating the result. +### Iteration + +> Yield a set of name and flags value pairs from a source flags value, where the result of unioning all yielded flags values together will lossily normalize the source. + +Each yielded flags value sets exactly the bits of a defined flag and is paired with its name. If the source is normalized then the result of unioning all yielded flags values together will exactly reproduce the source. If the source is not normalized then any bits that aren't in the set of any contained flag will not be yielded. + ### Formatting Flags values can be formatted and parsed using the following *whitespace-insensitive*, *case-sensitive* grammar: - _Flags:_ (_Flag_)`|`* -- _Flag:_ _Name_ | _HexNumber_ +- _Flag:_ _Name_ | _Hex Number_ - _Name:_ The name of any defined flag -- _HexNumber_: `0x`([0-9a-fA-F])* +- _Hex Number_: `0x`([0-9a-fA-F])* + +Flags values are formatted by iterating over defined flags in a source flags value. If the source is not normalized then any bits not in the set of any contained flag will format as a hex number. -If a source flags value is formatted and then parsed the result will exactly reproduce the source. +Parsing a formatted flags value will exactly reproduce it. ## Implementation From e6885663902a1b739624d31d634c9e92bba91058 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Thu, 29 Jun 2023 10:49:56 +1000 Subject: [PATCH 04/36] add examples --- spec.md | 310 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 308 insertions(+), 2 deletions(-) diff --git a/spec.md b/spec.md index a632ecfb..db733921 100644 --- a/spec.md +++ b/spec.md @@ -15,6 +15,8 @@ You can't use `bitflags` to: This section formally defines the terminology and semantics of `bitflags`. It's organized so more fundamental concepts are introduced before those that build on them. It may be helpful to start from the bottom of the section and refer back up to concepts defined earlier. +Examples use `bitflags` syntax with `u8` as the bits type. + ### Bits type > A type that stores a fixed number bits. @@ -35,14 +37,40 @@ Names must be unique, but bits are not required to be exclusive to a flag. Bits > A flag with a set of zero bits. +---- + +The following is a zero-bit flag: + +```rust +const ZERO = 0b0000_0000; +``` + #### Single-bit flag > A flag with a set of one bit. +---- + +The following are single-bit flags: + +```rust +const A = 0b0000_0001; +const B = 0b0000_0010; +``` + #### Multi-bit flag > A flag with a set of more than one bit. +---- + +The following are multi-bit flags: + +```rust +const A = 0b0000_0011; +const B = 0b1111_1111; +``` + ### Flags type > A set of defined flags over a specific bits type. @@ -51,17 +79,89 @@ Names must be unique, but bits are not required to be exclusive to a flag. Bits > A bit in any defined flag. +---- + +In the following flags type: + +```rust +struct Flags { + const A = 0b0000_0001; + const B = 0b0000_0010; + const C = 0b0000_0100; +} +``` + +the known bits are: + +```rust +0b0000_0111 +``` + #### Unknown bit > A bit not in any defined flag. +---- + +In the following flags type: + +```rust +struct Flags { + const A = 0b0000_0001; + const B = 0b0000_0010; + const C = 0b0000_0100; +} +``` + +the unknown bits are: + +```rust +0b1111_1000 +``` + #### Normal > A flags type that defines no zero-bit flags and all known bits are in at least one corresponding single-bit flag. A flags type may still be normal if it defines multi-bit flags so long as each of its bits is also in a defined single-bit flag. -In cases where flags are defined by an external source, a flags type can define a single flag with all bits set. This flags type is not normal, but guarantees no bits will ever be truncated. +---- + +The following are all normal flags types: + +```rust +struct NormalA { + const A = 0b0000_0001; + const B = 0b0000_0010; + const AB = 0b0000_0011; +} + +struct NormalB {} +``` + +The following are all not normal flags types: + +```rust +struct NotNormalA { + const A = 0b0000_0011; +} + +struct NotNormalB { + const A = 0b0000_0000; +} +``` + +---- + +In cases where flags are defined by an external source, a flags type can define a single flag with all bits set: + +```rust +struct External { + const ALL = 0b1111_1111; +} +``` + +This flags type is not normal, but guarantees no bits will ever be truncated. ### Flags value @@ -73,58 +173,242 @@ In cases where flags are defined by an external source, a flags type can define If any known or unknown bits are set then the flags value is considered not empty. +---- + +The following flags value is empty: + +```rust +0b0000_0000 +``` + +The following flags values are not empty: + +```rust +0b0000_0001 +0b0110_0000 +``` + #### All > Whether all defined flags are contained in a flags value. Unknown bits don't affect whether a flags value is all. It's not a strict equality condition like empty. +---- + +Given a flags type: + +```rust +struct Flags { + const A = 0b0000_0001; + const B = 0b0000_0010; +} +``` + +the following flags values all satisfy all: + +```rust +0b0000_0011 +0b1000_0011 +0b1111_1111 +``` + #### Contains > Whether all set bits in a source flags value are also set in a target flags value. If the tatget is empty then the source will always contain it. A flag is contained in a flags value if all bits in the flag are set in the flags value. +---- + +Given the flags value: + +```rust +0b0000_0011 +``` + +the following flags values are contained: + +```rust +0b0000_0000 +0b0000_0010 +0b0000_0001 +0b0000_0011 +``` + +but the following flags values are not contained: + +```rust +0b0000_1000 +0b0000_0110 +``` + #### Intersects > Whether any set bits in a source flags value are also set in a target flags value. An empty flags value never intersects any other flags value. A flag intersects a flags value if any bits in the flag are set in the flags value. +---- + +Given the flags value: + +```rust +0b0000_0011 +``` + +the following flags intersect: + +```rust +0b0000_0010 +0b0000_0001 +0b1111_1111 +``` + +but the following flags values do not intersect: + +```rust +0b0000_0000 +0b1111_0000 +``` + #### Normalized > Whether all set bits in a flags value are known bits and all defined flags that intersect are also contained. A consequence of zero-bit flags always being contained but never intersected means a flags type that defines one can never be normalized. +---- + +Given the flags type: + +```rust +struct Flags { + const A = 0b0000_0001; + const B = 0b0000_0010; + const C = 0b0000_1100; +} +``` + +the following flags values are normalized: + +```rust +0b0000_0000 +0b0000_0001 +0b0000_0010 +0b0000_1100 +0b0000_1111 +``` + +but the following flags values are not normalized: + +```rust +0b1111_1111 +0b0000_1000 +``` + ### Operations +Examples in this section all use the given flags type: + +```rust +struct Flags { + const A = 0b0000_0001; + const B = 0b0000_0010; + const C = 0b0000_1100; +} +``` + +The definition of this flags type has implications on the results of most operations. + #### Truncate > Unset all unknown bits in a flags value. If the flags type is normal then the result is normalized. If the flags type is not normal then truncating doesn't guarantee a normalized result; only one with no unknown bits set. This allows truncation to be implemented efficiently. +---- + +Given the flags value: + +```rust +0b1111_1111 +``` + +the result of truncation will be: + +```rust +0b0000_1111 +``` + #### Union > The bitwise or (`|`) of the bits in two flags values, truncating the result. +---- + +The following are examples of the result of unioning flags values: + +```rust +0b0000_0001 | 0b0000_0010 = 0b0000_0011 +0b0000_0000 | 0b1111_1111 = 0b0000_1111 +``` + #### Intersection > The bitwise and (`&`) of the bits in two flags values, truncating the result. +---- + +The following are examples of the result of intersecting flags values: + +```rust +0b0000_0001 & 0b0000_0010 = 0b0000_0000 +0b1111_1100 & 0b1111_0111 = 0b0000_0100 +``` + #### Symmetric difference -> The bitwise exclusive-or (`^`) of the bits in two flags values, truncating the result. +> The bitwise exclusive-or (`^`) of the bits in two flags values, truncating the result. + +---- + +The following are examples of the symmetric difference between two flags values: + +```rust +0b0000_0001 ^ 0b0000_0010 = 0b0000_0011 +0b0000_1111 ^ 0b0000_0011 = 0b0000_1100 +0b1100_0000 ^ 0b0011_0000 = 0b0000_0000 +``` #### Complement > The bitwise negation (`!`) of the bits in a flags value, truncating the result. +---- + +The following are examples of the complement of a flags value: + +```rust +!0b0000_0000 = 0b0000_1111 +!0b0000_1111 = 0b0000_0000 +!0b1111_1000 = 0b0000_0111 +``` + #### Difference > The intersection of a source flags value with the negation of a target flags value (`&!`), truncating the result. +---- + +The following are examples of the difference between two flags values: + +```rust +0b0000_0001 & !0b0000_0010 = 0b0000_0001 +0b0000_1101 & !0b0000_0011 = 0b0000_1100 +0b1111_1111 & !0b0000_0001 = 0b0000_1110 +``` + ### Iteration > Yield a set of name and flags value pairs from a source flags value, where the result of unioning all yielded flags values together will lossily normalize the source. @@ -144,6 +428,28 @@ Flags values are formatted by iterating over defined flags in a source flags val Parsing a formatted flags value will exactly reproduce it. +---- + +Given the following flags type: + +```rust +struct Flags { + const A = 0b0000_0001; + const B = 0b0000_0010; + const AB = 0b0000_0011; +} +``` + +The following are examples of how flags values can be formatted: + +```rust +0b0000_0001 = "A" +0b0000_0010 = "B" +0b0000_0011 = "A | B" +0b1000_0000 = "0x80" +0b1111_1111 = "A | B | 0xfc" +``` + ## Implementation The specification is implemented through the `Flags` trait. An implementor of the `Flags` trait is a flags type. An instance of the implementor is a flags value. From 5effb87fb66383b503f0425d47cdda551d8373fd Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Thu, 29 Jun 2023 10:52:33 +1000 Subject: [PATCH 05/36] change formatting --- spec.md | 72 +++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 23 deletions(-) diff --git a/spec.md b/spec.md index db733921..e55d2946 100644 --- a/spec.md +++ b/spec.md @@ -19,23 +19,29 @@ Examples use `bitflags` syntax with `u8` as the bits type. ### Bits type -> A type that stores a fixed number bits. +A type that stores a fixed number bits. + +---- Bits types are typically fixed-width unsigned integers, like `u32`, but may be more exotic. #### Bit -> A value at a specific location within a bits type that may be set or unset. +A value at a specific location within a bits type that may be set or unset. + +---- ### Flag -> A uniquely named set of bits in a bits type. +A uniquely named set of bits in a bits type. + +---- Names must be unique, but bits are not required to be exclusive to a flag. Bits are not required to be contiguous. #### Zero-bit flag -> A flag with a set of zero bits. +A flag with a set of zero bits. ---- @@ -47,7 +53,7 @@ const ZERO = 0b0000_0000; #### Single-bit flag -> A flag with a set of one bit. +A flag with a set of one bit. ---- @@ -60,7 +66,7 @@ const B = 0b0000_0010; #### Multi-bit flag -> A flag with a set of more than one bit. +A flag with a set of more than one bit. ---- @@ -73,11 +79,13 @@ const B = 0b1111_1111; ### Flags type -> A set of defined flags over a specific bits type. +A set of defined flags over a specific bits type. + +---- #### Known bit -> A bit in any defined flag. +A bit in any defined flag. ---- @@ -99,7 +107,7 @@ the known bits are: #### Unknown bit -> A bit not in any defined flag. +A bit not in any defined flag. ---- @@ -121,7 +129,9 @@ the unknown bits are: #### Normal -> A flags type that defines no zero-bit flags and all known bits are in at least one corresponding single-bit flag. +A flags type that defines no zero-bit flags and all known bits are in at least one corresponding single-bit flag. + +---- A flags type may still be normal if it defines multi-bit flags so long as each of its bits is also in a defined single-bit flag. @@ -165,11 +175,15 @@ This flags type is not normal, but guarantees no bits will ever be truncated. ### Flags value -> An instance of a flags type using its bits type for storage. +An instance of a flags type using its bits type for storage. + +---- #### Empty -> Whether all bits in a flags value are unset. +Whether all bits in a flags value are unset. + +---- If any known or unknown bits are set then the flags value is considered not empty. @@ -190,7 +204,9 @@ The following flags values are not empty: #### All -> Whether all defined flags are contained in a flags value. +Whether all defined flags are contained in a flags value. + +---- Unknown bits don't affect whether a flags value is all. It's not a strict equality condition like empty. @@ -215,7 +231,9 @@ the following flags values all satisfy all: #### Contains -> Whether all set bits in a source flags value are also set in a target flags value. +Whether all set bits in a source flags value are also set in a target flags value. + +---- If the tatget is empty then the source will always contain it. A flag is contained in a flags value if all bits in the flag are set in the flags value. @@ -245,7 +263,9 @@ but the following flags values are not contained: #### Intersects -> Whether any set bits in a source flags value are also set in a target flags value. +Whether any set bits in a source flags value are also set in a target flags value. + +---- An empty flags value never intersects any other flags value. A flag intersects a flags value if any bits in the flag are set in the flags value. @@ -274,7 +294,9 @@ but the following flags values do not intersect: #### Normalized -> Whether all set bits in a flags value are known bits and all defined flags that intersect are also contained. +Whether all set bits in a flags value are known bits and all defined flags that intersect are also contained. + +---- A consequence of zero-bit flags always being contained but never intersected means a flags type that defines one can never be normalized. @@ -323,7 +345,9 @@ The definition of this flags type has implications on the results of most operat #### Truncate -> Unset all unknown bits in a flags value. +Unset all unknown bits in a flags value. + +---- If the flags type is normal then the result is normalized. If the flags type is not normal then truncating doesn't guarantee a normalized result; only one with no unknown bits set. This allows truncation to be implemented efficiently. @@ -343,7 +367,7 @@ the result of truncation will be: #### Union -> The bitwise or (`|`) of the bits in two flags values, truncating the result. +The bitwise or (`|`) of the bits in two flags values, truncating the result. ---- @@ -356,7 +380,7 @@ The following are examples of the result of unioning flags values: #### Intersection -> The bitwise and (`&`) of the bits in two flags values, truncating the result. +The bitwise and (`&`) of the bits in two flags values, truncating the result. ---- @@ -369,7 +393,7 @@ The following are examples of the result of intersecting flags values: #### Symmetric difference -> The bitwise exclusive-or (`^`) of the bits in two flags values, truncating the result. +The bitwise exclusive-or (`^`) of the bits in two flags values, truncating the result. ---- @@ -383,7 +407,7 @@ The following are examples of the symmetric difference between two flags values: #### Complement -> The bitwise negation (`!`) of the bits in a flags value, truncating the result. +The bitwise negation (`!`) of the bits in a flags value, truncating the result. ---- @@ -397,7 +421,7 @@ The following are examples of the complement of a flags value: #### Difference -> The intersection of a source flags value with the negation of a target flags value (`&!`), truncating the result. +The intersection of a source flags value with the negation of a target flags value (`&!`), truncating the result. ---- @@ -411,7 +435,9 @@ The following are examples of the difference between two flags values: ### Iteration -> Yield a set of name and flags value pairs from a source flags value, where the result of unioning all yielded flags values together will lossily normalize the source. +Yield a set of name and flags value pairs from a source flags value, where the result of unioning all yielded flags values together + +----will lossily normalize the source. Each yielded flags value sets exactly the bits of a defined flag and is paired with its name. If the source is normalized then the result of unioning all yielded flags values together will exactly reproduce the source. If the source is not normalized then any bits that aren't in the set of any contained flag will not be yielded. From 0042d63b69120f779a4aa724a4ef924a459d516b Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Thu, 29 Jun 2023 10:53:38 +1000 Subject: [PATCH 06/36] remove spurious section breaks --- spec.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/spec.md b/spec.md index e55d2946..f8842562 100644 --- a/spec.md +++ b/spec.md @@ -29,8 +29,6 @@ Bits types are typically fixed-width unsigned integers, like `u32`, but may be m A value at a specific location within a bits type that may be set or unset. ----- - ### Flag A uniquely named set of bits in a bits type. @@ -81,8 +79,6 @@ const B = 0b1111_1111; A set of defined flags over a specific bits type. ----- - #### Known bit A bit in any defined flag. @@ -177,8 +173,6 @@ This flags type is not normal, but guarantees no bits will ever be truncated. An instance of a flags type using its bits type for storage. ----- - #### Empty Whether all bits in a flags value are unset. From d8cfa93d002dd30c312c2f401ef7c5c6405956e2 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Thu, 29 Jun 2023 11:04:09 +1000 Subject: [PATCH 07/36] tighten up formatting --- spec.md | 36 +++++++----------------------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/spec.md b/spec.md index f8842562..17f8102e 100644 --- a/spec.md +++ b/spec.md @@ -129,10 +129,6 @@ A flags type that defines no zero-bit flags and all known bits are in at least o ---- -A flags type may still be normal if it defines multi-bit flags so long as each of its bits is also in a defined single-bit flag. - ----- - The following are all normal flags types: ```rust @@ -157,8 +153,6 @@ struct NotNormalB { } ``` ----- - In cases where flags are defined by an external source, a flags type can define a single flag with all bits set: ```rust @@ -179,10 +173,6 @@ Whether all bits in a flags value are unset. ---- -If any known or unknown bits are set then the flags value is considered not empty. - ----- - The following flags value is empty: ```rust @@ -202,10 +192,6 @@ Whether all defined flags are contained in a flags value. ---- -Unknown bits don't affect whether a flags value is all. It's not a strict equality condition like empty. - ----- - Given a flags type: ```rust @@ -227,9 +213,7 @@ the following flags values all satisfy all: Whether all set bits in a source flags value are also set in a target flags value. ----- - -If the tatget is empty then the source will always contain it. A flag is contained in a flags value if all bits in the flag are set in the flags value. +A flag is contained in a flags value if all bits in the flag are set in the flags value. ---- @@ -259,9 +243,7 @@ but the following flags values are not contained: Whether any set bits in a source flags value are also set in a target flags value. ----- - -An empty flags value never intersects any other flags value. A flag intersects a flags value if any bits in the flag are set in the flags value. +A flag intersects a flags value if any bits in the flag are set in the flags value. ---- @@ -292,10 +274,6 @@ Whether all set bits in a flags value are known bits and all defined flags that ---- -A consequence of zero-bit flags always being contained but never intersected means a flags type that defines one can never be normalized. - ----- - Given the flags type: ```rust @@ -323,6 +301,8 @@ but the following flags values are not normalized: 0b0000_1000 ``` +A consequence of zero-bit flags always being contained but never intersected means a flags type that defines one can never be normalized. + ### Operations Examples in this section all use the given flags type: @@ -341,9 +321,7 @@ The definition of this flags type has implications on the results of most operat Unset all unknown bits in a flags value. ----- - -If the flags type is normal then the result is normalized. If the flags type is not normal then truncating doesn't guarantee a normalized result; only one with no unknown bits set. This allows truncation to be implemented efficiently. +If the flags type is normal then the result is normalized. ---- @@ -429,9 +407,9 @@ The following are examples of the difference between two flags values: ### Iteration -Yield a set of name and flags value pairs from a source flags value, where the result of unioning all yielded flags values together +Yield a set of name and flags value pairs from a source flags value, where the result of unioning all yielded flags values together will lossily normalize the source. -----will lossily normalize the source. +---- Each yielded flags value sets exactly the bits of a defined flag and is paired with its name. If the source is normalized then the result of unioning all yielded flags values together will exactly reproduce the source. If the source is not normalized then any bits that aren't in the set of any contained flag will not be yielded. From 681ea7df124df82631560e25e81a64faf3cf8e73 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Thu, 29 Jun 2023 21:16:43 +1000 Subject: [PATCH 08/36] update iteration and formatting --- spec.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/spec.md b/spec.md index 17f8102e..cc7441fe 100644 --- a/spec.md +++ b/spec.md @@ -10,6 +10,7 @@ You can use `bitflags` to: You can't use `bitflags` to: - guarantee only bits corresponding to defined flags will ever be set. `bitflags` is a light wrapper over an integer type, not a language feature pollyfill. +- define bitfields. It only generates types where bits correspond to flags and set functions over them. ## Definitions @@ -305,6 +306,8 @@ A consequence of zero-bit flags always being contained but never intersected mea ### Operations +A flags type must implement the operations in this section. + Examples in this section all use the given flags type: ```rust @@ -407,11 +410,11 @@ The following are examples of the difference between two flags values: ### Iteration -Yield a set of name and flags value pairs from a source flags value, where the result of unioning all yielded flags values together will lossily normalize the source. +Yield a set of flags values from a source flags value, where the result of unioning all yielded flags values together will exactly reproduce the source. ---- -Each yielded flags value sets exactly the bits of a defined flag and is paired with its name. If the source is normalized then the result of unioning all yielded flags values together will exactly reproduce the source. If the source is not normalized then any bits that aren't in the set of any contained flag will not be yielded. +Each yielded flags value should set exactly the bits of a defined flag contained in the source. Any bits that aren't in the set of any contained flag should be yielded together as a final flags value. ### Formatting @@ -422,7 +425,7 @@ Flags values can be formatted and parsed using the following *whitespace-insensi - _Name:_ The name of any defined flag - _Hex Number_: `0x`([0-9a-fA-F])* -Flags values are formatted by iterating over defined flags in a source flags value. If the source is not normalized then any bits not in the set of any contained flag will format as a hex number. +Flags values can be formatted by iterating over them. Any yielded flags value that sets exactly the bits of a defined flag may be formatted with its name. Any yielded flags value that doesn't set exactly the bits of a defined flag will be formatted as a hex number. Parsing a formatted flags value will exactly reproduce it. @@ -442,7 +445,9 @@ The following are examples of how flags values can be formatted: ```rust 0b0000_0001 = "A" +0b0000_0001 = "0x1" 0b0000_0010 = "B" +0b0000_0010 = "0x2" 0b0000_0011 = "A | B" 0b1000_0000 = "0x80" 0b1111_1111 = "A | B | 0xfc" @@ -450,6 +455,8 @@ The following are examples of how flags values can be formatted: ## Implementation +> This section is just here to link the spec to what's implemented; it should be removed in favor of regular doc comments. + The specification is implemented through the `Flags` trait. An implementor of the `Flags` trait is a flags type. An instance of the implementor is a flags value. ### `type Bits` From fdd5339fb834eb4a2c3bfa93ce3ecdbb6cdfbcc2 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Thu, 29 Jun 2023 21:44:21 +1000 Subject: [PATCH 09/36] reword iteration to reflect truncation --- spec.md | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/spec.md b/spec.md index cc7441fe..651300b2 100644 --- a/spec.md +++ b/spec.md @@ -306,8 +306,6 @@ A consequence of zero-bit flags always being contained but never intersected mea ### Operations -A flags type must implement the operations in this section. - Examples in this section all use the given flags type: ```rust @@ -364,6 +362,7 @@ The following are examples of the result of intersecting flags values: ```rust 0b0000_0001 & 0b0000_0010 = 0b0000_0000 0b1111_1100 & 0b1111_0111 = 0b0000_0100 +0b1111_1111 & 0b1111_1111 = 0b0000_1111 ``` #### Symmetric difference @@ -410,12 +409,54 @@ The following are examples of the difference between two flags values: ### Iteration -Yield a set of flags values from a source flags value, where the result of unioning all yielded flags values together will exactly reproduce the source. +Yield a set of flags values contained in a source flags value, where all set bits in the source are set in the yielded flags values. ---- +The result of unioning all yielded flags values will be the truncated source. Any unknown bits that are present in the source are still yielded. + Each yielded flags value should set exactly the bits of a defined flag contained in the source. Any bits that aren't in the set of any contained flag should be yielded together as a final flags value. +---- + +Given the following flags type: + +```rust +struct Flags { + const A = 0b0000_0001; + const B = 0b0000_0010; + const AB = 0b0000_0011; +} +``` + +and the following flags value: + +```rust +0b0000_1111 +``` + +When iterated it may yield a flags value for `A` and `B`, with a final flags value of the remaining bits: + +```rust +0b0000_0001 +0b0000_0010 +0b0000_1100 +``` + +It may also yield a flags value for `AB`, with a final flags value of the remaining bits: + +```rust +0b0000_0011 +0b0000_1100 +``` + +It may also yield any combination of flags values + +```rust +0b0000_0111 +0b0000_1000 +``` + ### Formatting Flags values can be formatted and parsed using the following *whitespace-insensitive*, *case-sensitive* grammar: From e7bdead32cca05cac052ab30fd8fb7759f714027 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Thu, 29 Jun 2023 22:35:50 +1000 Subject: [PATCH 10/36] don't require iterating or formatting unknown bits --- spec.md | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/spec.md b/spec.md index 651300b2..881f8811 100644 --- a/spec.md +++ b/spec.md @@ -9,8 +9,8 @@ You can use `bitflags` to: You can't use `bitflags` to: -- guarantee only bits corresponding to defined flags will ever be set. `bitflags` is a light wrapper over an integer type, not a language feature pollyfill. -- define bitfields. It only generates types where bits correspond to flags and set functions over them. +- guarantee only bits corresponding to defined flags will ever be set. `bitflags` is a light wrapper over an integer type with additional semantics. It's not a native language feature. +- define bitfields. `bitflags` only generates types where set bits denote the presence of some combination of flags. ## Definitions @@ -409,11 +409,11 @@ The following are examples of the difference between two flags values: ### Iteration -Yield a set of flags values contained in a source flags value, where all set bits in the source are set in the yielded flags values. +Yield a set of flags values contained in a source flags value, where the result of unioning the yielded flags values will be the truncated source. ---- -The result of unioning all yielded flags values will be the truncated source. Any unknown bits that are present in the source are still yielded. +Any unknown bits that are present in the source may still be yielded. Each yielded flags value should set exactly the bits of a defined flag contained in the source. Any bits that aren't in the set of any contained flag should be yielded together as a final flags value. @@ -457,6 +457,13 @@ It may also yield any combination of flags values 0b0000_1000 ``` +Unknown bits aren't required to be yielded: + +```rust +0b0000_0001 +0b0000_0010 +``` + ### Formatting Flags values can be formatted and parsed using the following *whitespace-insensitive*, *case-sensitive* grammar: @@ -468,7 +475,7 @@ Flags values can be formatted and parsed using the following *whitespace-insensi Flags values can be formatted by iterating over them. Any yielded flags value that sets exactly the bits of a defined flag may be formatted with its name. Any yielded flags value that doesn't set exactly the bits of a defined flag will be formatted as a hex number. -Parsing a formatted flags value will exactly reproduce it. +The result of parsing a formatted flags value will be the truncated source. ---- From 32892e4b38efd0c1a7abda83eb5a9dd69dd97f84 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Fri, 30 Jun 2023 08:43:42 +1000 Subject: [PATCH 11/36] clarify more iteration and formatting --- spec.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/spec.md b/spec.md index 881f8811..f98ed513 100644 --- a/spec.md +++ b/spec.md @@ -413,9 +413,7 @@ Yield a set of flags values contained in a source flags value, where the result ---- -Any unknown bits that are present in the source may still be yielded. - -Each yielded flags value should set exactly the bits of a defined flag contained in the source. Any bits that aren't in the set of any contained flag should be yielded together as a final flags value. +To be most useful, each yielded flags value should set exactly the bits of a defined flag contained in the source. Any bits that aren't in the set of any contained flag should be yielded together as a final flags value. Unknown bits that are present in the source should still be yielded to avoid possible confusion, but are not required. ---- @@ -473,9 +471,9 @@ Flags values can be formatted and parsed using the following *whitespace-insensi - _Name:_ The name of any defined flag - _Hex Number_: `0x`([0-9a-fA-F])* -Flags values can be formatted by iterating over them. Any yielded flags value that sets exactly the bits of a defined flag may be formatted with its name. Any yielded flags value that doesn't set exactly the bits of a defined flag will be formatted as a hex number. +Flags values can be formatted by iterating over them. Any yielded flags value that sets exactly the bits of a defined flag should be formatted with its name. Any yielded flags value that doesn't set exactly the bits of a defined flag must be formatted as a hex number. -The result of parsing a formatted flags value will be the truncated source. +The result of parsing a formatted flags value will be the truncated source. An empty formatted flags value is zero. ---- @@ -498,7 +496,10 @@ The following are examples of how flags values can be formatted: 0b0000_0010 = "0x2" 0b0000_0011 = "A | B" 0b1000_0000 = "0x80" +0b1000_0000 = "0x0" +0b1000_0000 = "" 0b1111_1111 = "A | B | 0xfc" +0b1111_1111 = "A | B" ``` ## Implementation From e7b069da2db70ea1d5c10884dc6ad9a12b3112aa Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Fri, 30 Jun 2023 13:43:25 +1000 Subject: [PATCH 12/36] add unnamed flags --- spec.md | 47 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/spec.md b/spec.md index f98ed513..c3a0c52c 100644 --- a/spec.md +++ b/spec.md @@ -9,7 +9,7 @@ You can use `bitflags` to: You can't use `bitflags` to: -- guarantee only bits corresponding to defined flags will ever be set. `bitflags` is a light wrapper over an integer type with additional semantics. It's not a native language feature. +- guarantee only bits corresponding to defined flags will ever be set. `bitflags` allows access to the underlying bits type so arbitrary bits may be set. - define bitfields. `bitflags` only generates types where set bits denote the presence of some combination of flags. ## Definitions @@ -32,11 +32,35 @@ A value at a specific location within a bits type that may be set or unset. ### Flag -A uniquely named set of bits in a bits type. +A set of bits in a bits type that may have a unique name. ---- -Names must be unique, but bits are not required to be exclusive to a flag. Bits are not required to be contiguous. +Bits are not required to be exclusive to a flag. Bits are not required to be contiguous. + +#### Named flag + +A flag with a name. + +---- + +The following is a named flag, where the name is `A`: + +```rust +const A = 0b0000_0001; +``` + +#### Unnamed flag + +A flag without a name. + +---- + +The following is an unnamed flag: + +```rust +const _ = 0b0000_0001; +``` #### Zero-bit flag @@ -158,7 +182,7 @@ In cases where flags are defined by an external source, a flags type can define ```rust struct External { - const ALL = 0b1111_1111; + const _ = 0b1111_1111; } ``` @@ -409,11 +433,11 @@ The following are examples of the difference between two flags values: ### Iteration -Yield a set of flags values contained in a source flags value, where the result of unioning the yielded flags values will be the truncated source. +Yield a set of flags values contained in a source flags value, where all set bits in the yielded flags values will exactly correspond to all set bits in the source. ---- -To be most useful, each yielded flags value should set exactly the bits of a defined flag contained in the source. Any bits that aren't in the set of any contained flag should be yielded together as a final flags value. Unknown bits that are present in the source should still be yielded to avoid possible confusion, but are not required. +To be most useful, each yielded flags value should set exactly the bits of a defined flag contained in the source. Any bits that aren't in the set of any contained flag should be yielded together as a final flags value. ---- @@ -448,20 +472,13 @@ It may also yield a flags value for `AB`, with a final flags value of the remain 0b0000_1100 ``` -It may also yield any combination of flags values +It may also yield any combination of flags values: ```rust 0b0000_0111 0b0000_1000 ``` -Unknown bits aren't required to be yielded: - -```rust -0b0000_0001 -0b0000_0010 -``` - ### Formatting Flags values can be formatted and parsed using the following *whitespace-insensitive*, *case-sensitive* grammar: @@ -471,7 +488,7 @@ Flags values can be formatted and parsed using the following *whitespace-insensi - _Name:_ The name of any defined flag - _Hex Number_: `0x`([0-9a-fA-F])* -Flags values can be formatted by iterating over them. Any yielded flags value that sets exactly the bits of a defined flag should be formatted with its name. Any yielded flags value that doesn't set exactly the bits of a defined flag must be formatted as a hex number. +Flags values can be formatted by iterating over them. Flags values may be truncated before formatting. Any yielded flags value that sets exactly the bits of a defined flag with a name should be formatted as that name. Any yielded flags value that doesn't set exactly the bits of a defined flag with a name must be formatted as a hex number. The result of parsing a formatted flags value will be the truncated source. An empty formatted flags value is zero. From ede2c6adb3855b33dcde987d549b1ba626e676ab Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Sat, 1 Jul 2023 14:13:00 +1000 Subject: [PATCH 13/36] work on spec around bitwise ops --- spec.md | 172 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 116 insertions(+), 56 deletions(-) diff --git a/spec.md b/spec.md index c3a0c52c..f297ddfb 100644 --- a/spec.md +++ b/spec.md @@ -20,15 +20,41 @@ Examples use `bitflags` syntax with `u8` as the bits type. ### Bits type -A type that stores a fixed number bits. +A type that can hold a fixed number bits. ---- Bits types are typically fixed-width unsigned integers, like `u32`, but may be more exotic. -#### Bit +### Bit -A value at a specific location within a bits type that may be set or unset. +A value at a specific index that may be set or unset. + +---- + +The bits type `u8` holds 8 bits; bit-0 through bit-7. + +### Bits value + +An instance of a bits type. + +---- + +Some examples of bits values for the bits type `u8` are: + +```rust +0b0000_0000 +0b1111_1111 +0b1010_0101 +``` + +#### Equality + +Two bits values are equal if their bits are in the same configuration; set bits in one are set in the other, and unset bits in one are unset in the other. + +#### Operations + +Bits values define the bitwise operators and (`&`), or (`|`), exclusive-or (`^`), and negation (`!`) that apply to each of their bits. ### Flag @@ -190,55 +216,13 @@ This flags type is not normal, but guarantees no bits will ever be truncated. ### Flags value -An instance of a flags type using its bits type for storage. - -#### Empty - -Whether all bits in a flags value are unset. - ----- - -The following flags value is empty: - -```rust -0b0000_0000 -``` - -The following flags values are not empty: - -```rust -0b0000_0001 -0b0110_0000 -``` - -#### All - -Whether all defined flags are contained in a flags value. - ----- - -Given a flags type: - -```rust -struct Flags { - const A = 0b0000_0001; - const B = 0b0000_0010; -} -``` - -the following flags values all satisfy all: - -```rust -0b0000_0011 -0b1000_0011 -0b1111_1111 -``` +An instance of a flags type with bits from its specific bits value. #### Contains Whether all set bits in a source flags value are also set in a target flags value. -A flag is contained in a flags value if all bits in the flag are set in the flags value. +A flag is contained in a source flags value if all bits in the flag are set in the source. ---- @@ -268,7 +252,7 @@ but the following flags values are not contained: Whether any set bits in a source flags value are also set in a target flags value. -A flag intersects a flags value if any bits in the flag are set in the flags value. +A flag intersects a source flags value if any bits in the flag are set in the source. ---- @@ -293,9 +277,51 @@ but the following flags values do not intersect: 0b1111_0000 ``` +#### Empty + +Whether all bits in a flags value are unset. + +---- + +The following flags value is empty: + +```rust +0b0000_0000 +``` + +The following flags values are not empty: + +```rust +0b0000_0001 +0b0110_0000 +``` + +#### All + +Whether all defined flags are contained in a flags value. + +---- + +Given a flags type: + +```rust +struct Flags { + const A = 0b0000_0001; + const B = 0b0000_0010; +} +``` + +the following flags values all satisfy all: + +```rust +0b0000_0011 +0b1000_0011 +0b1111_1111 +``` + #### Normalized -Whether all set bits in a flags value are known bits and all defined flags that intersect are also contained. +Whether all set bits in a flags value are known bits and all defined flags that intersect it are also contained. ---- @@ -419,7 +445,7 @@ The following are examples of the complement of a flags value: #### Difference -The intersection of a source flags value with the negation of a target flags value (`&!`), truncating the result. +The intersection of a source flags value with the complement of a target flags value (`&!`), truncating the result. ---- @@ -433,7 +459,7 @@ The following are examples of the difference between two flags values: ### Iteration -Yield a set of flags values contained in a source flags value, where all set bits in the yielded flags values will exactly correspond to all set bits in the source. +Yield a set of flags values contained in a source flags value, where the result of bitwise or'ing the bits of all yielded flags values will exactly reproduce the source. ---- @@ -488,9 +514,15 @@ Flags values can be formatted and parsed using the following *whitespace-insensi - _Name:_ The name of any defined flag - _Hex Number_: `0x`([0-9a-fA-F])* -Flags values can be formatted by iterating over them. Flags values may be truncated before formatting. Any yielded flags value that sets exactly the bits of a defined flag with a name should be formatted as that name. Any yielded flags value that doesn't set exactly the bits of a defined flag with a name must be formatted as a hex number. +Flags values can be formatted by iterating over them. + +Flags values may be truncated before formatting so any unknown bits are not formatted. -The result of parsing a formatted flags value will be the truncated source. An empty formatted flags value is zero. +Any yielded flags value that sets exactly the bits of a defined flag with a name should be formatted as that name. Any yielded flags value that doesn't set exactly the bits of a defined flag with a name must be formatted as a hex number. + +The result of parsing a formatted flags value is the truncated source. + +An empty formatted flags value is zero. ---- @@ -507,6 +539,8 @@ struct Flags { The following are examples of how flags values can be formatted: ```rust +0b0000_0000 = "" +0b0000_0000 = "0x0" 0b0000_0001 = "A" 0b0000_0001 = "0x1" 0b0000_0010 = "B" @@ -547,7 +581,7 @@ Defines the set of flags. fn bits(&self) -> Self::Bits; ``` -Get the value of the underlying bits type. +Get the the bits value. The result won't be truncated. @@ -577,7 +611,7 @@ fn from_bits_retain(bits: Self::Bits) -> Self; Get a flags value with exactly the bits in `bits` set. -Prefer `from_bits_truncate` where possible; this method is necessary as a building block, but not intended for end-users. If `bits` has any unknown bits set then they'll be truncated by any operations on the returned flags type. +Prefer `from_bits_truncate` where possible. If `bits` has any unknown bits set then they'll be truncated by any operations on the returned flags type. ### `fn from_name` @@ -587,7 +621,9 @@ fn from_name(name: &str) -> Option; Get a flags value with the bits for a defined flag with the given name set. -If there is a flag defined with `name` this function will return `Some`, otherwise it will return `None`. Names are case-sensitive. +If `name` is non-empty and there is a flag defined with `name` this function will return `Some`, otherwise it will return `None`. Names are case-sensitive. + +Unnamed flags won't be produced through this method. ### `fn empty` @@ -731,6 +767,30 @@ Calculates the complement of the bits in `self`. The result will be truncated. +### `fn truncation` + +```rust +fn truncation(self) -> Self; +``` + +Truncates `self`, unsetting all unknown bits. + +### `fn truncate` + +```rust +fn truncate(&mut self); +``` + +Truncates `self`, unsetting all unknown bits. + +### `fn has_unknown_bits` + +```rust +fn has_unknown_bits(&self) -> bool; +``` + +Whether any unknown bits are set in `self`. + ### `fn iter` ```rust From 53772d987c63a255182bbf52b2ae24aa35c05537 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Sat, 1 Jul 2023 16:13:51 +1000 Subject: [PATCH 14/36] rework bits definitions --- spec.md | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/spec.md b/spec.md index f297ddfb..3167f7b3 100644 --- a/spec.md +++ b/spec.md @@ -20,23 +20,15 @@ Examples use `bitflags` syntax with `u8` as the bits type. ### Bits type -A type that can hold a fixed number bits. +A type that defines a fixed number of bits at specific locations. ---- -Bits types are typically fixed-width unsigned integers, like `u32`, but may be more exotic. - -### Bit - -A value at a specific index that may be set or unset. - ----- - -The bits type `u8` holds 8 bits; bit-0 through bit-7. +Bits types are typically fixed-width unsigned integers like `u8`, which is a bits type that defines 8 bits; bit-0 through bit-7. ### Bits value -An instance of a bits type. +An instance of a bits type where each bit may be set (`1`) or unset (`0`). ---- @@ -216,7 +208,9 @@ This flags type is not normal, but guarantees no bits will ever be truncated. ### Flags value -An instance of a flags type with bits from its specific bits value. +An instance of a flags type using its specific bits value for storage. + +The flags value of a flag is one where each of its bits is set, and all others are unset. #### Contains From 576c5bcc13d9b19ead503e4fb29fba7247a35883 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Sat, 1 Jul 2023 17:53:25 +1000 Subject: [PATCH 15/36] remove normalization --- spec.md | 107 +++++++++----------------------------------------------- 1 file changed, 16 insertions(+), 91 deletions(-) diff --git a/spec.md b/spec.md index 3167f7b3..58e6e90b 100644 --- a/spec.md +++ b/spec.md @@ -24,7 +24,7 @@ A type that defines a fixed number of bits at specific locations. ---- -Bits types are typically fixed-width unsigned integers like `u8`, which is a bits type that defines 8 bits; bit-0 through bit-7. +Bits types are typically fixed-width unsigned integers. For example, `u8` is a bits type that defines 8 bits; bit-0 through bit-7. ### Bits value @@ -56,6 +56,18 @@ A set of bits in a bits type that may have a unique name. Bits are not required to be exclusive to a flag. Bits are not required to be contiguous. +The following is a flag for `u8` with the name `A` that includes bit-0: + +```rust +const A = 0b0000_0001; +``` + +The following is a flag for `u8` with the name `B` that includes bit-0, and bit-5: + +```rust +const B = 0b0010_0001; +``` + #### Named flag A flag with a name. @@ -166,46 +178,6 @@ the unknown bits are: 0b1111_1000 ``` -#### Normal - -A flags type that defines no zero-bit flags and all known bits are in at least one corresponding single-bit flag. - ----- - -The following are all normal flags types: - -```rust -struct NormalA { - const A = 0b0000_0001; - const B = 0b0000_0010; - const AB = 0b0000_0011; -} - -struct NormalB {} -``` - -The following are all not normal flags types: - -```rust -struct NotNormalA { - const A = 0b0000_0011; -} - -struct NotNormalB { - const A = 0b0000_0000; -} -``` - -In cases where flags are defined by an external source, a flags type can define a single flag with all bits set: - -```rust -struct External { - const _ = 0b1111_1111; -} -``` - -This flags type is not normal, but guarantees no bits will ever be truncated. - ### Flags value An instance of a flags type using its specific bits value for storage. @@ -216,8 +188,6 @@ The flags value of a flag is one where each of its bits is set, and all others a Whether all set bits in a source flags value are also set in a target flags value. -A flag is contained in a source flags value if all bits in the flag are set in the source. - ---- Given the flags value: @@ -246,8 +216,6 @@ but the following flags values are not contained: Whether any set bits in a source flags value are also set in a target flags value. -A flag intersects a source flags value if any bits in the flag are set in the source. - ---- Given the flags value: @@ -313,41 +281,6 @@ the following flags values all satisfy all: 0b1111_1111 ``` -#### Normalized - -Whether all set bits in a flags value are known bits and all defined flags that intersect it are also contained. - ----- - -Given the flags type: - -```rust -struct Flags { - const A = 0b0000_0001; - const B = 0b0000_0010; - const C = 0b0000_1100; -} -``` - -the following flags values are normalized: - -```rust -0b0000_0000 -0b0000_0001 -0b0000_0010 -0b0000_1100 -0b0000_1111 -``` - -but the following flags values are not normalized: - -```rust -0b1111_1111 -0b0000_1000 -``` - -A consequence of zero-bit flags always being contained but never intersected means a flags type that defines one can never be normalized. - ### Operations Examples in this section all use the given flags type: @@ -366,8 +299,6 @@ The definition of this flags type has implications on the results of most operat Unset all unknown bits in a flags value. -If the flags type is normal then the result is normalized. - ---- Given the flags value: @@ -453,7 +384,7 @@ The following are examples of the difference between two flags values: ### Iteration -Yield a set of flags values contained in a source flags value, where the result of bitwise or'ing the bits of all yielded flags values will exactly reproduce the source. +Yield exactly the bits of a source flags value in a set of contained flags values. ---- @@ -508,15 +439,9 @@ Flags values can be formatted and parsed using the following *whitespace-insensi - _Name:_ The name of any defined flag - _Hex Number_: `0x`([0-9a-fA-F])* -Flags values can be formatted by iterating over them. - -Flags values may be truncated before formatting so any unknown bits are not formatted. - -Any yielded flags value that sets exactly the bits of a defined flag with a name should be formatted as that name. Any yielded flags value that doesn't set exactly the bits of a defined flag with a name must be formatted as a hex number. - -The result of parsing a formatted flags value is the truncated source. +Flags values can be formatted by iterating over them with each yielded flags value can be formatted as a _Flag_. Any yielded flags value that sets exactly the bits of a defined flag with a name should be formatted as a _Name_. Any yielded flags value that doesn't set exactly the bits of a defined flag with a name must be formatted as a _Hex Number_. -An empty formatted flags value is zero. +Parsing and formatting roundtrips, including any unknown bits. Flags values may be truncating before formatting. An empty formatted flags value is zero. ---- From 1c4fec03ce3a8c83840e5f8b546ab79c169a4001 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Sat, 1 Jul 2023 22:49:48 +1000 Subject: [PATCH 16/36] ingore unknown bits in formatting --- spec.md | 64 +++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/spec.md b/spec.md index 58e6e90b..342e6b90 100644 --- a/spec.md +++ b/spec.md @@ -293,7 +293,7 @@ struct Flags { } ``` -The definition of this flags type has implications on the results of most operations. +The definition of this flags type has implications on the results of other examples in this section. #### Truncate @@ -384,11 +384,15 @@ The following are examples of the difference between two flags values: ### Iteration -Yield exactly the bits of a source flags value in a set of contained flags values. +Yield the known bits of a source flags value in a set of contained flags values. ---- -To be most useful, each yielded flags value should set exactly the bits of a defined flag contained in the source. Any bits that aren't in the set of any contained flag should be yielded together as a final flags value. +Unknown bits will not be yielded. + +The result of unioning all flags values yielded from an iterated source flags value will be the truncated source. + +To be most useful, each yielded flags value should set exactly the bits of a defined flag contained in the source. Any known bits that aren't in the set of any contained flag should be yielded together as a final flags value. ---- @@ -408,40 +412,53 @@ and the following flags value: 0b0000_1111 ``` -When iterated it may yield a flags value for `A` and `B`, with a final flags value of the remaining bits: +When iterated it may yield a flags value for `A` and `B`: ```rust 0b0000_0001 0b0000_0010 -0b0000_1100 ``` -It may also yield a flags value for `AB`, with a final flags value of the remaining bits: +It may also yield a flags value for `AB`: ```rust 0b0000_0011 -0b0000_1100 ``` -It may also yield any combination of flags values: +The unknown bits `0b0000_1100` will not be yielded. + +---- + +Given the following flags type: ```rust -0b0000_0111 -0b0000_1000 +struct Flags { + const A = 0b0000_0011; +} ``` +and the following flags value: + +```rust +0b0000_0001 +``` + +When iterated it will still yield a flags value for the known bit `0b0000_0001` even though it doesn't contain a flag. + ### Formatting -Flags values can be formatted and parsed using the following *whitespace-insensitive*, *case-sensitive* grammar: +Format and parse the known bits of a flags value as text using the following *whitespace-insensitive*, *case-sensitive* grammar: - _Flags:_ (_Flag_)`|`* - _Flag:_ _Name_ | _Hex Number_ - _Name:_ The name of any defined flag - _Hex Number_: `0x`([0-9a-fA-F])* -Flags values can be formatted by iterating over them with each yielded flags value can be formatted as a _Flag_. Any yielded flags value that sets exactly the bits of a defined flag with a name should be formatted as a _Name_. Any yielded flags value that doesn't set exactly the bits of a defined flag with a name must be formatted as a _Hex Number_. +Unknown bits will not be formatted. + +Flags values can be formatted as _Flags_ by iterating over them, formatting each yielded flags value as a _Flag_. Any yielded flags value that sets exactly the bits of a defined flag with a name should be formatted as a _Name_. Otherwise it must be formatted as a _Hex Number_. -Parsing and formatting roundtrips, including any unknown bits. Flags values may be truncating before formatting. An empty formatted flags value is zero. +The result of parsing a formatted source flags value will be the truncated source. Text that is empty or whitespace is an empty flags value. ---- @@ -465,13 +482,30 @@ The following are examples of how flags values can be formatted: 0b0000_0010 = "B" 0b0000_0010 = "0x2" 0b0000_0011 = "A | B" -0b1000_0000 = "0x80" 0b1000_0000 = "0x0" 0b1000_0000 = "" -0b1111_1111 = "A | B | 0xfc" +0b1111_1111 = "A | B" 0b1111_1111 = "A | B" ``` +---- + +Given the following flags type: + +```rust +struct Flags { + const A = 0b0000_0011; +} +``` + +and the following flags value: + +```rust +0b0000_0001 +``` + +The result of formatting the flags value must be the hex number `0x1`, because `0b0000_0001` doesn't contain the flag `A`. + ## Implementation > This section is just here to link the spec to what's implemented; it should be removed in favor of regular doc comments. From 8726775fa68bee7608a509221907ea9a8101066c Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Sun, 2 Jul 2023 08:49:38 +1000 Subject: [PATCH 17/36] make formatting/parsing non-truncating --- spec.md | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/spec.md b/spec.md index 342e6b90..4963406a 100644 --- a/spec.md +++ b/spec.md @@ -384,15 +384,15 @@ The following are examples of the difference between two flags values: ### Iteration -Yield the known bits of a source flags value in a set of contained flags values. +Yield all bits of a source flags value in a set of contained flags values. ---- -Unknown bits will not be yielded. +To be most useful, each yielded flags value should set exactly the bits of a defined flag contained in the source. Any known bits that aren't in the set of any contained flag should be yielded together as a final flags value. -The result of unioning all flags values yielded from an iterated source flags value will be the truncated source. +---- -To be most useful, each yielded flags value should set exactly the bits of a defined flag contained in the source. Any known bits that aren't in the set of any contained flag should be yielded together as a final flags value. +Unknown bits must be yielded, but since union is truncating the result of unioning all yielded flags values will be the truncated source. ---- @@ -412,21 +412,21 @@ and the following flags value: 0b0000_1111 ``` -When iterated it may yield a flags value for `A` and `B`: +When iterated it may yield a flags value for `A` and `B`, then a final flag with the unknown bits: ```rust 0b0000_0001 0b0000_0010 +0b0000_1100 ``` -It may also yield a flags value for `AB`: +It may also yield a flags value for `AB`, then a final flag with the unknown bits: ```rust 0b0000_0011 +0b0000_1100 ``` -The unknown bits `0b0000_1100` will not be yielded. - ---- Given the following flags type: @@ -447,18 +447,19 @@ When iterated it will still yield a flags value for the known bit `0b0000_0001` ### Formatting -Format and parse the known bits of a flags value as text using the following *whitespace-insensitive*, *case-sensitive* grammar: +Format and parse a flags value as text using the following grammar: -- _Flags:_ (_Flag_)`|`* +- _Flags:_ (_Whitespace_ _Flag_ _Whitespace_)`|`* - _Flag:_ _Name_ | _Hex Number_ - _Name:_ The name of any defined flag - _Hex Number_: `0x`([0-9a-fA-F])* - -Unknown bits will not be formatted. +- _Whitespace_: (\s)* Flags values can be formatted as _Flags_ by iterating over them, formatting each yielded flags value as a _Flag_. Any yielded flags value that sets exactly the bits of a defined flag with a name should be formatted as a _Name_. Otherwise it must be formatted as a _Hex Number_. -The result of parsing a formatted source flags value will be the truncated source. Text that is empty or whitespace is an empty flags value. +The result of parsing a formatted source flags value will be the original source. If a _Name_ doesn't correspond to the name of any defined flag then parsing will fail. If a formatted hex number includes unknown bits then those bits will be set in the parsed result. Parsing does not truncate. + +Text that is empty or whitespace is an empty flags value. ---- @@ -482,10 +483,10 @@ The following are examples of how flags values can be formatted: 0b0000_0010 = "B" 0b0000_0010 = "0x2" 0b0000_0011 = "A | B" -0b1000_0000 = "0x0" -0b1000_0000 = "" -0b1111_1111 = "A | B" -0b1111_1111 = "A | B" +0b0000_0011 = "AB" +0b1000_0000 = "0x80" +0b1111_1111 = "A | B | 0xfc" +0b1111_1111 = "0xff" ``` ---- From 119082c2499eda0e1f1f77bfd5e08cbfe1efd474 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Mon, 3 Jul 2023 13:36:58 +1000 Subject: [PATCH 18/36] specify formatting modes --- spec.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/spec.md b/spec.md index 4963406a..c095fa0d 100644 --- a/spec.md +++ b/spec.md @@ -457,7 +457,11 @@ Format and parse a flags value as text using the following grammar: Flags values can be formatted as _Flags_ by iterating over them, formatting each yielded flags value as a _Flag_. Any yielded flags value that sets exactly the bits of a defined flag with a name should be formatted as a _Name_. Otherwise it must be formatted as a _Hex Number_. -The result of parsing a formatted source flags value will be the original source. If a _Name_ doesn't correspond to the name of any defined flag then parsing will fail. If a formatted hex number includes unknown bits then those bits will be set in the parsed result. Parsing does not truncate. +Formatting and parsing supports three modes: + +- Truncate: Flags values are truncated before formatting, and truncated after parsing. This is the default behavior. +- Retain: Formatting and parsing roundtrips exactly the bits of the source flags value. +- Strict: A _Flag_ may only be formatted and parsed as a _Name_, _Hex numbers_ are not allowed. A consequence of this is that unknown bits and any bits that aren't in a contained named flag will be ignored. Text that is empty or whitespace is an empty flags value. From f2652687111e32ba76260e0631c894801d625a48 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Tue, 4 Jul 2023 09:19:44 +1000 Subject: [PATCH 19/36] clarify truncate and fmt behavior --- spec.md | 335 ++++++++++---------------------------------------------- 1 file changed, 55 insertions(+), 280 deletions(-) diff --git a/spec.md b/spec.md index c095fa0d..2c091d38 100644 --- a/spec.md +++ b/spec.md @@ -313,6 +313,45 @@ the result of truncation will be: 0b0000_1111 ``` +---- + +Truncating doesn't guarantee that a non-empty result will contain any defined flags. Given the following flags type: + +```rust +struct Flags { + const A = 0b0000_0101; +} +``` + +and the following flags value: + +```rust +0b0000_1110; +``` + +The result of truncation will be: + +```rust +0b0000_0100; +``` + +which intersects the flag `A`, but doesn't contain it. + +This behavior is possible even when only operating with flags values containing defined flags. Given the following flags type: + +```rust +struct Flags { + const A = 0b0000_0101; + const B = 0b0000_0001; +} +``` + +The result of `A ^ B` is `0b0000_0100`, which also doesn't contain any defined flag. + +---- + +If all known bits are in the set of at least one single-bit flag, then all operations that produce non-empty results will always contain defined flags. + #### Union The bitwise or (`|`) of the bits in two flags values, truncating the result. @@ -384,7 +423,7 @@ The following are examples of the difference between two flags values: ### Iteration -Yield all bits of a source flags value in a set of contained flags values. +Yield the bits of a source flags value in a set of contained flags values. ---- @@ -460,8 +499,8 @@ Flags values can be formatted as _Flags_ by iterating over them, formatting each Formatting and parsing supports three modes: - Truncate: Flags values are truncated before formatting, and truncated after parsing. This is the default behavior. -- Retain: Formatting and parsing roundtrips exactly the bits of the source flags value. -- Strict: A _Flag_ may only be formatted and parsed as a _Name_, _Hex numbers_ are not allowed. A consequence of this is that unknown bits and any bits that aren't in a contained named flag will be ignored. +- Retain: Formatting and parsing roundtrips exactly the bits of the source flags value. This is recommended for debug formatting. +- Strict: A _Flag_ may only be formatted and parsed as a _Name_. _Hex numbers_ are not allowed. A consequence of this is that unknown bits and any bits that aren't in a contained named flag will be ignored. This is recommended for flags values serialized across API boundaries, like web services. Text that is empty or whitespace is an empty flags value. @@ -474,303 +513,39 @@ struct Flags { const A = 0b0000_0001; const B = 0b0000_0010; const AB = 0b0000_0011; + const C = 0b0000_1100; } ``` -The following are examples of how flags values can be formatted: +The following are examples of how flags values can be formatted using any mode: ```rust 0b0000_0000 = "" -0b0000_0000 = "0x0" 0b0000_0001 = "A" -0b0000_0001 = "0x1" 0b0000_0010 = "B" -0b0000_0010 = "0x2" 0b0000_0011 = "A | B" 0b0000_0011 = "AB" -0b1000_0000 = "0x80" -0b1111_1111 = "A | B | 0xfc" -0b1111_1111 = "0xff" -``` - ----- - -Given the following flags type: - -```rust -struct Flags { - const A = 0b0000_0011; -} -``` - -and the following flags value: - -```rust -0b0000_0001 -``` - -The result of formatting the flags value must be the hex number `0x1`, because `0b0000_0001` doesn't contain the flag `A`. - -## Implementation - -> This section is just here to link the spec to what's implemented; it should be removed in favor of regular doc comments. - -The specification is implemented through the `Flags` trait. An implementor of the `Flags` trait is a flags type. An instance of the implementor is a flags value. - -### `type Bits` - -```rust -type Bits: Bits; -``` - -The bits type used. - -### `const FLAGS` - -```rust -const FLAGS: &'static [Flag]; -``` - -Defines the set of flags. - -### `fn bits` - -```rust -fn bits(&self) -> Self::Bits; -``` - -Get the the bits value. - -The result won't be truncated. - -### `fn from_bits_truncate` - -```rust -fn from_bits_truncate(bits: Self::Bits) -> Self; -``` - -Get a flags value with only the known bits in `bits` set. - -### `fn from_bits` - -```rust -fn from_bits(bits: Self::Bits) -> Option; -``` - -Get a flags value with only the known bits in `bits` set. - -If the result is non-empty this function will return `Some`, otherwise it will return `None`. - -### `fn from_bits_retain` - -```rust -fn from_bits_retain(bits: Self::Bits) -> Self; -``` - -Get a flags value with exactly the bits in `bits` set. - -Prefer `from_bits_truncate` where possible. If `bits` has any unknown bits set then they'll be truncated by any operations on the returned flags type. - -### `fn from_name` - -```rust -fn from_name(name: &str) -> Option; -``` - -Get a flags value with the bits for a defined flag with the given name set. - -If `name` is non-empty and there is a flag defined with `name` this function will return `Some`, otherwise it will return `None`. Names are case-sensitive. - -Unnamed flags won't be produced through this method. - -### `fn empty` - -```rust -fn empty() -> Self; -``` - -Get a flags value with all bits unset. - -The returned flags value will satisfy `is_empty`. - -### `fn all` - -```rust -fn all() -> Self; +0b0000_1111 = "A | B | C" ``` -Get a flags value with all known bits set and all unknown bits unset. - -The returned flags value will satisfy `is_all`. - -### `fn is_empty` +Truncate mode will unset any unknown bits: ```rust -fn is_empty(&self) -> bool; +0b1000_0000 = "" +0b1111_1111 = "A | B | C" ``` -Whether all bits in the flags value are unset. - -### `fn is_all` +Retain mode will include any unknown bits as a final _Flag_: ```rust -fn is_all(&self) -> bool; -``` - -Whether all defined flags are contained in the flags value. - -### `fn intersection` - -```rust -fn intersection(self, other: Self) -> Self; -``` - -Calculates the intersection of the bits in `self` and `other`. - -The result will be truncated. - -### `fn intersects` - -```rust -fn intersects(&self, other: Self) -> bool; -``` - -Whether `self` and `other` intersect. - -### `fn contains` - -```rust -fn contains(&self, other: Self) -> bool; -``` - -Whether `self` contains `other`. - -### `fn union` - -```rust -fn union(self, other: Self) -> Self; -``` - -Calculates the union of the bits in `self` and `other`. - -The result will be truncated. - -### `fn insert` - -```rust -fn insert(&mut self, other: Self); -``` - -Assigns the union of the bits in `self` and `other`. - -The result will be truncated. - -### `fn difference` - -```rust -fn difference(self, other: Self) -> Self; -``` - -Calculates the difference between the bits in `self` and `other`. - -The result will be truncated. - -### `fn remove` - -```rust -fn remove(&mut self, other: Self); -``` - -Assigns the difference between the bits in `self` and `other`. - -The result will be truncated. - -### `fn set` - -```rust -fn set(&mut self, other: Self, value: bool); -``` - -Assigns the union of `self` and `other` if `value` is `true`, or the difference between `self` and `other` if `value` is `false`. - -The result will be truncated. - -### `fn symmetric_difference` - -```rust -fn symmetric_difference(self, other: Self) -> Self; -``` - -Calculates the symmetric difference between the bits in `self` and `other`. - -The result will be truncated. - -### `fn toggle` - -```rust -fn toggle(&mut self, other: Self); -``` - -Calculates the symmetric difference between the bits in `self` and `other`. - -The result will be truncated. - -### `fn complement` - -```rust -fn complement(self) -> Self; -``` - -Calculates the complement of the bits in `self`. - -The result will be truncated. - -### `fn truncation` - -```rust -fn truncation(self) -> Self; -``` - -Truncates `self`, unsetting all unknown bits. - -### `fn truncate` - -```rust -fn truncate(&mut self); -``` - -Truncates `self`, unsetting all unknown bits. - -### `fn has_unknown_bits` - -```rust -fn has_unknown_bits(&self) -> bool; -``` - -Whether any unknown bits are set in `self`. - -### `fn iter` - -```rust -fn iter(&self) -> iter::Iter; +0b1000_0000 = "0x80" +0b1111_1111 = "A | B | C | 0xf0" ``` -Iterate over defined flags contained in `self`. - -The result of unioning all yielded flags will exactly reproduce `self`. - -Each yielded flags value will correspond to a single flag. Not all flags contained in `self` are guaranteed to be yielded; only enough to exactly reproduce `self`. Overlapping flags may be omitted. - -If `self` is not normalized then any remaining bits will be yielded as a final result. - -### `fn iter_names` +Strict mode will unset any unknown bits, as well as bits not contained in any defined named flags: ```rust -fn iter_names(&self) -> iter::IterNames; +0b1000_0000 = "" +0b1111_1111 = "A | B | C" +0b0000_1000 = "" ``` - -Iterate over defined flags and their names contained in `self`. - -The result of unioning all yielded flags will lossily normalize `self`. - -If `self` is normalized then the result of unioning all yielded flags will exactly reproduce `self`. If `self` is not normalized then any remaining bits will not be yielded. Not all flags contained in `self` are guaranteed to be yielded; only enough to lossily normalize `self`. From 106c9d627b1ff2e0bfd19cbb0901c691273ed489 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Tue, 4 Jul 2023 09:31:51 +1000 Subject: [PATCH 20/36] formatting changes --- spec.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/spec.md b/spec.md index 2c091d38..351e0b1b 100644 --- a/spec.md +++ b/spec.md @@ -350,7 +350,7 @@ The result of `A ^ B` is `0b0000_0100`, which also doesn't contain any defined f ---- -If all known bits are in the set of at least one single-bit flag, then all operations that produce non-empty results will always contain defined flags. +If all known bits are in the set of at least one defined single-bit flag, then all operations that produce non-empty results will always contain defined flags. #### Union @@ -498,9 +498,9 @@ Flags values can be formatted as _Flags_ by iterating over them, formatting each Formatting and parsing supports three modes: -- Truncate: Flags values are truncated before formatting, and truncated after parsing. This is the default behavior. -- Retain: Formatting and parsing roundtrips exactly the bits of the source flags value. This is recommended for debug formatting. -- Strict: A _Flag_ may only be formatted and parsed as a _Name_. _Hex numbers_ are not allowed. A consequence of this is that unknown bits and any bits that aren't in a contained named flag will be ignored. This is recommended for flags values serialized across API boundaries, like web services. +- **Truncate**: Flags values are truncated before formatting, and truncated after parsing. This is the default behavior. +- **Retain**: Formatting and parsing roundtrips exactly the bits of the source flags value. This is recommended for debug formatting. +- **Strict**: A _Flag_ may only be formatted and parsed as a _Name_. _Hex numbers_ are not allowed. A consequence of this is that unknown bits and any bits that aren't in a contained named flag will be ignored. This is recommended for flags values serialized across API boundaries, like web services. Text that is empty or whitespace is an empty flags value. @@ -533,6 +533,7 @@ Truncate mode will unset any unknown bits: ```rust 0b1000_0000 = "" 0b1111_1111 = "A | B | C" +0b0000_1000 = "0x8" ``` Retain mode will include any unknown bits as a final _Flag_: @@ -540,6 +541,7 @@ Retain mode will include any unknown bits as a final _Flag_: ```rust 0b1000_0000 = "0x80" 0b1111_1111 = "A | B | C | 0xf0" +0b0000_1000 = "0x8" ``` Strict mode will unset any unknown bits, as well as bits not contained in any defined named flags: From e5a3fe4b5fc48ac8a82068ef955d7eb9fccbde18 Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Tue, 11 Jul 2023 13:03:46 +1000 Subject: [PATCH 21/36] align truncation to existing semantics --- spec.md | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/spec.md b/spec.md index 351e0b1b..63a48881 100644 --- a/spec.md +++ b/spec.md @@ -293,8 +293,6 @@ struct Flags { } ``` -The definition of this flags type has implications on the results of other examples in this section. - #### Truncate Unset all unknown bits in a flags value. @@ -354,7 +352,7 @@ If all known bits are in the set of at least one defined single-bit flag, then a #### Union -The bitwise or (`|`) of the bits in two flags values, truncating the result. +The bitwise or (`|`) of the bits in two flags values. ---- @@ -362,12 +360,12 @@ The following are examples of the result of unioning flags values: ```rust 0b0000_0001 | 0b0000_0010 = 0b0000_0011 -0b0000_0000 | 0b1111_1111 = 0b0000_1111 +0b0000_0000 | 0b1111_1111 = 0b1111_1111 ``` #### Intersection -The bitwise and (`&`) of the bits in two flags values, truncating the result. +The bitwise and (`&`) of the bits in two flags values. ---- @@ -375,13 +373,13 @@ The following are examples of the result of intersecting flags values: ```rust 0b0000_0001 & 0b0000_0010 = 0b0000_0000 -0b1111_1100 & 0b1111_0111 = 0b0000_0100 -0b1111_1111 & 0b1111_1111 = 0b0000_1111 +0b1111_1100 & 0b1111_0111 = 0b1111_0100 +0b1111_1111 & 0b1111_1111 = 0b1111_1111 ``` #### Symmetric difference -The bitwise exclusive-or (`^`) of the bits in two flags values, truncating the result. +The bitwise exclusive-or (`^`) of the bits in two flags values. ---- @@ -390,7 +388,7 @@ The following are examples of the symmetric difference between two flags values: ```rust 0b0000_0001 ^ 0b0000_0010 = 0b0000_0011 0b0000_1111 ^ 0b0000_0011 = 0b0000_1100 -0b1100_0000 ^ 0b0011_0000 = 0b0000_0000 +0b1100_0000 ^ 0b0011_0000 = 0b1111_0000 ``` #### Complement @@ -431,10 +429,6 @@ To be most useful, each yielded flags value should set exactly the bits of a def ---- -Unknown bits must be yielded, but since union is truncating the result of unioning all yielded flags values will be the truncated source. - ----- - Given the following flags type: ```rust @@ -498,8 +492,8 @@ Flags values can be formatted as _Flags_ by iterating over them, formatting each Formatting and parsing supports three modes: -- **Truncate**: Flags values are truncated before formatting, and truncated after parsing. This is the default behavior. -- **Retain**: Formatting and parsing roundtrips exactly the bits of the source flags value. This is recommended for debug formatting. +- **Retain**: Formatting and parsing roundtrips exactly the bits of the source flags value. This is the default behavior. +- **Truncate**: Flags values are truncated before formatting, and truncated after parsing. - **Strict**: A _Flag_ may only be formatted and parsed as a _Name_. _Hex numbers_ are not allowed. A consequence of this is that unknown bits and any bits that aren't in a contained named flag will be ignored. This is recommended for flags values serialized across API boundaries, like web services. Text that is empty or whitespace is an empty flags value. From ea40e7fff0d0e1ef3ac121cd734c5b2e058b9a04 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Fri, 14 Jul 2023 16:20:18 +1000 Subject: [PATCH 22/36] align docs to spec terminology --- src/lib.rs | 79 ++++++++++++++++----------------------------- src/public.rs | 36 ++++++++++++++------- src/traits.rs | 88 ++++++++++++++++++++++++++++----------------------- 3 files changed, 101 insertions(+), 102 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ffeef79b..5ff36a66 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -723,74 +723,73 @@ macro_rules! __impl_bitflags { ) => { #[allow(dead_code, deprecated, unused_attributes)] impl $PublicBitFlags { - /// Returns an empty set of flags. + /// Get a flags value with all bits unset. #[inline] pub const fn empty() -> Self { $empty } - /// Returns the set containing all flags. + /// Get a flags value with all known bits set. #[inline] pub const fn all() -> Self { $all } - /// Returns the raw value of the flags currently stored. + /// Get the underlying bits value. + /// + /// The returned value is exactly the bits set in this flags value. #[inline] pub const fn bits(&self) -> $T { let $bits0 = self; $bits } - /// Convert from underlying bit representation, unless that - /// representation contains bits that do not correspond to a flag. + /// Convert from a bits value, unless any unset bits are set. #[inline] pub const fn from_bits(bits: $T) -> $crate::__private::core::option::Option { let $from_bits0 = bits; $from_bits } - /// Convert from underlying bit representation, dropping any bits - /// that do not correspond to flags. + /// Convert from a bits value, unsetting any unknown bits. #[inline] pub const fn from_bits_truncate(bits: $T) -> Self { let $from_bits_truncate0 = bits; $from_bits_truncate } - /// Convert from underlying bit representation, preserving all - /// bits (even those not corresponding to a defined flag). + /// Convert from a bits value, without altering them in any way. #[inline] pub const fn from_bits_retain(bits: $T) -> Self { let $from_bits_retain0 = bits; $from_bits_retain } - /// Get the value for a flag from its stringified name. + /// Get a flags value with the bits of a flag with the given name set. /// - /// Names are _case-sensitive_, so must correspond exactly to - /// the identifier given to the flag. + /// This method will return `None` if `name` is empty or doesn't + /// correspond to any named flag. #[inline] pub fn from_name(name: &str) -> $crate::__private::core::option::Option { let $from_name0 = name; $from_name } - /// Returns `true` if no flags are currently stored. + /// Whether all bits in this flags value are unset. #[inline] pub const fn is_empty(&self) -> bool { let $is_empty0 = self; $is_empty } - /// Returns `true` if all flags are currently set. + /// Whether all defined flags are contained in this flags value. #[inline] pub const fn is_all(&self) -> bool { let $is_all0 = self; $is_all } - /// Returns `true` if there are flags common to both `self` and `other`. + /// Whether any set bits in a source flags value are also set in a target flags value. #[inline] pub const fn intersects(&self, other: Self) -> bool { let $intersects0 = self; @@ -798,7 +797,7 @@ macro_rules! __impl_bitflags { $intersects } - /// Returns `true` if all of the flags in `other` are contained within `self`. + /// Whether all set bits in a source flags value are also set in a target flags value. #[inline] pub const fn contains(&self, other: Self) -> bool { let $contains0 = self; @@ -806,9 +805,7 @@ macro_rules! __impl_bitflags { $contains } - /// Inserts the specified flags in-place. - /// - /// This method is equivalent to `union`. + /// The bitwise or (`|`) of the bits in two flags values. #[inline] pub fn insert(&mut self, other: Self) { let $insert0 = self; @@ -816,9 +813,10 @@ macro_rules! __impl_bitflags { $insert } - /// Removes the specified flags in-place. + /// The intersection of a source flags value with the complement of a target flags value (`&!`). /// - /// This method is equivalent to `difference`. + /// This method is not equivalent to `self & !other` when `other` has unknown bits set. + /// `remove` won't truncate `other`, but the `!` operator will. #[inline] pub fn remove(&mut self, other: Self) { let $remove0 = self; @@ -826,9 +824,7 @@ macro_rules! __impl_bitflags { $remove } - /// Toggles the specified flags in-place. - /// - /// This method is equivalent to `symmetric_difference`. + /// The bitwise exclusive-or (`^`) of the bits in two flags values. #[inline] pub fn toggle(&mut self, other: Self) { let $toggle0 = self; @@ -836,7 +832,7 @@ macro_rules! __impl_bitflags { $toggle } - /// Inserts or removes the specified flags depending on the passed value. + /// Call `insert` when `value` is `true` or `remove` when `value` is `false`. #[inline] pub fn set(&mut self, other: Self, value: bool) { let $set0 = self; @@ -845,11 +841,7 @@ macro_rules! __impl_bitflags { $set } - /// Returns the intersection between the flags in `self` and - /// `other`. - /// - /// Calculating `self` bitwise and (`&`) other, including - /// any bits that don't correspond to a defined flag. + /// The bitwise and (`&`) of the bits in two flags values. #[inline] #[must_use] pub const fn intersection(self, other: Self) -> Self { @@ -858,10 +850,7 @@ macro_rules! __impl_bitflags { $intersection } - /// Returns the union of between the flags in `self` and `other`. - /// - /// Calculates `self` bitwise or (`|`) `other`, including - /// any bits that don't correspond to a defined flag. + /// The bitwise or (`|`) of the bits in two flags values. #[inline] #[must_use] pub const fn union(self, other: Self) -> Self { @@ -870,15 +859,10 @@ macro_rules! __impl_bitflags { $union } - /// Returns the difference between the flags in `self` and `other`. - /// - /// Calculates `self` bitwise and (`&!`) the bitwise negation of `other`, - /// including any bits that don't correspond to a defined flag. + /// The intersection of a source flags value with the complement of a target flags value (`&!`). /// - /// This method is _not_ equivalent to `a & !b` when there are bits set that - /// don't correspond to a defined flag. The `!` operator will unset any - /// bits that don't correspond to a flag, so they'll always be unset by `a &! b`, - /// but respected by `a.difference(b)`. + /// This method is not equivalent to `self & !other` when `other` has unknown bits set. + /// `difference` won't truncate `other`, but the `!` operator will. #[inline] #[must_use] pub const fn difference(self, other: Self) -> Self { @@ -887,11 +871,7 @@ macro_rules! __impl_bitflags { $difference } - /// Returns the symmetric difference between the flags - /// in `self` and `other`. - /// - /// Calculates `self` bitwise exclusive or (`^`) `other`, - /// including any bits that don't correspond to a defined flag. + /// The bitwise exclusive-or (`^`) of the bits in two flags values. #[inline] #[must_use] pub const fn symmetric_difference(self, other: Self) -> Self { @@ -900,10 +880,7 @@ macro_rules! __impl_bitflags { $symmetric_difference } - /// Returns the complement of this set of flags. - /// - /// Calculates the bitwise negation (`!`) of `self`, - /// **unsetting** any bits that don't correspond to a defined flag. + /// The bitwise negation (`!`) of the bits in a flags value, truncating the result. #[inline] #[must_use] pub const fn complement(self) -> Self { diff --git a/src/public.rs b/src/public.rs index 1952dced..d0537cc7 100644 --- a/src/public.rs +++ b/src/public.rs @@ -273,7 +273,10 @@ macro_rules! __impl_public_bitflags { macro_rules! __impl_public_bitflags_iter { ($BitFlags:ident: $T:ty, $PublicBitFlags:ident) => { impl $BitFlags { - /// Iterate over enabled flag values. + /// Yield a set of contained flags values. + /// + /// Each yielded flags value will correspond to a defined named flag. Any unknown bits + /// will be yielded together as a final flags value. #[inline] pub const fn iter(&self) -> $crate::iter::Iter<$PublicBitFlags> { $crate::iter::Iter::__private_const_new( @@ -283,7 +286,10 @@ macro_rules! __impl_public_bitflags_iter { ) } - /// Iterate over enabled flag values with their stringified names. + /// Yield a set of contained named flags values. + /// + /// This method is like [`iter`], except only yields bits in contained named flags. + /// Any unknown bits, or bits not corresponding to a contained flag will not be yielded. #[inline] pub const fn iter_names(&self) -> $crate::iter::IterNames<$PublicBitFlags> { $crate::iter::IterNames::__private_const_new( @@ -349,7 +355,7 @@ macro_rules! __impl_public_bitflags_ops { impl $crate::__private::core::ops::BitOr for $PublicBitFlags { type Output = Self; - /// Returns the union of the two sets of flags. + /// The bitwise or (`|`) of the bits in two flags values. #[inline] fn bitor(self, other: $PublicBitFlags) -> Self { self.union(other) @@ -357,7 +363,7 @@ macro_rules! __impl_public_bitflags_ops { } impl $crate::__private::core::ops::BitOrAssign for $PublicBitFlags { - /// Adds the set of flags. + /// The bitwise or (`|`) of the bits in two flags values. #[inline] fn bitor_assign(&mut self, other: Self) { self.insert(other); @@ -367,7 +373,7 @@ macro_rules! __impl_public_bitflags_ops { impl $crate::__private::core::ops::BitXor for $PublicBitFlags { type Output = Self; - /// Returns the left flags, but with all the right flags toggled. + /// The bitwise exclusive-or (`^`) of the bits in two flags values. #[inline] fn bitxor(self, other: Self) -> Self { self.symmetric_difference(other) @@ -375,7 +381,7 @@ macro_rules! __impl_public_bitflags_ops { } impl $crate::__private::core::ops::BitXorAssign for $PublicBitFlags { - /// Toggles the set of flags. + /// The bitwise exclusive-or (`^`) of the bits in two flags values. #[inline] fn bitxor_assign(&mut self, other: Self) { self.toggle(other); @@ -385,7 +391,7 @@ macro_rules! __impl_public_bitflags_ops { impl $crate::__private::core::ops::BitAnd for $PublicBitFlags { type Output = Self; - /// Returns the intersection between the two sets of flags. + /// The bitwise and (`&`) of the bits in two flags values. #[inline] fn bitand(self, other: Self) -> Self { self.intersection(other) @@ -393,7 +399,7 @@ macro_rules! __impl_public_bitflags_ops { } impl $crate::__private::core::ops::BitAndAssign for $PublicBitFlags { - /// Disables all flags disabled in the set. + /// The bitwise and (`&`) of the bits in two flags values. #[inline] fn bitand_assign(&mut self, other: Self) { *self = Self::from_bits_retain(self.bits()).intersection(other); @@ -403,7 +409,10 @@ macro_rules! __impl_public_bitflags_ops { impl $crate::__private::core::ops::Sub for $PublicBitFlags { type Output = Self; - /// Returns the set difference of the two sets of flags. + /// The intersection of a source flags value with the complement of a target flags value (`&!`). + /// + /// This method is not equivalent to `self & !other` when `other` has unknown bits set. + /// `difference` won't truncate `other`, but the `!` operator will. #[inline] fn sub(self, other: Self) -> Self { self.difference(other) @@ -411,7 +420,10 @@ macro_rules! __impl_public_bitflags_ops { } impl $crate::__private::core::ops::SubAssign for $PublicBitFlags { - /// Disables all flags enabled in the set. + /// The intersection of a source flags value with the complement of a target flags value (`&!`). + /// + /// This method is not equivalent to `self & !other` when `other` has unknown bits set. + /// `difference` won't truncate `other`, but the `!` operator will. #[inline] fn sub_assign(&mut self, other: Self) { self.remove(other); @@ -421,7 +433,7 @@ macro_rules! __impl_public_bitflags_ops { impl $crate::__private::core::ops::Not for $PublicBitFlags { type Output = Self; - /// Returns the complement of this set of flags. + /// The bitwise negation (`!`) of the bits in a flags value, truncating the result. #[inline] fn not(self) -> Self { self.complement() @@ -429,6 +441,7 @@ macro_rules! __impl_public_bitflags_ops { } impl $crate::__private::core::iter::Extend<$PublicBitFlags> for $PublicBitFlags { + /// The bitwise or (`|`) of the bits in each flags value. fn extend>( &mut self, iterator: T, @@ -440,6 +453,7 @@ macro_rules! __impl_public_bitflags_ops { } impl $crate::__private::core::iter::FromIterator<$PublicBitFlags> for $PublicBitFlags { + /// The bitwise or (`|`) of the bits in each flags value. fn from_iter>( iterator: T, ) -> Self { diff --git a/src/traits.rs b/src/traits.rs index e21a179f..4ca2465f 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -8,7 +8,7 @@ use crate::{ parser::{ParseError, ParseHex, WriteHex}, }; -/// Metadata for an individual flag. +/// A set of bits in a bits type that may have a unique name. pub struct Flag { name: &'static str, value: B, @@ -16,11 +16,15 @@ pub struct Flag { impl Flag { /// Create a new flag with the given name and value. + /// + /// If `name` is empty then the flag is unnamed. pub const fn new(name: &'static str, value: B) -> Self { Flag { name, value } } /// Get the name of this flag. + /// + /// If `name` is empty then the flag is unnamed. pub const fn name(&self) -> &'static str { self.name } @@ -31,23 +35,20 @@ impl Flag { } } -/// A set of flags. -/// -/// This trait is automatically implemented for flags types defined using the `bitflags!` macro. -/// It can also be implemented manually for custom flags types. +/// A set of defined flags over a specific bits type. pub trait Flags: Sized + 'static { - /// The set of available flags and their names. + /// The set of defined flags. const FLAGS: &'static [Flag]; - /// The underlying storage type. + /// The underlying bits type. type Bits: Bits; - /// Returns an empty set of flags. + /// Get a flags value with all bits unset. fn empty() -> Self { Self::from_bits_retain(Self::Bits::EMPTY) } - /// Returns the set containing all flags. + /// Get a flags value with all known bits set. fn all() -> Self { let mut truncated = Self::Bits::EMPTY; @@ -58,11 +59,12 @@ pub trait Flags: Sized + 'static { Self::from_bits_retain(truncated) } - /// Returns the raw value of the flags currently stored. + /// Get the underlying bits value. + /// + /// The returned value is exactly the bits set in this flags value. fn bits(&self) -> Self::Bits; - /// Convert from underlying bit representation, unless that - /// representation contains bits that do not correspond to a flag. + /// Convert from a bits value, unless any unset bits are set. fn from_bits(bits: Self::Bits) -> Option { let truncated = Self::from_bits_truncate(bits); @@ -73,17 +75,18 @@ pub trait Flags: Sized + 'static { } } - /// Convert from underlying bit representation, dropping any bits - /// that do not correspond to flags. + /// Convert from a bits value, unsetting any unknown bits. fn from_bits_truncate(bits: Self::Bits) -> Self { Self::from_bits_retain(bits & Self::all().bits()) } - /// Convert from underlying bit representation, preserving all - /// bits (even those not corresponding to a defined flag). + /// Convert from a bits value, without altering them in any way. fn from_bits_retain(bits: Self::Bits) -> Self; - /// Get the flag for a particular name. + /// Get a flags value with the bits of a flag with the given name set. + /// + /// This method will return `None` if `name` is empty or doesn't + /// correspond to any named flag. fn from_name(name: &str) -> Option { // Don't parse empty names as empty flags if name.is_empty() { @@ -99,29 +102,35 @@ pub trait Flags: Sized + 'static { None } - /// Iterate over enabled flag values. + /// Yield a set of contained flags values. + /// + /// Each yielded flags value will correspond to a defined named flag. Any unknown bits + /// will be yielded together as a final flags value. fn iter(&self) -> iter::Iter { iter::Iter::new(self) } - /// Iterate over the raw names and bits for enabled flag values. + /// Yield a set of contained named flags values. + /// + /// This method is like [`Flags::iter`], except only yields bits in contained named flags. + /// Any unknown bits, or bits not corresponding to a contained flag will not be yielded. fn iter_names(&self) -> iter::IterNames { iter::IterNames::new(self) } - /// Returns `true` if no flags are currently stored. + /// Whether all bits in this flags value are unset. fn is_empty(&self) -> bool { self.bits() == Self::Bits::EMPTY } - /// Returns `true` if all flags are currently set. + /// Whether all defined flags are contained in this flags value. fn is_all(&self) -> bool { // NOTE: We check against `Self::all` here, not `Self::Bits::ALL` // because the set of all flags may not use all bits Self::all().bits() | self.bits() == self.bits() } - /// Returns `true` if there are flags common to both `self` and `other`. + /// Whether any set bits in a source flags value are also set in a target flags value. fn intersects(&self, other: Self) -> bool where Self: Sized, @@ -129,7 +138,7 @@ pub trait Flags: Sized + 'static { self.bits() & other.bits() != Self::Bits::EMPTY } - /// Returns `true` if all of the flags in `other` are contained within `self`. + /// Whether all set bits in a source flags value are also set in a target flags value. fn contains(&self, other: Self) -> bool where Self: Sized, @@ -137,9 +146,7 @@ pub trait Flags: Sized + 'static { self.bits() & other.bits() == other.bits() } - /// Inserts the specified flags in-place. - /// - /// This method is equivalent to `union`. + /// The bitwise or (`|`) of the bits in two flags values. fn insert(&mut self, other: Self) where Self: Sized, @@ -147,9 +154,10 @@ pub trait Flags: Sized + 'static { *self = Self::from_bits_retain(self.bits()).union(other); } - /// Removes the specified flags in-place. + /// The intersection of a source flags value with the complement of a target flags value (`&!`). /// - /// This method is equivalent to `difference`. + /// This method is not equivalent to `self & !other` when `other` has unknown bits set. + /// `remove` won't truncate `other`, but the `!` operator will. fn remove(&mut self, other: Self) where Self: Sized, @@ -157,9 +165,7 @@ pub trait Flags: Sized + 'static { *self = Self::from_bits_retain(self.bits()).difference(other); } - /// Toggles the specified flags in-place. - /// - /// This method is equivalent to `symmetric_difference`. + /// The bitwise exclusive-or (`^`) of the bits in two flags values. fn toggle(&mut self, other: Self) where Self: Sized, @@ -167,7 +173,7 @@ pub trait Flags: Sized + 'static { *self = Self::from_bits_retain(self.bits()).symmetric_difference(other); } - /// Inserts or removes the specified flags depending on the passed value. + /// Call [`Flags::insert`] when `value` is `true` or [`Flags::remove`] when `value` is `false`. fn set(&mut self, other: Self, value: bool) where Self: Sized, @@ -179,32 +185,34 @@ pub trait Flags: Sized + 'static { } } - /// Returns the intersection between the flags in `self` and `other`. + /// The bitwise and (`&`) of the bits in two flags values. #[must_use] fn intersection(self, other: Self) -> Self { Self::from_bits_retain(self.bits() & other.bits()) } - /// Returns the union of between the flags in `self` and `other`. + /// The bitwise or (`|`) of the bits in two flags values. #[must_use] fn union(self, other: Self) -> Self { Self::from_bits_retain(self.bits() | other.bits()) } - /// Returns the difference between the flags in `self` and `other`. + /// The intersection of a source flags value with the complement of a target flags value (`&!`). + /// + /// This method is not equivalent to `self & !other` when `other` has unknown bits set. + /// `difference` won't truncate `other`, but the `!` operator will. #[must_use] fn difference(self, other: Self) -> Self { Self::from_bits_retain(self.bits() & !other.bits()) } - /// Returns the symmetric difference between the flags - /// in `self` and `other`. + /// The bitwise exclusive-or (`^`) of the bits in two flags values. #[must_use] fn symmetric_difference(self, other: Self) -> Self { Self::from_bits_retain(self.bits() ^ other.bits()) } - /// Returns the complement of this set of flags. + /// The bitwise negation (`!`) of the bits in a flags value, truncating the result. #[must_use] fn complement(self) -> Self { Self::from_bits_truncate(!self.bits()) @@ -223,10 +231,10 @@ pub trait Bits: + Sized + 'static { - /// The value of `Self` where no bits are set. + /// A value with all bits unset. const EMPTY: Self; - /// The value of `Self` where all bits are set. + /// A value with all bits set. const ALL: Self; } From 5b171dc694a27dcca3988a48767b24edd90e0689 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Fri, 14 Jul 2023 16:41:28 +1000 Subject: [PATCH 23/36] align spec for difference to actual behavior --- spec.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/spec.md b/spec.md index 63a48881..43dae1d7 100644 --- a/spec.md +++ b/spec.md @@ -407,7 +407,12 @@ The following are examples of the complement of a flags value: #### Difference -The intersection of a source flags value with the complement of a target flags value (`&!`), truncating the result. +The bitwise union (`|`) of the bits in one flags value and the bitwise negation (`!`) of the bits in another. + +---- + +This operation is not equivalent to the intersection of one flags value with the complement of another (`&!`). +The former will truncate the result, where difference will not. ---- @@ -416,7 +421,7 @@ The following are examples of the difference between two flags values: ```rust 0b0000_0001 & !0b0000_0010 = 0b0000_0001 0b0000_1101 & !0b0000_0011 = 0b0000_1100 -0b1111_1111 & !0b0000_0001 = 0b0000_1110 +0b1111_1111 & !0b0000_0001 = 0b1111_1110 ``` ### Iteration From 6096cab740920b8752b032957d383aa387bc605f Mon Sep 17 00:00:00 2001 From: KodrAus Date: Mon, 17 Jul 2023 15:13:16 +1000 Subject: [PATCH 24/36] start reworking the library docs --- src/lib.rs | 582 +++++++++++------------------------------------------ 1 file changed, 115 insertions(+), 467 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5ff36a66..54bf44cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,416 +8,110 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! A typesafe bitmask flag generator useful for sets of C-style flags. -//! It can be used for creating ergonomic wrappers around C APIs. -//! -//! The `bitflags!` macro generates `struct`s that manage a set of flags. The -//! type of those flags must be some primitive integer. -//! -//! # Examples -//! -//! ``` -//! use bitflags::bitflags; -//! -//! bitflags! { -//! #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -//! struct Flags: u32 { -//! const A = 0b00000001; -//! const B = 0b00000010; -//! const C = 0b00000100; -//! const ABC = Self::A.bits() | Self::B.bits() | Self::C.bits(); -//! } -//! } -//! -//! fn main() { -//! let e1 = Flags::A | Flags::C; -//! let e2 = Flags::B | Flags::C; -//! assert_eq!((e1 | e2), Flags::ABC); // union -//! assert_eq!((e1 & e2), Flags::C); // intersection -//! assert_eq!((e1 - e2), Flags::A); // set difference -//! assert_eq!(!e2, Flags::A); // set complement -//! } -//! ``` -//! -//! See [`example_generated::Flags`](./example_generated/struct.Flags.html) for documentation of code -//! generated by the above `bitflags!` expansion. -//! -//! # Visibility -//! -//! The `bitflags!` macro supports visibility, just like you'd expect when writing a normal -//! Rust `struct`: -//! -//! ``` -//! mod example { -//! use bitflags::bitflags; -//! -//! bitflags! { -//! #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -//! pub struct Flags1: u32 { -//! const A = 0b00000001; -//! } -//! -//! #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -//! # pub -//! struct Flags2: u32 { -//! const B = 0b00000010; -//! } -//! } -//! } -//! -//! fn main() { -//! let flag1 = example::Flags1::A; -//! let flag2 = example::Flags2::B; // error: const `B` is private -//! } -//! ``` -//! -//! # Attributes -//! -//! Attributes can be attached to the generated flags types and their constants as normal. -//! -//! # Representation -//! -//! It's valid to add a `#[repr(C)]` or `#[repr(transparent)]` attribute to a generated flags type. -//! The generated flags type is always guaranteed to be a newtype where its only field has the same -//! ABI as the underlying integer type. -//! -//! In this example, `Flags` has the same ABI as `u32`: -//! -//! ``` -//! use bitflags::bitflags; -//! -//! bitflags! { -//! #[repr(transparent)] -//! #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -//! struct Flags: u32 { -//! const A = 0b00000001; -//! const B = 0b00000010; -//! const C = 0b00000100; -//! } -//! } -//! ``` -//! -//! # Extending -//! -//! Generated flags types belong to you, so you can add trait implementations to them outside -//! of what the `bitflags!` macro gives: -//! -//! ``` -//! use std::fmt; -//! -//! use bitflags::bitflags; -//! -//! bitflags! { -//! #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -//! struct Flags: u32 { -//! const A = 0b00000001; -//! const B = 0b00000010; -//! } -//! } -//! -//! impl Flags { -//! pub fn clear(&mut self) { -//! *self.0.bits_mut() = 0; -//! } -//! } -//! -//! fn main() { -//! let mut flags = Flags::A | Flags::B; -//! -//! flags.clear(); -//! assert!(flags.is_empty()); -//! -//! assert_eq!(format!("{:?}", Flags::A | Flags::B), "Flags(A | B)"); -//! assert_eq!(format!("{:?}", Flags::B), "Flags(B)"); -//! } -//! ``` -//! -//! # What's implemented by `bitflags!` -//! -//! The `bitflags!` macro adds some trait implementations and inherent methods -//! to generated flags types, but leaves room for you to choose the semantics -//! of others. -//! -//! ## Iterators -//! -//! The following iterator traits are implemented for generated flags types: -//! -//! - `Extend`: adds the union of the instances iterated over. -//! - `FromIterator`: calculates the union. -//! - `IntoIterator`: iterates over set flag values. -//! -//! ## Formatting -//! -//! The following formatting traits are implemented for generated flags types: -//! -//! - `Binary`. -//! - `LowerHex` and `UpperHex`. -//! - `Octal`. -//! -//! Also see the _Debug and Display_ section for details about standard text -//! representations for flags types. -//! -//! ## Operators -//! -//! The following operator traits are implemented for the generated `struct`s: -//! -//! - `BitOr` and `BitOrAssign`: union -//! - `BitAnd` and `BitAndAssign`: intersection -//! - `BitXor` and `BitXorAssign`: toggle -//! - `Sub` and `SubAssign`: set difference -//! - `Not`: set complement -//! -//! ## Methods -//! -//! The following methods are defined for the generated `struct`s: -//! -//! - `empty`: an empty set of flags -//! - `all`: the set of all defined flags -//! - `bits`: the raw value of the flags currently stored -//! - `from_bits`: convert from underlying bit representation, unless that -//! representation contains bits that do not correspond to a -//! defined flag -//! - `from_bits_truncate`: convert from underlying bit representation, dropping -//! any bits that do not correspond to defined flags -//! - `from_bits_retain`: convert from underlying bit representation, keeping -//! all bits (even those not corresponding to defined -//! flags) -//! - `is_empty`: `true` if no flags are currently stored -//! - `is_all`: `true` if currently set flags exactly equal all defined flags -//! - `intersects`: `true` if there are flags common to both `self` and `other` -//! - `contains`: `true` if all of the flags in `other` are contained within `self` -//! - `insert`: inserts the specified flags in-place -//! - `remove`: removes the specified flags in-place -//! - `toggle`: the specified flags will be inserted if not present, and removed -//! if they are. -//! - `set`: inserts or removes the specified flags depending on the passed value -//! - `intersection`: returns a new set of flags, containing only the flags present -//! in both `self` and `other` (the argument to the function). -//! - `union`: returns a new set of flags, containing any flags present in -//! either `self` or `other` (the argument to the function). -//! - `difference`: returns a new set of flags, containing all flags present in -//! `self` without any of the flags present in `other` (the -//! argument to the function). -//! - `symmetric_difference`: returns a new set of flags, containing all flags -//! present in either `self` or `other` (the argument -//! to the function), but not both. -//! - `complement`: returns a new set of flags, containing all flags which are -//! not set in `self`, but which are allowed for this type. -//! -//! # What's not implemented by `bitflags!` -//! -//! Some functionality is not automatically implemented for generated flags types -//! by the `bitflags!` macro, even when it reasonably could be. This is so callers -//! have more freedom to decide on the semantics of their flags types. -//! -//! ## `Clone` and `Copy` -//! -//! Generated flags types are not automatically copyable, even though they can always -//! derive both `Clone` and `Copy`. -//! -//! ## `Default` -//! -//! The `Default` trait is not automatically implemented for the generated structs. -//! -//! If your default value is equal to `0` (which is the same value as calling `empty()` -//! on the generated struct), you can simply derive `Default`: -//! -//! ``` -//! use bitflags::bitflags; -//! -//! bitflags! { -//! // Results in default value with bits: 0 -//! #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)] -//! struct Flags: u32 { -//! const A = 0b00000001; -//! const B = 0b00000010; -//! const C = 0b00000100; -//! } -//! } -//! -//! fn main() { -//! let derived_default: Flags = Default::default(); -//! assert_eq!(derived_default.bits(), 0); -//! } -//! ``` -//! -//! If your default value is not equal to `0` you need to implement `Default` yourself: -//! -//! ``` -//! use bitflags::bitflags; -//! -//! bitflags! { -//! #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -//! struct Flags: u32 { -//! const A = 0b00000001; -//! const B = 0b00000010; -//! const C = 0b00000100; -//! } -//! } -//! -//! // explicit `Default` implementation -//! impl Default for Flags { -//! fn default() -> Flags { -//! Flags::A | Flags::C -//! } -//! } -//! -//! fn main() { -//! let implemented_default: Flags = Default::default(); -//! assert_eq!(implemented_default, (Flags::A | Flags::C)); -//! } -//! ``` -//! -//! ## `Debug` and `Display` -//! -//! The `Debug` trait can be derived for a reasonable implementation. This library defines a standard -//! text-based representation for flags that generated flags types can use. For details on the exact -//! grammar, see the [`parser`] module. -//! -//! To support formatting and parsing your generated flags types using that representation, you can implement -//! the standard `Display` and `FromStr` traits in this fashion: -//! -//! ``` -//! use bitflags::bitflags; -//! use std::{fmt, str}; -//! -//! bitflags! { -//! pub struct Flags: u32 { -//! const A = 1; -//! const B = 2; -//! const C = 4; -//! const D = 8; -//! } -//! } -//! -//! impl fmt::Debug for Flags { -//! fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { -//! fmt::Debug::fmt(&self.0, f) -//! } -//! } -//! -//! impl fmt::Display for Flags { -//! fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { -//! fmt::Display::fmt(&self.0, f) -//! } -//! } -//! -//! impl str::FromStr for Flags { -//! type Err = bitflags::parser::ParseError; -//! -//! fn from_str(flags: &str) -> Result { -//! Ok(Self(flags.parse()?)) -//! } -//! } -//! ``` -//! -//! ## `PartialEq` and `PartialOrd` -//! -//! Equality and ordering can be derived for a reasonable implementation, or implemented manually -//! for different semantics. -//! -//! # Edge cases -//! -//! ## Zero Flags -//! -//! Flags with a value equal to zero will have some strange behavior that one should be aware of. -//! -//! ``` -//! use bitflags::bitflags; -//! -//! bitflags! { -//! #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -//! struct Flags: u32 { -//! const NONE = 0b00000000; -//! const SOME = 0b00000001; -//! } -//! } -//! -//! fn main() { -//! let empty = Flags::empty(); -//! let none = Flags::NONE; -//! let some = Flags::SOME; -//! -//! // Zero flags are treated as always present -//! assert!(empty.contains(Flags::NONE)); -//! assert!(none.contains(Flags::NONE)); -//! assert!(some.contains(Flags::NONE)); -//! -//! // Zero flags will be ignored when testing for emptiness -//! assert!(none.is_empty()); -//! } -//! ``` -//! -//! Users should generally avoid defining a flag with a value of zero. -//! -//! ## Multi-bit Flags -//! -//! It is allowed to define a flag with multiple bits set. When using multi-bit flags -//! with [`from_bits`] or [`from_bits_truncate`], if only a subset of the bits in that flag -//! are set, the result will still be non-empty: -//! -//! ``` -//! use bitflags::bitflags; -//! -//! bitflags! { -//! #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -//! struct Flags: u8 { -//! const F3 = 0b00000011; -//! } -//! } -//! -//! fn main() { -//! // This bit pattern does not set all the bits in `F3`, so it is rejected. -//! assert!(Flags::from_bits(0b00000001).is_some()); -//! assert!(!Flags::from_bits_truncate(0b00000001).is_empty()); -//! } -//! ``` -//! -//! [`from_bits`]: Flags::from_bits -//! [`from_bits_truncate`]: Flags::from_bits_truncate -//! -//! # The `Flags` trait -//! -//! This library defines a `Flags` trait that's implemented by all generated flags types. -//! The trait makes it possible to work with flags types generically: -//! -//! ``` -//! fn count_unset_flags(flags: &F) -> usize { -//! // Find out how many flags there are in total -//! let total = F::all().iter().count(); -//! -//! // Find out how many flags are set -//! let set = flags.iter().count(); -//! -//! total - set -//! } -//! -//! use bitflags::bitflags; -//! -//! bitflags! { -//! #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -//! struct Flags: u32 { -//! const A = 0b00000001; -//! const B = 0b00000010; -//! const C = 0b00000100; -//! } -//! } -//! -//! assert_eq!(2, count_unset_flags(&Flags::B)); -//! ``` -//! -//! # The internal field -//! -//! This library generates newtypes like: -//! -//! ``` -//! # pub struct Field0; -//! pub struct Flags(Field0); -//! ``` -//! -//! You can freely use methods and trait implementations on this internal field as `.0`. -//! For details on exactly what's generated for it, see the [`Field0`](example_generated/struct.Field0.html) -//! example docs. +/*! +Generate types for C-style flags with ergonomic APIs. + +# Getting started + +Add `bitflags` to your `Cargo.toml`: + +```toml +[dependencies.bitflags] +version = "2.3.3" +``` + +## Generating flags types + +Use the [`bitflags`] macro to generate flags types: + +```rust +use bitflags::bitflags; + +bitflags! { + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] + pub struct Flags: u32 { + const A = 0b00000001; + const B = 0b00000010; + const C = 0b00000100; + } +} +``` + +See the docs for the `bitflags` macro for the full syntax. + +### Externally defined flags types + +If you're generated flags types for an external API, you can define an extra unnamed flag +as a mask of all bits the external source may set. Usually this would be all bits (`!0`): + +```rust +use bitflags::bitflags; + +bitflags! { + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] + pub struct Flags: u32 { + const A = 0b00000001; + const B = 0b00000010; + const C = 0b00000100; + + // The source may set any bits + const _ = !0; + } +} +``` + +Why should you do this? Generated methods like `all` and truncating operators like `!` only consider +bits in defined flags. Adding an unnamed flag makes those methods consider additional bits, +without generating additional constants for them. It helps compatibility when the external source +may start setting additional bits at any time. + +## Working with flags values + +Use generated constants and standard bitwise operators to interact with flags values: + +```rust +# use bitflags::bitflags; +# bitflags! { +# #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +# pub struct Flags: u32 { +# const A = 0b00000001; +# const B = 0b00000010; +# const C = 0b00000100; +# } +# } +// union +let ab = Flags::A | Flags::B; + +// intersection +let a = ab & Flags::A; + +// difference +let b = ab - Flags::A; + +// complement +let c = !ab; +``` + +See the docs for the [`Flags`] trait for more details on operators. + +# Specification + +The terminology and behavior of generated flags types is +[specified in the source repository](https://github.com/bitflags/bitflags/blob/main/spec.md). +Details are repeated in these docs where appropriate, but is exhaustively listed in the spec. Some +things are worth calling out explicitly here. + +## Unknown bits + +Flags types generated by `bitflags` don't guarantee that only bits in flags you define can ever be set. +Bits outside of flags you define are called _unknown bits_. Most operators will treat these unknown bits +the same as known ones, with the exception of `!`, which will unset them. This behavior may be +tightened in future major versions of `bitflags`. + +If you're using `bitflags` for flags types defined externally, such as when binding to C APIs, you +can use an unnamed flag to guarantee all bits are known bits. See the +[externally defined flags types](#externally-defined-flags-types) section for more details. +*/ #![cfg_attr(not(any(feature = "std", test)), no_std)] #![cfg_attr(not(test), forbid(unsafe_code))] @@ -495,63 +189,17 @@ The macros are split into 3 modules: - `external`: where external library traits are implemented conditionally. */ -/// The macro used to generate the flag structure. -/// -/// See the [crate level docs](../bitflags/index.html) for complete documentation. -/// -/// # Example -/// -/// ``` -/// use bitflags::bitflags; -/// -/// bitflags! { -/// #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -/// struct Flags: u32 { -/// const A = 0b00000001; -/// const B = 0b00000010; -/// const C = 0b00000100; -/// const ABC = Self::A.bits() | Self::B.bits() | Self::C.bits(); -/// } -/// } -/// -/// let e1 = Flags::A | Flags::C; -/// let e2 = Flags::B | Flags::C; -/// assert_eq!((e1 | e2), Flags::ABC); // union -/// assert_eq!((e1 & e2), Flags::C); // intersection -/// assert_eq!((e1 - e2), Flags::A); // set difference -/// assert_eq!(!e2, Flags::A); // set complement -/// ``` -/// -/// The generated `struct`s can also be extended with type and trait -/// implementations: -/// -/// ``` -/// use std::fmt; -/// -/// use bitflags::bitflags; -/// -/// bitflags! { -/// #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -/// struct Flags: u32 { -/// const A = 0b00000001; -/// const B = 0b00000010; -/// } -/// } -/// -/// impl Flags { -/// pub fn clear(&mut self) { -/// *self.0.bits_mut() = 0; -/// } -/// } -/// -/// let mut flags = Flags::A | Flags::B; -/// -/// flags.clear(); -/// assert!(flags.is_empty()); -/// -/// assert_eq!(format!("{:?}", Flags::A | Flags::B), "Flags(A | B)"); -/// assert_eq!(format!("{:?}", Flags::B), "Flags(B)"); -/// ``` +/** +Generate a flags type. + +# `struct` mode + +# `impl` mode + +# Named and unnamed flags + +# Generated API +*/ #[macro_export(local_inner_macros)] macro_rules! bitflags { ( From c8cf8f0805a4a3cea51e3a13c85193fd2e027165 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Mon, 24 Jul 2023 10:45:27 +1000 Subject: [PATCH 25/36] work on library docs --- src/lib.rs | 104 +++++++++++++++++++++++++++++++++++++++++--------- src/traits.rs | 9 +++-- 2 files changed, 91 insertions(+), 22 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 54bf44cc..608f6ca7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,6 @@ Use the [`bitflags`] macro to generate flags types: use bitflags::bitflags; bitflags! { - #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct Flags: u32 { const A = 0b00000001; const B = 0b00000010; @@ -39,16 +38,17 @@ bitflags! { See the docs for the `bitflags` macro for the full syntax. -### Externally defined flags types +Also see the [`example_generated`] module for an example of what the `bitflags` macro generates for a flags type. -If you're generated flags types for an external API, you can define an extra unnamed flag -as a mask of all bits the external source may set. Usually this would be all bits (`!0`): +### Unnamed flags + +If you're generating flags types for an externally defined source you can define an extra unnamed flag +as a mask of all bits the external source may ever set. Usually this would be all bits (`!0`): ```rust use bitflags::bitflags; bitflags! { - #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct Flags: u32 { const A = 0b00000001; const B = 0b00000010; @@ -63,7 +63,57 @@ bitflags! { Why should you do this? Generated methods like `all` and truncating operators like `!` only consider bits in defined flags. Adding an unnamed flag makes those methods consider additional bits, without generating additional constants for them. It helps compatibility when the external source -may start setting additional bits at any time. +may start setting additional bits at any time. The [known and unknown bits](#known-and-unknown-bits) +section has more details on this behavior. + +### Bring-your-own struct + +You can define your own flags type outside of the [`bitflags`] macro and then use it to generate methods. +This can be useful if you need a custom `#[derive]` attribute for a library that `bitflags` doesn't +natively support: + +```rust +# use std::fmt::Debug as SomeTrait; +use bitflags::bitflags; + +#[derive(SomeTrait)] +pub struct Flags(u32); + +bitflags! { + impl Flags: u32 { + const A = 0b00000001; + const B = 0b00000010; + const C = 0b00000100; + } +} +``` + +### Attributes and impl blocks + +The [`bitflags`] macro supports attributes on generated flags types within the macro itself, while +`impl` blocks can be added outside of it: + +```rust +use bitflags::bitflags; + +bitflags! { + // Attributes can be applied to flags types + #[repr(transparent)] + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct Flags: u32 { + const A = 0b00000001; + const B = 0b00000010; + const C = 0b00000100; + } +} + +// Impl blocks can be added to flags types +impl Flags { + pub fn as_u64(&self) -> u64 { + self.bits() as u64 + } +} +``` ## Working with flags values @@ -92,7 +142,13 @@ let b = ab - Flags::A; let c = !ab; ``` -See the docs for the [`Flags`] trait for more details on operators. +See the docs for the [`Flags`] trait for more details on operators and how they behave. + +# Formatting and parsing + +`bitflags` defines a text format that can be used to convert any flags value to and from strings. + +See the [`parser`] module for more details. # Specification @@ -101,16 +157,16 @@ The terminology and behavior of generated flags types is Details are repeated in these docs where appropriate, but is exhaustively listed in the spec. Some things are worth calling out explicitly here. -## Unknown bits +## Known and unknown bits -Flags types generated by `bitflags` don't guarantee that only bits in flags you define can ever be set. -Bits outside of flags you define are called _unknown bits_. Most operators will treat these unknown bits -the same as known ones, with the exception of `!`, which will unset them. This behavior may be -tightened in future major versions of `bitflags`. +Any bit that's set in a flag you define are considered _known bits_. Any other bits are _unknown bits_. +`bitflags` doesn't guarantee that a flags value will only ever have known bits set, but some operators +will unset any unknown bits they encounter. In a future version of `bitflags`, all operators will +unset unknown bits. -If you're using `bitflags` for flags types defined externally, such as when binding to C APIs, you -can use an unnamed flag to guarantee all bits are known bits. See the -[externally defined flags types](#externally-defined-flags-types) section for more details. +If you're using `bitflags` for flags types defined externally, such as from C, you probably want all +bits to be considered known, in case that external source changes. You can do this using an unnamed +flag, as described in [unnamed flags](#unnamed-flags). */ #![cfg_attr(not(any(feature = "std", test)), no_std)] @@ -194,11 +250,19 @@ Generate a flags type. # `struct` mode +A declaration that begins with `$vis struct` will generate a `struct` for a flags type, along with +methods and trait implementations for it. + # `impl` mode +A declaration that begins with `impl` will only generate methods and trait implementations for the +`struct` defined outside of the `bitflags` macro. + # Named and unnamed flags -# Generated API +Constants in the body of a declaration are flags. The identifier of the constant is the name of +the flag. If the identifier is `_`, then the flag is unnamed. Unnamed flags don't appear in the +generated API, but affect how bits are truncated. */ #[macro_export(local_inner_macros)] macro_rules! bitflags { @@ -392,7 +456,9 @@ macro_rules! __impl_bitflags { $bits } - /// Convert from a bits value, unless any unset bits are set. + /// Convert from a bits value. + /// + /// This method will return `None` if any unknown bits are set. #[inline] pub const fn from_bits(bits: $T) -> $crate::__private::core::option::Option { let $from_bits0 = bits; @@ -406,7 +472,7 @@ macro_rules! __impl_bitflags { $from_bits_truncate } - /// Convert from a bits value, without altering them in any way. + /// Convert from a bits value exactly. #[inline] pub const fn from_bits_retain(bits: $T) -> Self { let $from_bits_retain0 = bits; @@ -430,7 +496,7 @@ macro_rules! __impl_bitflags { $is_empty } - /// Whether all defined flags are contained in this flags value. + /// Whether all known bits in this flags value are set. #[inline] pub const fn is_all(&self) -> bool { let $is_all0 = self; diff --git a/src/traits.rs b/src/traits.rs index 4ca2465f..69af3a46 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -64,7 +64,9 @@ pub trait Flags: Sized + 'static { /// The returned value is exactly the bits set in this flags value. fn bits(&self) -> Self::Bits; - /// Convert from a bits value, unless any unset bits are set. + /// Convert from a bits value. + /// + /// This method will return `None` if any unknown bits are set. fn from_bits(bits: Self::Bits) -> Option { let truncated = Self::from_bits_truncate(bits); @@ -80,7 +82,7 @@ pub trait Flags: Sized + 'static { Self::from_bits_retain(bits & Self::all().bits()) } - /// Convert from a bits value, without altering them in any way. + /// Convert from a bits value exactly. fn from_bits_retain(bits: Self::Bits) -> Self; /// Get a flags value with the bits of a flag with the given name set. @@ -123,7 +125,7 @@ pub trait Flags: Sized + 'static { self.bits() == Self::Bits::EMPTY } - /// Whether all defined flags are contained in this flags value. + /// Whether all known bits in this flags value are set. fn is_all(&self) -> bool { // NOTE: We check against `Self::all` here, not `Self::Bits::ALL` // because the set of all flags may not use all bits @@ -304,6 +306,7 @@ pub trait PublicFlags { type Internal; } +#[doc(hidden)] #[deprecated(note = "use the `Flags` trait instead")] pub trait BitFlags: ImplementedByBitFlagsMacro + Flags { /// An iterator over enabled flags in an instance of the type. From ff558841092e21683e21f896186a7ecc1e1e5ac4 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Mon, 31 Jul 2023 19:59:59 +1000 Subject: [PATCH 26/36] work on docs --- src/lib.rs | 150 +++++++++++++++++++++++++++++++++++++++++++++----- src/traits.rs | 44 ++++++++++++++- 2 files changed, 179 insertions(+), 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 608f6ca7..01c7e4f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,14 +40,13 @@ See the docs for the `bitflags` macro for the full syntax. Also see the [`example_generated`] module for an example of what the `bitflags` macro generates for a flags type. -### Unnamed flags +### Externally defined flags -If you're generating flags types for an externally defined source you can define an extra unnamed flag -as a mask of all bits the external source may ever set. Usually this would be all bits (`!0`): +If you're generating flags types for an external source, such as a C API, you can define +an extra unnamed flag as a mask of all bits the external source may ever set. Usually this would be all bits (`!0`): ```rust -use bitflags::bitflags; - +# use bitflags::bitflags; bitflags! { pub struct Flags: u32 { const A = 0b00000001; @@ -66,16 +65,24 @@ without generating additional constants for them. It helps compatibility when th may start setting additional bits at any time. The [known and unknown bits](#known-and-unknown-bits) section has more details on this behavior. -### Bring-your-own struct +### Custom derives -You can define your own flags type outside of the [`bitflags`] macro and then use it to generate methods. +You can derive some traits on generated flags types if you enable Cargo features. The following +libraries are currently supported: + +- `serde`: Support `#[derive(Serialize, Deserialize)]`, using text for human-readable formats, +and a raw number for binary formats. +- `arbitrary`: Support `#[derive(Arbitrary)]`, only generating flags values with known bits. +- `bytemuck`: Support `#[derive(Pod, Zeroable)]`, for casting between flags values and their +underlying bits values. + +You can also define your own flags type outside of the [`bitflags`] macro and then use it to generate methods. This can be useful if you need a custom `#[derive]` attribute for a library that `bitflags` doesn't natively support: ```rust # use std::fmt::Debug as SomeTrait; -use bitflags::bitflags; - +# use bitflags::bitflags; #[derive(SomeTrait)] pub struct Flags(u32); @@ -88,14 +95,13 @@ bitflags! { } ``` -### Attributes and impl blocks +### Adding custom methods The [`bitflags`] macro supports attributes on generated flags types within the macro itself, while `impl` blocks can be added outside of it: ```rust -use bitflags::bitflags; - +# use bitflags::bitflags; bitflags! { // Attributes can be applied to flags types #[repr(transparent)] @@ -159,7 +165,7 @@ things are worth calling out explicitly here. ## Known and unknown bits -Any bit that's set in a flag you define are considered _known bits_. Any other bits are _unknown bits_. +Any bits in a flag you define are called _known bits_. Any other bits are _unknown bits_. `bitflags` doesn't guarantee that a flags value will only ever have known bits set, but some operators will unset any unknown bits they encounter. In a future version of `bitflags`, all operators will unset unknown bits. @@ -167,6 +173,14 @@ unset unknown bits. If you're using `bitflags` for flags types defined externally, such as from C, you probably want all bits to be considered known, in case that external source changes. You can do this using an unnamed flag, as described in [unnamed flags](#unnamed-flags). + +## Zero-bit flags + +Avoid them. + +## Multi-bit flags + +Avoid them, unless each bit is also part of a single-bit flag. */ #![cfg_attr(not(any(feature = "std", test)), no_std)] @@ -251,18 +265,126 @@ Generate a flags type. # `struct` mode A declaration that begins with `$vis struct` will generate a `struct` for a flags type, along with -methods and trait implementations for it. +methods and trait implementations for it. The body of the declaration defines flags as constants, +where each constant is a flags value of the generated flags type. + +## Examples + +Generate a flags type using `u8` as the bits type: + +``` +# use bitflags::bitflags; +bitflags! { + struct Flags: u8 { + const A = 1; + const B = 1 << 1; + const C = 0b0000_0100; + } +} +``` + +Flags types are private by default and accept standard visibility modifiers. Flags themselves +are always public: + +``` +# use bitflags::bitflags; +bitflags! { + pub struct Flags: u8 { + // Constants are always `pub` + const A = 1; + } +} +``` + +Flags may refer to other flags using their [`Flags::bits`] value: + +``` +# use bitflags::bitflags; +bitflags! { + struct Flags: u8 { + const A = 1; + const B = 1 << 1; + const AB = Flags::A.bits() | Flags::B.bits(); + } +} +``` + +A single `bitflags` invocation may include zero or more flags type declarations: + +``` +# use bitflags::bitflags; +bitflags! {} + +bitflags! { + struct Flags1: u8 { + const A = 1; + } + + struct Flags2: u8 { + const A = 1; + } +} +``` # `impl` mode A declaration that begins with `impl` will only generate methods and trait implementations for the `struct` defined outside of the `bitflags` macro. +The struct itself must be a newtype using the bits type as its field. + +The syntax for `impl` mode is identical to `struct` mode besides the starting token. + +## Examples + +Implement flags methods and traits for a custom flags type using `u8` as its underlying bits type: + +``` +# use bitflags::bitflags; +struct Flags(u8); + +bitflags! { + impl Flags: u8 { + const A = 1; + const B = 1 << 1; + const C = 0b0000_0100; + } +} +``` + # Named and unnamed flags Constants in the body of a declaration are flags. The identifier of the constant is the name of the flag. If the identifier is `_`, then the flag is unnamed. Unnamed flags don't appear in the generated API, but affect how bits are truncated. + +## Examples + +Adding an unnamed flag that makes all bits known: + +``` +# use bitflags::bitflags; +bitflags! { + struct Flags: u8 { + const A = 1; + const B = 1 << 1; + + const _ = !0; + } +} +``` + +Flags types may define multiple unnamed flags: + +``` +# use bitflags::bitflags; +bitflags! { + struct Flags: u8 { + const _ = 1; + const _ = 1 << 1; + } +} +``` */ #[macro_export(local_inner_macros)] macro_rules! bitflags { diff --git a/src/traits.rs b/src/traits.rs index 69af3a46..376a7347 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -35,7 +35,49 @@ impl Flag { } } -/// A set of defined flags over a specific bits type. +/** +A set of defined flags using a bits type as storage. + +## Implementing `Flags` + +This trait is implemented by the [`bitflags`](macro.bitflags.html) macro: + +``` +use bitflags::bitflags; + +bitflags! { + struct MyFlags: u8 { + const A = 1; + const B = 1 << 1; + } +} +``` + +It can also be implemented manually: + +``` +use bitflags::{Flag, Flags}; + +struct MyFlags(u8); + +impl Flags for MyFlags { + const FLAGS: &'static [Flag] = &[ + Flag::new("A", MyFlags(1)), + Flag::new("B", MyFlags(1 << 1)), + ]; + + type Bits = u8; + + fn from_bits_retain(bits: Self::Bits) -> Self { + MyFlags(bits) + } + + fn bits(&self) -> Self::Bits { + self.0 + } +} +``` +*/ pub trait Flags: Sized + 'static { /// The set of defined flags. const FLAGS: &'static [Flag]; From 63d40445ddc46ad34d433ab2a679b4a6f58c68f3 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Mon, 31 Jul 2023 20:20:06 +1000 Subject: [PATCH 27/36] fill in more docs --- src/lib.rs | 20 ++++++++++++++++++-- src/public.rs | 2 +- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 01c7e4f6..6da0bcee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -176,11 +176,27 @@ flag, as described in [unnamed flags](#unnamed-flags). ## Zero-bit flags -Avoid them. +Flags with no bits set should be avoided because they interact strangely with [`Flags::contains`] +and [`Flags::intersects`]. A zero-bit flag is always contained, but is never intersected. The +names of zero-bit flags can be parsed, but are never formatted. ## Multi-bit flags -Avoid them, unless each bit is also part of a single-bit flag. +Flags that set multiple bits should be avoided unless each bit is also in a single-bit flag. +Take the following flags type as an example: + +``` +# use bitflags::bitflags; +bitflags! { + struct Flags: u8 { + const A = 1; + const B = 1 | 1 << 1; + } +} +``` + +The result of `Flags::A ^ Flags::B` is `0b0000_0010`, which doesn't correspond to either +`Flags::A` or `Flags::B` even though it's still a known bit. */ #![cfg_attr(not(any(feature = "std", test)), no_std)] diff --git a/src/public.rs b/src/public.rs index d0537cc7..967e0dac 100644 --- a/src/public.rs +++ b/src/public.rs @@ -288,7 +288,7 @@ macro_rules! __impl_public_bitflags_iter { /// Yield a set of contained named flags values. /// - /// This method is like [`iter`], except only yields bits in contained named flags. + /// This method is like [`iter`](#method.iter), except only yields bits in contained named flags. /// Any unknown bits, or bits not corresponding to a contained flag will not be yielded. #[inline] pub const fn iter_names(&self) -> $crate::iter::IterNames<$PublicBitFlags> { From ac67dea3d87f0d95fe4a453ff422a7534301579c Mon Sep 17 00:00:00 2001 From: KodrAus Date: Tue, 1 Aug 2023 08:39:07 +1000 Subject: [PATCH 28/36] more docs --- src/external/arbitrary.rs | 4 +- src/external/serde.rs | 12 +++++- src/iter.rs | 31 +++++++++------ src/lib.rs | 2 +- src/parser.rs | 84 ++++++++++++++++++++++----------------- src/traits.rs | 70 +++++++++++++++++++++++++++----- 6 files changed, 142 insertions(+), 61 deletions(-) diff --git a/src/external/arbitrary.rs b/src/external/arbitrary.rs index 715ba251..ea76f0a2 100644 --- a/src/external/arbitrary.rs +++ b/src/external/arbitrary.rs @@ -2,7 +2,9 @@ use crate::Flags; -/// Get a random known flags value. +/** +Generate some arbitrary flags value with only known bits set. +*/ pub fn arbitrary<'a, B: Flags>(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result where B::Bits: arbitrary::Arbitrary<'a>, diff --git a/src/external/serde.rs b/src/external/serde.rs index 1d501db8..be4f2edb 100644 --- a/src/external/serde.rs +++ b/src/external/serde.rs @@ -10,7 +10,11 @@ use serde::{ Deserialize, Deserializer, Serialize, Serializer, }; -/// Serialize a set of flags as a human-readable string or their underlying bits. +/** +Serialize a set of flags as a human-readable string or their underlying bits. + +Any unknown bits will be retained. +*/ pub fn serialize(flags: &B, serializer: S) -> Result where B::Bits: WriteHex + Serialize, @@ -25,7 +29,11 @@ where } } -/// Deserialize a set of flags from a human-readable string or their underlying bits. +/** +Deserialize a set of flags from a human-readable string or their underlying bits. + +Any unknown bits will be retained. +*/ pub fn deserialize<'de, B: Flags, D: Deserializer<'de>>(deserializer: D) -> Result where B::Bits: ParseHex + Deserialize<'de>, diff --git a/src/iter.rs b/src/iter.rs index cad7d15a..7f7ce554 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -1,18 +1,21 @@ -//! Iterating over set flag values. +/*! +Yield the bits of a source flags value in a set of contained flags values. +*/ use crate::{Flag, Flags}; -/// An iterator over a set of flags. -/// -/// Any bits that don't correspond to a valid flag will be yielded -/// as a final item from the iterator. +/** +An iterator over flags values. + +This iterator will yield flags values for contained, defined flags first, with any remaining bits yielded +as a final flags value. +*/ pub struct Iter { inner: IterNames, done: bool, } impl Iter { - /// Create a new iterator over the given set of flags. pub(crate) fn new(flags: &B) -> Self { Iter { inner: IterNames::new(flags), @@ -22,6 +25,7 @@ impl Iter { } impl Iter { + // Used by the `bitflags` macro #[doc(hidden)] pub const fn __private_const_new(flags: &'static [Flag], source: B, remaining: B) -> Self { Iter { @@ -54,9 +58,12 @@ impl Iterator for Iter { } } -/// An iterator over a set of flags and their names. -/// -/// Any bits that don't correspond to a valid flag will be ignored. +/** +An iterator over flags values. + +This iterator only yields flags values for contained, defined, named flags. Any remaining bits +won't be yielded, but can be found with the [`IterNames::remaining`] method. +*/ pub struct IterNames { flags: &'static [Flag], idx: usize, @@ -65,7 +72,6 @@ pub struct IterNames { } impl IterNames { - /// Create a new iterator over the given set of flags. pub(crate) fn new(flags: &B) -> Self { IterNames { flags: B::FLAGS, @@ -77,6 +83,7 @@ impl IterNames { } impl IterNames { + // Used by the bitflags macro #[doc(hidden)] pub const fn __private_const_new(flags: &'static [Flag], source: B, remaining: B) -> Self { IterNames { @@ -87,11 +94,11 @@ impl IterNames { } } - /// Get the remaining (unyielded) flags. + /// Get a flags value of any remaining bits that haven't been yielded yet. /// /// Once the iterator has finished, this method can be used to /// check whether or not there are any bits that didn't correspond - /// to a valid flag remaining. + /// to a contained, defined, named flag remaining. pub fn remaining(&self) -> &B { &self.remaining } diff --git a/src/lib.rs b/src/lib.rs index 6da0bcee..0cd6c92e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -172,7 +172,7 @@ unset unknown bits. If you're using `bitflags` for flags types defined externally, such as from C, you probably want all bits to be considered known, in case that external source changes. You can do this using an unnamed -flag, as described in [unnamed flags](#unnamed-flags). +flag, as described in [externally defined flags](#externally-defined-flags). ## Zero-bit flags diff --git a/src/parser.rs b/src/parser.rs index 1f5a42fd..5081eb41 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,30 +1,32 @@ -//! Parsing flags from text. -//! -//! `bitflags` defines the following *whitespace-insensitive*, *case-sensitive* grammar for flags formatted -//! as text: -//! -//! - _Flags:_ (_Flag_)`|`* -//! - _Flag:_ _Identifier_ | _HexNumber_ -//! - _Identifier:_ Any Rust identifier -//! - _HexNumber_: `0x`([0-9a-fA-F])* -//! -//! As an example, this is how `Flags::A | Flags::B | 0x0c` can be represented as text: -//! -//! ```text -//! A | B | 0x0c -//! ``` -//! -//! Alternatively, it could be represented without whitespace: -//! -//! ```text -//! A|B|0x0C -//! ``` -//! -//! Note that identifiers are *case-sensitive*, so the following is *not equivalent*: -//! -//! ```text -//! a | b | 0x0c -//! ``` +/*! +Parsing flags from text. + +Format and parse a flags value as text using the following grammar: + +- _Flags:_ (_Whitespace_ _Flag_ _Whitespace_)`|`* +- _Flag:_ _Name_ | _Hex Number_ +- _Name:_ The name of any defined flag +- _Hex Number_: `0x`([0-9a-fA-F])* +- _Whitespace_: (\s)* + +As an example, this is how `Flags::A | Flags::B | 0x0c` can be represented as text: + +```text +A | B | 0x0c +``` + +Alternatively, it could be represented without whitespace: + +```text +A|B|0x0C +``` + +Note that identifiers are *case-sensitive*, so the following is *not equivalent*: + +```text +a|b|0x0C +``` +*/ #![allow(clippy::let_unit_value)] @@ -32,10 +34,11 @@ use core::fmt::{self, Write}; use crate::{Bits, Flags}; -/// Write a set of flags to a writer. -/// -/// Any bits that don't correspond to a valid flag will be formatted -/// as a hex number. +/** +Write a flags value as text. + +Any bits that aren't part of a contained flag will be formatted as a hex number. +*/ pub fn to_writer(flags: &B, mut writer: impl Write) -> Result<(), fmt::Error> where B::Bits: WriteHex, @@ -85,9 +88,12 @@ where } } -/// Parse a set of flags from text. -/// -/// This function will fail on unknown flags rather than ignore them. +/** +Parse a flags value from text. + +This function will fail on any names that don't correspond to defined flags. +Unknown bits will be retained. +*/ pub fn from_str(input: &str) -> Result where B::Bits: ParseHex, @@ -128,13 +134,19 @@ where Ok(parsed_flags) } -/// Encode a value as a hex number. +/** +Encode a value as a hex string. + +Implementors of this trait should not write the leading `0x` prefix. +*/ pub trait WriteHex { /// Write the value as hex. fn write_hex(&self, writer: W) -> fmt::Result; } -/// Parse a value from a number encoded as a hex string. +/** +Parse a value from a hex string. +*/ pub trait ParseHex { /// Parse the value from hex. fn parse_hex(input: &str) -> Result diff --git a/src/traits.rs b/src/traits.rs index 376a7347..90561711 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -8,31 +8,57 @@ use crate::{ parser::{ParseError, ParseHex, WriteHex}, }; -/// A set of bits in a bits type that may have a unique name. +/** +A defined flags value that may be named or unnamed. +*/ pub struct Flag { name: &'static str, value: B, } impl Flag { - /// Create a new flag with the given name and value. - /// - /// If `name` is empty then the flag is unnamed. + /** + Define a flag. + + If `name` is non-empty then the flag is named, otherwise it's unnamed. + */ pub const fn new(name: &'static str, value: B) -> Self { Flag { name, value } } - /// Get the name of this flag. - /// - /// If `name` is empty then the flag is unnamed. + /** + Get the name of this flag. + + If the flag is unnamed then the returned string will be empty. + */ pub const fn name(&self) -> &'static str { self.name } - /// Get the value of this flag. + /** + Get the flags value of this flag. + */ pub const fn value(&self) -> &B { &self.value } + + /** + Whether the flag is named. + + If [`name`] returns a non-empty string then this method will return `true`. + */ + pub const fn is_named(&self) -> bool { + self.name != "" + } + + /** + Whether the flag is unnamed. + + If [`name`] returns a non-empty string then this method will return `false`. + */ + pub const fn is_unnamed(&self) -> bool { + self.name == "" + } } /** @@ -77,6 +103,30 @@ impl Flags for MyFlags { } } ``` + +## Using `Flags` + +The `Flags` trait can be used generically to work with any flags types. In this example, +we can count the number of defined named flags: + +``` +# use bitflags::{bitflags, Flags}; +fn defined_flags() -> usize { + F::FLAGS.iter().filter(|f| f.is_named()).count() +} + +bitflags! { + struct MyFlags: u8 { + const A = 1; + const B = 1 << 1; + const C = 1 << 2; + + const _ = !0; + } +} + +assert_eq!(3, defined_flags::()); +``` */ pub trait Flags: Sized + 'static { /// The set of defined flags. @@ -263,7 +313,9 @@ pub trait Flags: Sized + 'static { } } -/// Underlying storage for a flags type. +/** +A bits type that can be used as storage for a flags type. +*/ pub trait Bits: Clone + Copy From 0b4f757d28512aaf1bd0fee84ae7a7e34e979e31 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Tue, 1 Aug 2023 08:42:52 +1000 Subject: [PATCH 29/36] update the readme --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d604d48d..bb7c64a1 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,20 @@ bitflags [![Documentation](https://docs.rs/bitflags/badge.svg)](https://docs.rs/bitflags) ![License](https://img.shields.io/crates/l/bitflags.svg) -A Rust macro to generate structures which behave like a set of bitflags +`bitflags` generates flags enums with well-defined semantics and ergonomic end-user APIs. + +You can use `bitflags` to: + +- provide more user-friendly bindings to C APIs where flags may or may not be fully known in advance. +- generate efficient options types with string parsing and formatting support. + +You can't use `bitflags` to: + +- guarantee only bits corresponding to defined flags will ever be set. `bitflags` allows access to the underlying bits type so arbitrary bits may be set. +- define bitfields. `bitflags` only generates types where set bits denote the presence of some combination of flags. - [Documentation](https://docs.rs/bitflags) +- [Specification](https://github.com/bitflags/bitflags/blob/main/spec.md) - [Release notes](https://github.com/bitflags/bitflags/releases) ## Usage From 665720767852599b9f4eff5ec418473fbf1dbd78 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Tue, 1 Aug 2023 08:59:43 +1000 Subject: [PATCH 30/36] add some key definitions to the readme --- src/lib.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 0cd6c92e..8fe5408a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -163,9 +163,48 @@ The terminology and behavior of generated flags types is Details are repeated in these docs where appropriate, but is exhaustively listed in the spec. Some things are worth calling out explicitly here. +## Flags types, flags values, flags + +The spec and these docs use consistent terminology to refer to things in the bitflags domain: + +- **Bits type**: A type that defines a fixed number of bits at specific locations. +- **Flag**: A set of bits in a bits type that may have a unique name. +- **Flags type**: A set of defined flags over a specific bits type. +- **Flags value**: An instance of a flags type using its specific bits value for storage. + +``` +# use bitflags::bitflags; +bitflags! { + struct FlagsType: u8 { +// -- Bits type +// --------- Flags type + const A = 1; +// ----- Flag + } +} + +let flag = FlagsType::A; +// ---- Flags value +``` + ## Known and unknown bits Any bits in a flag you define are called _known bits_. Any other bits are _unknown bits_. +In the following flags type: + +``` +# use bitflags::bitflags; +bitflags! { + struct Flags: u8 { + const A = 1; + const B = 1 << 1; + const C = 1 << 2; + } +} +``` + +The known bits are `0b0000_0111` and the unknown bits are `0b1111_1000`. + `bitflags` doesn't guarantee that a flags value will only ever have known bits set, but some operators will unset any unknown bits they encounter. In a future version of `bitflags`, all operators will unset unknown bits. From 635fb1457659781b5a3c6db8097bd1e390e993c9 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Fri, 11 Aug 2023 18:47:15 +1000 Subject: [PATCH 31/36] use const fns for name comparison --- src/traits.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/traits.rs b/src/traits.rs index 90561711..6583135a 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -48,7 +48,7 @@ impl Flag { If [`name`] returns a non-empty string then this method will return `true`. */ pub const fn is_named(&self) -> bool { - self.name != "" + !self.name.is_empty() } /** @@ -57,7 +57,7 @@ impl Flag { If [`name`] returns a non-empty string then this method will return `false`. */ pub const fn is_unnamed(&self) -> bool { - self.name == "" + self.name.is_empty() } } From eb5d8a6a3ae8f8fd441fc654ae604a8ac568a471 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Fri, 11 Aug 2023 18:48:57 +1000 Subject: [PATCH 32/36] fix up some broken links --- src/traits.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/traits.rs b/src/traits.rs index 6583135a..28235142 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -45,7 +45,7 @@ impl Flag { /** Whether the flag is named. - If [`name`] returns a non-empty string then this method will return `true`. + If [`Flag::name`] returns a non-empty string then this method will return `true`. */ pub const fn is_named(&self) -> bool { !self.name.is_empty() @@ -54,7 +54,7 @@ impl Flag { /** Whether the flag is unnamed. - If [`name`] returns a non-empty string then this method will return `false`. + If [`Flag::name`] returns a non-empty string then this method will return `false`. */ pub const fn is_unnamed(&self) -> bool { self.name.is_empty() From df23c23adf133fc1cc6dedef9ec2c18fbf597b4d Mon Sep 17 00:00:00 2001 From: KodrAus Date: Fri, 11 Aug 2023 18:51:40 +1000 Subject: [PATCH 33/36] redundant qualifier --- src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.rs b/src/parser.rs index 5081eb41..130dc2e1 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -137,7 +137,7 @@ where /** Encode a value as a hex string. -Implementors of this trait should not write the leading `0x` prefix. +Implementors of this trait should not write the `0x` prefix. */ pub trait WriteHex { /// Write the value as hex. From ec25c8295163f8c807c59ffb9c81197006229abc Mon Sep 17 00:00:00 2001 From: KodrAus Date: Fri, 11 Aug 2023 18:57:06 +1000 Subject: [PATCH 34/36] ensure compile tests run in CI --- .github/workflows/rust.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 15d1f950..e5edec86 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -51,6 +51,22 @@ jobs: - name: Default features run: cargo bench --no-run + ui: + name: UI + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab + + - name: Install Rust toolchain + run: rustup default beta + + - name: Compile pass + run: cargo test pass + + - name: Compile fail + run: cargo test fail + msrv: name: MSRV runs-on: ubuntu-latest From a86ed60cfef90ccdf7df1a8114076ad6c8a0acd2 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Fri, 11 Aug 2023 19:02:51 +1000 Subject: [PATCH 35/36] update error message --- tests/compile-fail/bitflags_custom_bits.stderr | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/compile-fail/bitflags_custom_bits.stderr b/tests/compile-fail/bitflags_custom_bits.stderr index 799461ea..29430055 100644 --- a/tests/compile-fail/bitflags_custom_bits.stderr +++ b/tests/compile-fail/bitflags_custom_bits.stderr @@ -5,14 +5,14 @@ error[E0277]: the trait bound `MyInt: bitflags::traits::Primitive` is not satisf | ^^^^^ the trait `bitflags::traits::Primitive` is not implemented for `MyInt` | = help: the following other types implement trait `bitflags::traits::Primitive`: - i128 + isize + i8 i16 i32 i64 - i8 - isize - u128 - u16 + i128 + usize + u8 and $N others note: required by a bound in `PublicFlags::Primitive` --> src/traits.rs From 6a966d70d8a2d4b1cc5c9d19eac04ee6571ea049 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Fri, 11 Aug 2023 19:05:22 +1000 Subject: [PATCH 36/36] fix up an unwanted compiler error --- tests/compile-fail/bitflags_custom_bits.rs | 2 +- tests/compile-fail/bitflags_custom_bits.stderr | 15 --------------- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/tests/compile-fail/bitflags_custom_bits.rs b/tests/compile-fail/bitflags_custom_bits.rs index b719de4f..cfa3c0ba 100644 --- a/tests/compile-fail/bitflags_custom_bits.rs +++ b/tests/compile-fail/bitflags_custom_bits.rs @@ -125,7 +125,7 @@ impl ParseHex for MyInt { impl WriteHex for MyInt { fn write_hex(&self, writer: W) -> fmt::Result { - LowerHex::fmt(&self.0, writer) + write!(writer, "{:x}", self.0) } } diff --git a/tests/compile-fail/bitflags_custom_bits.stderr b/tests/compile-fail/bitflags_custom_bits.stderr index 29430055..bc1a71eb 100644 --- a/tests/compile-fail/bitflags_custom_bits.stderr +++ b/tests/compile-fail/bitflags_custom_bits.stderr @@ -19,18 +19,3 @@ note: required by a bound in `PublicFlags::Primitive` | | type Primitive: Primitive; | ^^^^^^^^^ required by this bound in `PublicFlags::Primitive` - -error[E0308]: mismatched types - --> tests/compile-fail/bitflags_custom_bits.rs:128:32 - | -127 | fn write_hex(&self, writer: W) -> fmt::Result { - | - this type parameter -128 | LowerHex::fmt(&self.0, writer) - | ------------- ^^^^^^ expected `&mut Formatter<'_>`, found type parameter `W` - | | - | arguments to this function are incorrect - | - = note: expected mutable reference `&mut Formatter<'_>` - found type parameter `W` -note: method defined here - --> $RUST/core/src/fmt/mod.rs