Robbepop / modular-bitfield

Macro to generate bitfields for structs that allow for modular use of enums.
Apache License 2.0
155 stars 40 forks source link

How to implement signed 10 bit number? #98

Closed JiveyGuy closed 10 months ago

JiveyGuy commented 11 months ago

I am attempting to declare a signed 10-bit number within a struct. How would I do this?

#[bitfield]
#[derive(BinRead, Debug)]
#[br(map = Self::from_bytes)]
pub struct MyStruct
{
    /*
        -00-0 Spare, set to 0
        -01-0 Spare, set to 0
        -02-0 Spare, set to 0
        -03-0 Spare, set to 0
        -04-0 Spare, set to 0
        -05-0 Spare, set to 0
        -06-0 Spare, set to 0
        number:
          -07-Sign
          -08-MSB
          -09-N
          -10-N
          -11-N
          -12-N
          -13-N
          -14-N
          -15-LSB
    */
    // bits
    spare_bits:          B6,
    number:             SignedB10,
}

I thought to do it this way:

#[derive(Debug, BinRead, Clone, Copy)]
struct SignedB10
{
    // this is a 10 bit number
    // with the first bit being the sign
    raw_value: B10,
}

impl SignedB10
{
    pub fn raw_value(&self) -> i16
    {
        return self.raw_value()[0..9].load_be::<i16>();
    }
}

However, this gives the error:

the trait bound `SignedB10: Specifier` is not satisfied

Is there a method for declaring such a field using this package or maybe binrw has a simpler way?

hecatia-elegua commented 11 months ago

I'm not so sure, but does adding #[derive(BitfieldSpecifier)] to struct SignedB10 work? You'd have to implement Specifier for SignedB10 yourself, otherwise.

JiveyGuy commented 11 months ago

I'm not so sure, but does adding #[derive(BitfieldSpecifier)] to struct SignedB10 work? You'd have to implement Specifier for SignedB10 yourself, otherwise.

I am quite new to rust and how structs are impl with traits, it seems that it doesn't work so how would I proceed manually?

••••\••••on  master [✘!?] is 📦 v0.1.0 via 🦀 v1.71.0 
❯ cargo run -- -i ../data/handmade/fake.txt.••••
   Compiling ••••v0.1.0 (C:\Users\••••\OneDrive - ••••\Documents\main_work\work\••••\••••)
error: structs are not supported as bitfield specifiers
  --> src\••••\••••\words\••••.rs:33:1
   |
33 | struct SignedB10
   | ^^^^^^

warning: unused import: `BitfieldSpecifier`
 --> src\••••\••••\words\••••.rs:2:49
  |
2 | use modular_bitfield::{bitfield, specifiers::*, BitfieldSpecifier};
  |                                                 ^^^^^^^^^^^^^^^^^
  |
note: the lint level is defined here
 --> src\main.rs:3:9
  |
3 | #![warn(unused_imports)]
  |         ^^^^^^^^^^^^^^

error[E0277]: the trait bound `SignedB10: Specifier` is not satisfied
  --> src\••••\••••\words\••••.rs:22:5
   |
22 |     ••••:    SignedB10,  // TODO
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Specifier` is not implemented for `SignedB10`
   |
   = help: the following other types implement trait `Specifier`:
             AlignStore
             ••••Align
             ••••Commands
             ••••Mode
             B1
             B100
             B101
             B102
           and 161 others

For more information about this error, try `rustc --explain E0277`.
warning: `••••_reader_rs` (bin "••••_reader_rs") generated 1 warning
error: could not compile `••••_reader_rs` (bin "••••_reader_rs") due to 2 previous errors; 1 warning emitted

I tried to do this manually by:

// manually impliment BitfieldSpecifier for signedB10
impl BitfieldSpecifier for SignedB10
{
    type BaseType = i16;
    type BitRangeType = B10;
    type AccessorType = Self;

    fn new(raw_data: Self::BaseType) -> Self
    {
        Self { raw_data }
    }

    fn get_bit_range(&self) -> Self::BitRangeType
    {
        B10::new(self.raw_data)
    }

    fn get_accessor(&self) -> Self::AccessorType
    {
        *self
    }

    fn get_value(&self) -> Self::BaseType
    {
        // return self.raw_data as i16, the first bit is sign bit
        // we are big endian (do manually) 2's compliment:
        // bit[0] = sign
        // bit[1] = 256
        // bit[2] = 128
        // bit[3] = 64
        // bit[4] = 32
        // bit[5] = 16
        // bit[6] = 8
        // bit[7] = 4
        // bit[8] = 2
        // bit[9] = 1
        let temp = self.raw_data as i16;
        let sign = temp & 0b1000_0000_00;
        let mut value = 0;
        if sign == 0b1000_0000_00
        {
            // negative
            value = -1 * (temp & 0b0111_1111_11);
        }
        else
        {
            // positive
            value = temp & 0b0111_1111_11;
        }
        value
    }
}

However, this also gives a similar error:

expected trait, found derive macro `BitfieldSpecifier`
not a trait
hecatia-elegua commented 11 months ago

rn can only guide you a little (have not implemented sth like this myself). It says the trait bound SignedB10: Specifier is not satisfied -> so do impl Specifier instead. Then your IDE should tell you something like "into_bytes and from_bytes expected", or "implement missing members". At least vscode then gives you a quick fix action to add those to the impl. So in from_bytes you'll get the unparsed bits and turn them into your SignedB10 I think.

This is the trait: https://github.com/Robbepop/modular-bitfield/blob/b24388c40b58c18c4fdf8f9cd561bf0e75b86ae2/src/lib.rs#L453

JiveyGuy commented 10 months ago

Follow-up question, is there a "modular bitfield" supported way to pull the first bit out of a B10?

This is useful for signed numbers where the first bit has to be checked.

hecatia-elegua commented 10 months ago
struct SignedB10 {
   sign: B1,
   this_is_called_magnitude_i_think: B9,
}
JiveyGuy commented 10 months ago
struct SignedB10 {
   sign: B1,
   this_is_called_magnitude_i_think: B9,
}

Oh, there is no way to have a B10 and then later do something like:

let sign = self.my_field().to_iter()[0];
hecatia-elegua commented 10 months ago

You'd have to call .into_bytes()[0] on it and then do the bitfiddling yourself at that point.

JiveyGuy commented 10 months ago

.into_bits() that returns an [bool; n] would be great in the future.

Though this is just the answer I was looking for thanks @hecatia-elegua