TooTallNate / ref

Turn Buffer instances into "pointers"
http://tootallnate.github.com/ref
453 stars 141 forks source link

Foreign allocated array #1

Closed tjfontaine closed 12 years ago

tjfontaine commented 12 years ago

In my libvlc bindings I need to call a function which allocates an array, I have no idea how large the array will be until after the function returns and I'm responsible for freeing the data (but we can tackle that in another ticket)

Here's the code, it works for the first item, the second item's address is out of whack and therefore reading in the struct predictably segfaults with exec_bad_access

var i, ptr;
var results = [];

var info = new Buffer(ref.sizeof.pointer);
var tracks = lib.libvlc_media_get_tracks_info(self.instance, info);
info = info.reinterpret(TrackInfo.size * tracks);

for (i = 0; i < tracks; i++) {
  ptr = info.readPointer(i * TrackInfo.size, TrackInfo.size)
  ptr.type = TrackInfo;
  console.log('track', i, 'address', '0x' + ptr.address().toString(16));
  results.push(ptr.deref());
}

return results;

output

track 0 address 0x7fe390c06fc0
track 1 address 0x6f894e000008000
track info: i_codec 1630826605
track info: i_id 0
track info: i_type 0
track info: i_profile -1
track info: i_level -1
Segmentation fault: 11

For comparison here's C code of the same procedure, and it's output when processing the same file

#include <stdio.h>
#include <vlc/vlc.h>

int main (int argc, char **argv) {
  libvlc_instance_t *instance;
  libvlc_media_t *media;
  libvlc_media_track_info_t *info;
  int tracks, i;

  instance = libvlc_new(0, NULL);
  media = libvlc_media_new_path(instance, "test.m4v");
  libvlc_media_parse(media);

  tracks = libvlc_media_get_tracks_info(media, &info);

  for (i = 0; i < tracks; i++) {
    printf("%p\n", &info[i]);
    printf("i_codec: %d\n", info[i].i_codec);
    printf("i_id: %d\n", info[i].i_id);
    printf("i_type: %d\n", info[i].i_type);
    printf("i_profile: %d\n", info[i].i_profile);
    printf("i_level: %d\n", info[i].i_level);
  }

  return 0;
}
0x7f9adbd0c660
i_codec: 1630826605
i_id: 0
i_type: 0
i_profile: -1
i_level: -1
0x7f9adbd0c67c
i_codec: 875967080
i_id: 1
i_type: 1
i_profile: -1
i_level: -1

You can see that the first struct for js matches the first struct returned by the c code, but instead of a reasonable 0x1C offset the second is positioned way out in the nether regions.

tjfontaine commented 12 years ago

I'll double post this, I added some fprintf's since I couldn't convince gdb to let me break into ReadPointer and saw

ReadPointer 0x101816b00 + 0 -- 0x101816b00
Reinterpret_Cast 0x100a1a070
track 0 address 0x100a1a070
ReadPointer 0x101816b00 + 28 -- 0x101816b1c
Reinterpret_Cast 0xb7150000000001
track 1 address 0xb7150000000000
tjfontaine commented 12 years ago

The proper way to do this is

      var info = new Buffer(ref.sizeof.pointer);
      var tracks = lib.libvlc_media_get_tracks_info(self.instance, info);
      info = info.readPointer(0, TrackInfo.size * tracks);

      for (i = 0; i < tracks; i++) {
        ptr = ref.get(info, i * TrackInfo.size, TrackInfo);
      }