Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dart:ffi Struct bit fields #38954

Open
ethanblake4 opened this issue Oct 17, 2019 · 5 comments
Open

dart:ffi Struct bit fields #38954

ethanblake4 opened this issue Oct 17, 2019 · 5 comments
Labels
area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. contributions-welcome Contributions welcome to help resolve this (the resolution is expected to be clear from the issue) library-ffi

Comments

@ethanblake4
Copy link

ethanblake4 commented Oct 17, 2019

How do we support bit fields in dart:ffi? Something like:

typedef struct {
    unsigned int bold      : 1;
    unsigned int underline : 2;
    unsigned int italic    : 1;
    unsigned int blink     : 1;
    unsigned int reverse   : 1;
    unsigned int strike    : 1;
    unsigned int font      : 4;
} ScreenCellAttrs;

what would be the correct way to represent this from within Dart?

@srawlins srawlins added area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. library-ffi type-question A question about expected behavior or functionality labels Oct 17, 2019
@dcharkes
Copy link
Contributor

We do not have support for that yet, but it's a good suggestion.

The current workaround would be to write a wrapper in Dart that does the bit-masking:

const _boldMask = 0x1;

const _underlineOffset = 1;
const _underlineMask = 0x6;

class ScreenCellAttrs extends Struct {
  @Int16()
  int bits;

  int get bold => bits & _boldMask;
  void set bold(int value) {
    bits = (bits & ~_boldMask) | (value & _boldMask);
  }

  int get underline => (bits & _underlineMask) >> _underlineOffset;
  void set underline(int value) {
    bits = (bits & ~_underlineMask) |
        ((value << _underlineOffset) & _underlineMask);
  }
}

main() {
  final p = allocate<ScreenCellAttrs>();
  p.ref.bold = 1;
  p.ref.underline = 2;
  print(p.ref.bold);
  print(p.ref.underline);
  free(p);
}

@dcharkes dcharkes removed the type-question A question about expected behavior or functionality label Oct 23, 2019
@ethanblake4
Copy link
Author

ethanblake4 commented Oct 23, 2019

Thanks!

For anyone else who comes across this issue, here's a simple mixin I made to make it easier:

mixin Bitfield16 on Struct {
  @Int16()
  int bits;

  int extract(int offset, int size) => (bits & (((math.pow(2, size) as int) - 1) << offset)) >> offset;
}

dart-bot pushed a commit that referenced this issue Oct 24, 2019
Issue: #38954
Change-Id: I04b2b81d83a19c5ff88d4364256b23594fe2139b
Cq-Include-Trybots: luci.dart.try:vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64-try,analyzer-analysis-server-linux-try,analyzer-linux-release-try,front-end-linux-release-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/122762
Commit-Queue: Daco Harkes <[email protected]>
Reviewed-by: Martin Kustermann <[email protected]>
@dcharkes
Copy link
Contributor

I've added a full example in the commit above. It uses bit shifts instead of math functions, so it should be a bit faster.

We could make writing these bit fields by hand a bit more ergonomic by exposing the int extension, and a predefined set of fixed-size bit field mixins in package:ffi.

/// Extension to use a 64-bit integer as bit field.
extension IntBitField on int {
  static int _bitMask(int offset, int lenght) => ((1 << lenght) - 1) << offset;

  /// Read `length` bits at `offset`.
  ///
  /// Truncates everything.
  int getBits(int offset, int length) {
    final mask = _bitMask(offset, length);
    return (this & mask) >> offset;
  }

  /// Returns a new integer value in which `length` bits are overwritten at
  /// `offset`.
  ///
  /// Truncates everything.
  int setBits(int offset, int length, int value) {
    final mask = _bitMask(offset, length);
    return (this & ~mask) | ((value << offset) & mask);
  }
}
mixin BitField16 on Struct {
  @Int16()
  int bits;

  int getBits(int offset, int length) => bits.getBits(offset, length);

  void setBits(int offset, int length, int value) {
    bits = bits.setBits(offset, length, value);
  }
}

In the end we probably would like to have support for bitfields in a .h-to-.dart bindings generator as the most ergonomic option (#35843).

From that perspective, we do not need to add support for bit fields in dart:ffi directly.

@dcharkes
Copy link
Contributor

The landed sample is a decent solution. If anyone has problems using that solution, please reopen this issue.

@dcharkes
Copy link
Contributor

dcharkes commented Sep 7, 2020

This needs to be supported in the VM for fully platform agnostic bitfield definitions, see dart-lang/native#406.

@dcharkes dcharkes reopened this Sep 7, 2020
@dcharkes dcharkes added the good first issue A good starting issue for contributors (issues with this label will appear in /contribute) label Jun 18, 2024
@dcharkes dcharkes added contributions-welcome Contributions welcome to help resolve this (the resolution is expected to be clear from the issue) and removed good first issue A good starting issue for contributors (issues with this label will appear in /contribute) labels Aug 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. contributions-welcome Contributions welcome to help resolve this (the resolution is expected to be clear from the issue) library-ffi
Projects
None yet
Development

No branches or pull requests

3 participants