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

How do I use complex data structures with ffi, ref-struct, and ref-array? #28

Open
zacharyabresch opened this issue Dec 7, 2016 · 7 comments

Comments

@zacharyabresch
Copy link

Using Complex Data Structures with ffi, ref-struct, and ref-array

I am working on a project that uses ffi to interact with a compiled C library. I am trying to get some complex data structures to work and am having problems figuring out the proper way to get values inside these data structures. I have two instances I'm trying to figure out. I am not a C guy so I'm winging it on that end.

Instance 1

There is a C struct that contains properties that are, what look like pointers to character arrays. Here's the C-code that defines the struct:

#define RPC_STRING_LENGTH           32
#define RPC_STRING_LENGTH_CALLID    128
typedef struct {
    E_CALL_STATE eCallState;
    BOOL bInboundCall;
    BOOL bVideoCall;
    BOOL bRecording;
    BOOL bEncrypted;
    BOOL bHDAudio;
    BOOL bVirtualMeetingCall;
    E_PROTOCOL_CODEC eCurrentCodec;
    E_PROTOCOL_ALERT_INFO_CALL_TYPE eAlertInfoType;
    double dAccumulatedMOSLQ;
    int  nVideoState;
    int  nErrorCode;
    unsigned int nCallStartTime;
    char cPhoneNumber[RPC_STRING_LENGTH];
    char cName[RPC_STRING_LENGTH];
    char cCallId[RPC_STRING_LENGTH_CALLID];
    char cPreviousCallId[RPC_STRING_LENGTH_CALLID];
} RPC_CALL_STATE;

Now, I've been able to successfully extract the E_CALL_STATE value and the BOOL properties but I'm having difficulty getting the strings stores in cPhoneNumber, cName, cCallId, and cPreviousCallId. Here's how I have the it setup using ref-struct:

import ref from 'ref';
import StructType from 'ref-struct';

const RPC_CALL_STATE = StructType({
    eCallState: ref.types.int,
    bInboundCall: ref.types.bool,
    bVideoCall: ref.types.bool,
    bRecording: ref.types.bool,
    bEncrypted: ref.types.bool,
    bHDAudio: ref.types.bool,
    bVirtualMeetingCall: ref.types.bool,
    dAccumulatedMOSLQ: ref.types.double,
    nVideoState: ref.types.int,
    nErrorCode: ref.types.int,
    nCallStartTime: ref.types.uint,
    cPhoneNumber: ref.types.char,
    cName: ref.types.char,
    cCallId: ref.types.char,
    cPreviousCallId: ref.types.char,
});

Here's the Javascript that interprets this struct (for reference, RPC_CALL_STATE is an externally defined enum):

pDataRef = ref.reinterpret(pData, RPC_CALL_STATE.size);
pDataRef.type = RPC_CALL_STATE;
const callProgressStruct = ref.get(pDataRef);

If I look at the value stored on callProgressStruct.cPhoneNumber, I get 0. So, a few questions:

  • What is the correct ref.types to use here?
    • Is char correct?
    • If not, what should this be?
  • How do I extract the actual string value stored in cPhoneNumber?
  • Is there a way to use ref-array here?

Instance 2

I have a C struct that is defined as such:

typedef struct AUDIO_OS_DEVICES {
    int nNumberDevices;
    AUDIO_OS_DEVICE *pDevices;
} AUDIO_OS_DEVICES;

In this code, *pDevices ends up being an array of AUDIO_OS_DEVICE structs. Those structs are defined in C as:

typedef struct AUDIO_OS_DEVICE {
    int deviceId;
    int deviceFlags;
    char deviceName[AUDIO_OS_DEVICE_NAME_MAX_LEN];
    char deviceUID[AUDIO_OS_DEVICE_UID_MAX_LEN];
    char manufacturerName[AUDIO_OS_DEVICE_MANUFACTURER_MAX_LEN];
} AUDIO_OS_DEVICE;

In Javascript I have this setup using ref-struct:

const AUDIO_OS_DEVICE = StructType({
  deviceId: ref.types.int,
  deviceFlags: ref.types.int,
  deviceName: ref.types.char,
  deviceUID: ref.types.char,
  manufacturerName: ref.types.char,
});

const AUDIO_OS_DEVICES = StructType({
    nNumberDevices: ref.types.int,
    pDevices: [???],
});

I have no idea what type to cast pDevices to here nor do I know how to extract the array of AUDIO_OS_DEVICE structs. I am also hitting the issue already described with character arrays.

Any help would be appreciated. I'm at a dead-end until I can figure this out.

