Class: StructFx

Inherits:
Object
  • Object
show all
Defined in:
lib/struct-fx.rb

Overview

StructFx builder and analyzer.

Defined Under Namespace

Classes: Compiled

Constant Summary collapse

TYPES =

Holds types metainformations map.

Frozen << {
    :int8 => ["c", 1],
    :int16 => ["s", 2],
    :int32 => ["l", 4],
    :int64 => ["q", 8],
    :uint8 => ["C", 1],
    :uint16 => ["S", 2],
    :uint32 => ["L", 4],
    :uint64 => ["Q", 8],
    :float => ["f", 4],
    :double => ["d", 8],
    :char => ["a", 1],
    :string => ["Z", 1],
    :byte => ["C", 1],
    :skip => ["a", 1],
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(data = nil, &block) ⇒ StructFx

Constructor.

Parameters:

  • data (String) (defaults to: nil)

    raw string for unpack

  • block (Proc)

    block with declaration

See Also:



83
84
85
86
87
88
89
90
# File 'lib/struct-fx.rb', line 83

def initialize(data = nil, &block)
    @stack = [ ]
    self.declare(&block)
    
    if not data.nil?
        self << data
    end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Integer

Handles missing methods as declarations.

Parameters:

  • name (Symbol)

    data type of the entry

  • args (Array)

    first argument is expected to be name

  • block (Proc)

    block which returns length of the entry in bits

Returns:

  • (Integer)

    new length of the packed data

See Also:



137
138
139
140
141
142
143
# File 'lib/struct-fx.rb', line 137

def method_missing(name, *args, &block)
    if self.class::TYPES.include? name
        self.add(args.shift, name, nil, block)
    else
        raise Exception::new("Invalid type/method specified: '" << name.to_s << "'")
    end
end

Instance Attribute Details

#rawString (readonly)

Holds raw data.

Returns:

  • (String)

    raw (original) string data



37
38
39
# File 'lib/struct-fx.rb', line 37

def raw
  @raw
end

Instance Method Details

#<<(value) ⇒ Object

Fills by input data.

Parameters:

  • value (Integer)

    raw integer data for unpack



204
205
206
207
208
209
210
211
# File 'lib/struct-fx.rb', line 204

def <<(value)
    if value.bytesize < self.compiled.length
        raise Exception::new("Data are shorter than declaration.")
    end
    
    @raw = value.to_s
    @data = nil
end

#add(name, type, callback = nil, block = nil) ⇒ Object

Adds declaration.

Parameters:

  • name (Symbol)

    name of the entry

  • type (Symbol)

    type of the entry

  • callback (Proc) (defaults to: nil)

    postprocessing callback

  • block (Proc) (defaults to: nil)

    length declaration block



222
223
224
225
226
# File 'lib/struct-fx.rb', line 222

def add(name, type, callback = nil, block = nil)
    @stack << [name, type, callback, block]
    @struct = nil
    @compiled = nil
end

#byte(name, &block) ⇒ Object

Adds byte BitPacker declaration.

Parameters:

  • name (Symbol)

    name of the entry

  • block (Proc)

    declaration

See Also:



182
183
184
185
186
187
188
# File 'lib/struct-fx.rb', line 182

def byte(name, &block)
    callback = Proc::new do |value|
        BitPacker::new(value, &block)
    end
    
    self.add(name, :byte, callback)
end

#bytesizeInteger Also known as: length

Returns total length of the struct in bytes.

Returns:

  • (Integer)

    total length of the declaration

Since:

  • 0.1.1



269
270
271
# File 'lib/struct-fx.rb', line 269

def bytesize
    self.compiled.length
end

#compiledClass

Returns compiled form.

Returns:

  • (Class)

    compiled form struct



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/struct-fx.rb', line 150

def compiled
    if @compiled.nil?
        @compiled = self.class::Compiled::new([], [], "", "", [], 0)
        types = self.class::TYPES
        
        @stack.each do |name, type, args, block|
            type_meta = types[type]
            length = block.nil? ? 1 : block.call()
            pack_char = type_meta[0] + length.to_s
            total_length = type_meta[1] * length
            
            @compiled.enabled << (not name.nil?)
            @compiled.processors << args
            @compiled.packing << pack_char
            @compiled.unpacking << pack_char
            @compiled.lengths << total_length
            @compiled.length += total_length
        end
        
    end
    
    return @compiled
end

#dataClass

Returns structure analyze.

Returns:

  • (Class)

    struct with the packed data



233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/struct-fx.rb', line 233

def data
    if @data.nil?
        values = [ ]
        compiled = self.compiled
        extracted = @raw.unpack(compiled.unpacking)

        compiled.enabled.each_index do |i|
        
            # Skips skipped items
            if not compiled.enabled[i]
                next
            end
            
            # Calls postprocessing if required
            callback = compiled.processors[i]
            if callback.nil?
                values << extracted[i]
            else
                values << callback.call(extracted[i])
            end
            
        end

        @data = __struct::new(*values)
    end
    
    return @data
end

#declare(&block) ⇒ Object

Receives declaration.

Adds declaration of bit array items. Can be call multiple times. New delcarations are joind to end of the array.

Examples:

struct = StructFx::new("abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMOPQRSTUVWXYZ") do
    int8 :n_int8
    int16 :n_int16
    int32 :n_int32
    int64 :n_int64
    uint8 :n_uint8
    uint16 :n_uint16
    uint32 :n_uint32
    uint64 :n_uint64
    skip {2}
    float :n_float
    double :n_double
    char :n_char
    string (:n_string) {3}
    byte :n_flags do  # byte is usually length 8 bits
        number (:n_number) {7}
        boolean :n_boolean
    end
end

Parameters:

  • block (Proc)

    block with declaration

See Also:



123
124
125
# File 'lib/struct-fx.rb', line 123

def declare(&block)
    self.instance_eval(&block)
end

#skip(&block) ⇒ Object

Adds skipping declaration.

Parameters:

  • block (Proc)

    with length



195
196
197
# File 'lib/struct-fx.rb', line 195

def skip(&block)
    self.add(nil, :skip, nil, block)
end

#to_sString

Converts to string.

Returns:

  • (String)

    resultant string according to current data state



280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'lib/struct-fx.rb', line 280

def to_s
    entries = self.data.entries
    compiled = self.compiled
    
    values = [ ]
    length = 0
    delta = 0
    
    compiled.enabled.each_index do |i|
    
        # Pastes skipped data from RAW
        if not compiled.enabled[i]
            from = length
            to = (length + compiled.lengths[i])
            values << @raw[from...to]
            delta += 1
        
        # In otherwise, take value from analyzed data
        else
            values << entries[i - delta]    # current item minus count of non-entry (skipped) items
        end
    
        length += self.compiled.lengths[i]
    end

    values.pack(self.compiled.packing)
end