Structuring BinData Records in Ruby

浪子不回头ぞ 提交于 2019-12-11 05:56:27

问题


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?


回答1:


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.



来源:https://stackoverflow.com/questions/37684026/structuring-bindata-records-in-ruby

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!