File: skip.rb

package info (click to toggle)
ruby-bindata 2.4.14-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 600 kB
  • sloc: ruby: 8,566; makefile: 4
file content (133 lines) | stat: -rw-r--r-- 3,652 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
require "bindata/base_primitive"

module BinData
  # Skip will skip over bytes from the input stream.  If the stream is not
  # seekable, then the bytes are consumed and discarded.
  #
  # When writing, skip will write the appropriate number of zero bytes.
  #
  #   require 'bindata'
  #
  #   class A < BinData::Record
  #     skip length: 5
  #     string :a, read_length: 5
  #   end
  #
  #   obj = A.read("abcdefghij")
  #   obj.a #=> "fghij"
  #
  #
  #   class B < BinData::Record
  #     skip until_valid: [:string, {read_length: 2, assert: "ef"} ]
  #     string :b, read_length: 5
  #   end
  #
  #   obj = B.read("abcdefghij")
  #   obj.b #=> "efghi"
  #
  #
  # == Parameters
  #
  # Skip objects accept all the params that BinData::BasePrimitive
  # does, as well as the following:
  #
  # <tt>:length</tt>::        The number of bytes to skip.
  # <tt>:to_abs_offset</tt>:: Skips to the given absolute offset.
  # <tt>:until_valid</tt>::   Skips untils a given byte pattern is matched.
  #                           This parameter contains a type that will raise
  #                           a BinData::ValidityError unless an acceptable byte
  #                           sequence is found.  The type is represented by a
  #                           Symbol, or if the type is to have params #
  #                           passed to it, then it should be provided as #
  #                           <tt>[type_symbol, hash_params]</tt>.
  #
  class Skip < BinData::BasePrimitive
    arg_processor :skip

    optional_parameters :length, :to_abs_offset, :until_valid
    mutually_exclusive_parameters :length, :to_abs_offset, :until_valid

    def initialize_shared_instance
      extend SkipLengthPlugin      if has_parameter?(:length)
      extend SkipToAbsOffsetPlugin if has_parameter?(:to_abs_offset)
      extend SkipUntilValidPlugin  if has_parameter?(:until_valid)
      super
    end

    #---------------
    private

    def value_to_binary_string(val)
      len = skip_length
      if len < 0
        raise ValidityError, "#{debug_name} attempted to seek backwards by #{len.abs} bytes"
      end

      "\000" * skip_length
    end

    def read_and_return_value(io)
      len = skip_length
      if len < 0
        raise ValidityError, "#{debug_name} attempted to seek backwards by #{len.abs} bytes"
      end

      io.seekbytes(len)
      ""
    end

    def sensible_default
      ""
    end
  end

  class SkipArgProcessor < BaseArgProcessor
    def sanitize_parameters!(obj_class, params)
      unless params.has_at_least_one_of?(:length, :to_abs_offset, :until_valid)
        raise ArgumentError,
          "#{obj_class} requires either :length, :to_abs_offset or :until_valid"
      end
      params.must_be_integer(:to_abs_offset, :length)
      params.sanitize_object_prototype(:until_valid)
    end
  end

  # Logic for the :length parameter
  module SkipLengthPlugin
    def skip_length
      eval_parameter(:length)
    end
  end

  # Logic for the :to_abs_offset parameter
  module SkipToAbsOffsetPlugin
    def skip_length
      eval_parameter(:to_abs_offset) - abs_offset
    end
  end

  # Logic for the :until_valid parameter
  module SkipUntilValidPlugin
    def skip_length
      # no skipping when writing
      0
    end

    def read_and_return_value(io)
      prototype = get_parameter(:until_valid)
      validator = prototype.instantiate(nil, self)

      valid = false
      until valid
        begin
          io.with_readahead do
            validator.read(io)
            valid = true
          end
        rescue ValidityError
          io.readbytes(1)
        end
      end
    end
  end
end