scott-griffiths / bitstring

A Python module to help you manage your bits
https://bitstring.readthedocs.io/en/stable/index.html
MIT License
401 stars 67 forks source link

Add support for LSB0 on a per object / instance basis #309

Closed hendrickmelo closed 4 months ago

hendrickmelo commented 6 months ago

Support for LSB0 can currently set for the entire module as it replaces bound methods of Bits, BitArray, and BitStore with _lsb0 or _msb0 versions. This is nice but in a codebase that deals with different rules for different sources of data, sometimes we want to set/access specific bits in a BitArray directly by index in LSB0 for some variables, and MSB0 for others.

In the default case, reversing / [::-1] doesn't solve the problem because it returns a reversed array, so it is not ideal. When you want to use indices and/or slices to access specific bits in an LSB0 fashion, you can't:

In [1]: from bitstring import BitArray
   ...: bb = BitArray("0b101010")
   ...: print(bb[::])
0b101010

In [2]: bb[-1] = True # set the lsb with -i -1 approach
   ...: print(bb[::])
   ...: print(bb[::-1]) # inverted
0b101011
0b110101

In [3]: bb[::-1][0]= False # doesn't modify the source
   ...: print(bb[::-1])
0b110101

One way to solve it could be to have objects like BitArray_lsb0 (or better naming) added to the API. In that case, the objects would use bounded _lsb0 methods).

A different way (runtime, may be expensive) would be to set it in the initializer like BitArray("0b000", lsb0=True)

scott-griffiths commented 6 months ago

Hi, thanks for the feature request.

Making individual bitstrings either lsb0 or msb0 isn't really something I can wrap my head around. As soon as they interact in any way (concatenation, finding, etc) it becomes very unclear what a reasonable behaviour should be.

Take a look at issue #265 though, as I think some of what you need is covered by that. That's more just about allowing bit-reversed interpretations, so don't cover your case of bit indexing.

The indexing could be done via a new method. Right now bb[x] calls bb.__getitem__(x) and it would be easy to add another method that calls bb.__getitem__(len(bb) - x) (and similarly for extended slices). I've no idea what such a method would be called though as you can't also use the nice bb[x] form for this. bb.getlsb0item(x) ???? I don't think there are any other operators that could be overloaded to make a nice looking syntax.

hendrickmelo commented 6 months ago

Hi Scott, thank you for the reply. Yes, I see the problem with interactions. Creating explicit lsb methods wouldn't be that helpful if we wanted to keep a higher-level API LSB0/MSB0 agnostic.

My thinking was that all it would alter is the access to the data, not necessarily the underlying bits. Per the documentation on how the LSB0 support currently works, it would still be internally stored in the same way. But that's easier said than done. I haven't really dug too far into the codebase to think the whole thing through. I'm sure it's not too trivial.

Thanks anyways. Cheers.