1

I'm not sure choices is exactly what I need here, but I'll explain what I'm trying to do. I have the following BinData structure that works fine.

class Property < BinData::Record
    endian :little

    int32  :name_len
    string :name, :read_length => :name_len
    int32  :type_len
    string :type, :read_length => :type_len
    int64  :data_len
end

However, after I get the :data_len, I need to get the actual data. How I handle what comes next depends on the value of the :type string.

  1. if type is "IntProperty" then the next four bytes (int32) is an int.
  2. if type is "FloatProperty" then the next four bytes (float_32) is a float.
  3. if type is "StringProperty" then the next four bytes (int32) is an int (len of string) and the next (len * 8) are the string itself.
  4. if type is "ArrayProperty" then the next four bytes (int32) is an int (len of array), then next however many bytes is len of array many Property objects (to store in an array).

Can anyone possibly help me figure out how to navigate these paths and set the BinData::Record up properly?

Fianite
  • 319
  • 2
  • 9

1 Answers1

0

Based on the Choice syntax section of the wiki (and some of the tests), I think it would look something like this (caveat emptor: I don't have access to your data so I really have no idea if this works):

class Property < BinData::Record
  endian :little

  int32  :name_len
  string :name, read_length: :name_len

  int32 :type_len
  string :type, read_length: :type_len

  int64 :data_len    
  choice :data, selection: :type do
    # if type is "IntProperty" then the next four bytes (int32) is an int.
    int32 "IntProperty"

    # if type is "FloatProperty" then the next four bytes (float_32) is a float.
    float "FloatProperty"

    # if type is "StringProperty" then the next four bytes (int32) is an int (len
    # of string) and the next (len * 8) are the string itself.
    struct "StringProperty" do
      int32 :len
      string :data, read_length: :len
    end

    # if type is "ArrayProperty" then the next four bytes (int32) is an int (len
    # of array), then next however many bytes is len of array many Property objects
    # (to store in an array).
    struct "ArrayProperty" do
      int32 :num_properties
      array :properties, type: :property, initial_length: :num_items
    end
  end
end

I think it would be beneficial, however, to split this up into classes. We have int32/string pairs in a few places, so let's abstract those out into their own class:

class StringRecord < BinData::Record
  endian :little

  int32 :len, value: -> { data.length }
  string :data, read_length: :len
end

If you want, you could also make that a Primitive.

And one the "PropertyArray" type:

class ArrayOfProperties < BinData::Record
  endian :little

  int32 :num_properties, value: -> { properties.size }
  array :properties, type: :property, initial_length: :num_items
end

Now our Property class looks a lot cleaner:

class Property < BinData::Record
  endian :little

  string_record :name
  string_record :type

  int64 :data_len    
  choice :data, selection: :type do
    int32 "IntProperty"
    float "FloatProperty"
    string_record "StringProperty"
    array_of_properties "ArrayProperty"
  end
end

I've specified :value options for the length fields, but if you're using this for reading and not writing you could skip them. I'm not quite sure how to write the :value option for :data_len; perhaps something like value: -> { data.num_bytes }.

Again, I have no idea if any of the above will work, but hopefully it'll help.

Jordan Running
  • 102,619
  • 17
  • 182
  • 182
  • Thanks so much Jordan, that's just what I needed! [If you do have a minute, I did ask a much smaller question here.](http://stackoverflow.com/questions/37692554/skipping-bytes-when-reading-with-bindata) Last one, promise! – Fianite Jun 08 '16 at 03:29