@TooTallNate
Copy link
Owner

For instance 1, yes, use could use ref-array using something like RefArray('char', RPC_STRING_LENGTH). But actually this looks like a good use-case for the "fixed buffer" type that I wrote for a different issue a while back: https://gist.github.com/TooTallNate/80ac2d94b950216a2705

So your type for cPhoneNumber would be something like FixedBuffer(RPC_STRING_LENGTH) and then you would access it like str.cPhoneNumber.toString().

@TooTallNate
Copy link
Owner

For instance 2, use defineProperty():

const AUDIO_OS_DEVICES = StructType({
    nNumberDevices: ref.types.int
})
AUDIO_OS_DEVICES.defineProperty('pDevices', RefArray(AUDIO_OS_DEVICES))

And then pDevices ends up being a ref array instance. It will have length of 0 at first. You must explicitly set the length, it looks like from your nNumberDevices property. So it might look like:

const osDevices = <?get os devices struct?>
const devices = osDevices.pDevices
devices.length = osDevices.nNumberDevices
console.log(devices)

Let me know if that works out for you!

@zacharyabresch
Copy link
Author

Thanks mate! Instance 1 worked like a charm so I'm half-way there.

I'm now working on Instance 2 and attempting to implement what you suggested. Here's what I've got:

Struct definition

const AUDIO_OS_DEVICES = StructType({
  nNumberDevices: ref.types.int,
});
AUDIO_OS_DEVICES.defineProperty('pDevices', ArrayType(AUDIO_OS_DEVICES));

Usage

  getAudioInputDevices() {
    debug('getAudioInputDevices');
    const deviceData = this.client.getAudioInputDevices(ref.types.null);
    const deviceDataRef = ref.reinterpret(deviceData, AUDIO_OS_DEVICES.size);
    deviceDataRef.type = AUDIO_OS_DEVICES;
    const osDevices = ref.get(deviceDataRef);
    const devices = osDevices.pDevices;
    devices.length = Number(osDevices.nNumberDevices);
    debug(devices);
  }

Result

When I run this code I get the following error:

Uncaught Exception:
AssertionError: Buffer instance must be at least 16 bytes to back this struct type
    at new StructType (/Users/zabresch/Documents/8x8/stack-vod/node_modules/ref-struct/lib/struct.js:84:7)
    at Function.get (/Users/zabresch/Documents/8x8/stack-vod/node_modules/ref-struct/lib/struct.js:158:10)
    at Object.get (/Users/zabresch/Documents/8x8/stack-vod/node_modules/ref/lib/ref.js:447:17)
    at ArrayType.getter (/Users/zabresch/Documents/8x8/stack-vod/node_modules/ref-array/lib/array.js:255:15)
    at ArrayType.get (/Users/zabresch/Documents/8x8/stack-vod/node_modules/array-index/index.js:171:32)
    at ArrayType.toArray (/Users/zabresch/Documents/8x8/stack-vod/node_modules/array-index/index.js:78:20)
    at ArrayType.inspect (/Users/zabresch/Documents/8x8/stack-vod/node_modules/array-index/index.js:105:16)
    at formatValue (util.js:360:21)
    at Object.inspect (util.js:198:10)
    at inspect (/Users/zabresch/Documents/8x8/stack-vod/node_modules/debug/node.js:67:17)

I feel like this is so close! Thanks for all your help.

@TooTallNate
Copy link
Owner

Try changing:

AUDIO_OS_DEVICES.defineProperty('pDevices', ArrayType(AUDIO_OS_DEVICES));

to

AUDIO_OS_DEVICES.defineProperty('pDevices', ArrayType(ref.refType(AUDIO_OS_DEVICES)));

@zacharyabresch
Copy link
Author

Well ... that looked promising but Electron just crashes. I checked the crash report and saw:

Exception Type:        EXC_BAD_ACCESS (SIGSEGV)
Exception Codes:       KERN_INVALID_ADDRESS at 0x0000000d00000030

Not sure how much of that log would be relevant.

@zacharyabresch
Copy link
Author

And I did this:

  • AUDIO_OS_DEVICES.defineProperty('pDevices', ArrayType(ref.refType(AUDIO_OS_DEVICES)));

@TooTallNate
Copy link
Owner

Sorry, I misread the struct. I didn't realize that there are 2 different ones. Try it like this:

const AUDIO_OS_DEVICES = StructType({
    nNumberDevices: ref.types.int,
    pDevices: RefArray(AUDIO_OS_DEVICE),
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants