问题
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.
- if type is "IntProperty" then the next four bytes (int32) is an int.
- if type is "FloatProperty" then the next four bytes (float_32) is a float.
- 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.
- 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