kaitai-struct 0.8 → 0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +1 -1
- data/LICENSE +21 -0
- data/README.md +41 -64
- data/kaitai-struct.gemspec +31 -31
- data/lib/kaitai/struct/struct.rb +621 -502
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3eb5375d9ddf8772844c25d3319de5be17eaafc71d5da835191e5e61ee84fec7
|
4
|
+
data.tar.gz: 82d10de0ba0c3bb004286df6a38febe40fb9b3223a7e24a6386c6ca854386b7e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db13f61f4b06680cb5b1e2b156fd466aa1c80a3a1e2c0ddad9a181ecdd4e66c9099c70de4cb05e25d6279ea007263db7e591d2ae0672fdba5431b93aa2b1e955
|
7
|
+
data.tar.gz: 3688b04940d1b4e8fa06cc3434b5ddc441387f6a62af5811cfc66cbfbf444565393000022df3b8ea997e6034968ff3a8067d258cc5e000969880986ea37a2197
|
data/.gitignore
CHANGED
@@ -1 +1 @@
|
|
1
|
-
*.gem
|
1
|
+
*.gem
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2015-2019 Kaitai Project
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
CHANGED
@@ -1,64 +1,41 @@
|
|
1
|
-
# Kaitai Struct: runtime library for Ruby
|
2
|
-
|
3
|
-
This library implements Kaitai Struct API for Ruby.
|
4
|
-
|
5
|
-
Kaitai Struct is a declarative language used for describe various binary
|
6
|
-
data structures, laid out in files or in memory: i.e. binary file
|
7
|
-
formats, network stream packet formats, etc.
|
8
|
-
|
9
|
-
Further reading:
|
10
|
-
|
11
|
-
* [About Kaitai Struct](http://kaitai.io/)
|
12
|
-
* [About API implemented in this library](http://doc.kaitai.io/stream_api.html)
|
13
|
-
|
14
|
-
## Installing
|
15
|
-
|
16
|
-
### Using `Gemfile`
|
17
|
-
|
18
|
-
If your project uses Bundler, just include the line
|
19
|
-
|
20
|
-
```
|
21
|
-
gem 'kaitai-struct'
|
22
|
-
```
|
23
|
-
|
24
|
-
in your project's `Gemfile`.
|
25
|
-
|
26
|
-
### Using `gem install`
|
27
|
-
|
28
|
-
If you have a RubyGems package manager installed, you can use command
|
29
|
-
|
30
|
-
```
|
31
|
-
gem install kaitai-struct
|
32
|
-
```
|
33
|
-
|
34
|
-
to install this runtime library.
|
35
|
-
|
36
|
-
### Manually
|
37
|
-
|
38
|
-
This library is intentionally kept as very simple, one-file `.rb`
|
39
|
-
file. One can just copy it to your project from this
|
40
|
-
repository. Usually you won't `require` it directly, it will be loaded
|
41
|
-
by Ruby source code generate by Kaitai Struct compiler.
|
42
|
-
|
43
|
-
## Licensing
|
44
|
-
|
45
|
-
Copyright 2015-2017 Kaitai Project: MIT license
|
46
|
-
|
47
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
48
|
-
a copy of this software and associated documentation files (the
|
49
|
-
"Software"), to deal in the Software without restriction, including
|
50
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
51
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
52
|
-
permit persons to whom the Software is furnished to do so, subject to
|
53
|
-
the following conditions:
|
54
|
-
|
55
|
-
The above copyright notice and this permission notice shall be
|
56
|
-
included in all copies or substantial portions of the Software.
|
57
|
-
|
58
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
59
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
60
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
61
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
62
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
63
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
64
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
1
|
+
# Kaitai Struct: runtime library for Ruby
|
2
|
+
|
3
|
+
This library implements Kaitai Struct API for Ruby.
|
4
|
+
|
5
|
+
Kaitai Struct is a declarative language used for describe various binary
|
6
|
+
data structures, laid out in files or in memory: i.e. binary file
|
7
|
+
formats, network stream packet formats, etc.
|
8
|
+
|
9
|
+
Further reading:
|
10
|
+
|
11
|
+
* [About Kaitai Struct](http://kaitai.io/)
|
12
|
+
* [About API implemented in this library](http://doc.kaitai.io/stream_api.html)
|
13
|
+
|
14
|
+
## Installing
|
15
|
+
|
16
|
+
### Using `Gemfile`
|
17
|
+
|
18
|
+
If your project uses Bundler, just include the line
|
19
|
+
|
20
|
+
```
|
21
|
+
gem 'kaitai-struct'
|
22
|
+
```
|
23
|
+
|
24
|
+
in your project's `Gemfile`.
|
25
|
+
|
26
|
+
### Using `gem install`
|
27
|
+
|
28
|
+
If you have a RubyGems package manager installed, you can use command
|
29
|
+
|
30
|
+
```
|
31
|
+
gem install kaitai-struct
|
32
|
+
```
|
33
|
+
|
34
|
+
to install this runtime library.
|
35
|
+
|
36
|
+
### Manually
|
37
|
+
|
38
|
+
This library is intentionally kept as very simple, one-file `.rb`
|
39
|
+
file. One can just copy it to your project from this
|
40
|
+
repository. Usually you won't `require` it directly, it will be loaded
|
41
|
+
by Ruby source code generate by Kaitai Struct compiler.
|
data/kaitai-struct.gemspec
CHANGED
@@ -1,31 +1,31 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
|
3
|
-
require File.expand_path("../lib/kaitai/struct/struct", __FILE__)
|
4
|
-
require 'date'
|
5
|
-
|
6
|
-
Gem::Specification.new { |s|
|
7
|
-
s.name = 'kaitai-struct'
|
8
|
-
s.version = Kaitai::Struct::VERSION
|
9
|
-
s.date = Date.today.to_s
|
10
|
-
|
11
|
-
s.authors = ['Mikhail Yakshin']
|
12
|
-
s.email = 'greycat@kaitai.io'
|
13
|
-
|
14
|
-
s.homepage = 'http://kaitai.io'
|
15
|
-
s.summary = 'Kaitai Struct: runtime library for Ruby'
|
16
|
-
s.license = 'MIT'
|
17
|
-
s.description = <<-EOF
|
18
|
-
Kaitai Struct is a declarative language used for describe various binary data structures, laid out in files or in memory: i.e. binary file formats, network stream packet formats, etc.
|
19
|
-
|
20
|
-
The main idea is that a particular format is described in Kaitai Struct language (.ksy file) and then can be compiled with ksc into source files in one of the supported programming languages. These modules will include a generated code for a parser that can read described data structure from a file / stream and give access to it in a nice, easy-to-comprehend API.
|
21
|
-
|
22
|
-
This package provides small runtime library used by code generated by Kaitai Struct compiler.
|
23
|
-
EOF
|
24
|
-
|
25
|
-
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
26
|
-
s.require_paths = ['lib']
|
27
|
-
|
28
|
-
s.files = `git ls-files`.split("\n")
|
29
|
-
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
30
|
-
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
31
|
-
}
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require File.expand_path("../lib/kaitai/struct/struct", __FILE__)
|
4
|
+
require 'date'
|
5
|
+
|
6
|
+
Gem::Specification.new { |s|
|
7
|
+
s.name = 'kaitai-struct'
|
8
|
+
s.version = Kaitai::Struct::VERSION
|
9
|
+
s.date = Date.today.to_s
|
10
|
+
|
11
|
+
s.authors = ['Mikhail Yakshin']
|
12
|
+
s.email = 'greycat@kaitai.io'
|
13
|
+
|
14
|
+
s.homepage = 'http://kaitai.io'
|
15
|
+
s.summary = 'Kaitai Struct: runtime library for Ruby'
|
16
|
+
s.license = 'MIT'
|
17
|
+
s.description = <<-EOF
|
18
|
+
Kaitai Struct is a declarative language used for describe various binary data structures, laid out in files or in memory: i.e. binary file formats, network stream packet formats, etc.
|
19
|
+
|
20
|
+
The main idea is that a particular format is described in Kaitai Struct language (.ksy file) and then can be compiled with ksc into source files in one of the supported programming languages. These modules will include a generated code for a parser that can read described data structure from a file / stream and give access to it in a nice, easy-to-comprehend API.
|
21
|
+
|
22
|
+
This package provides small runtime library used by code generated by Kaitai Struct compiler.
|
23
|
+
EOF
|
24
|
+
|
25
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
26
|
+
s.require_paths = ['lib']
|
27
|
+
|
28
|
+
s.files = `git ls-files`.split("\n")
|
29
|
+
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
30
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
31
|
+
}
|
data/lib/kaitai/struct/struct.rb
CHANGED
@@ -1,502 +1,621 @@
|
|
1
|
-
require 'stringio'
|
2
|
-
|
3
|
-
module Kaitai
|
4
|
-
module Struct
|
5
|
-
|
6
|
-
VERSION = '0.
|
7
|
-
|
8
|
-
##
|
9
|
-
# Common base class for all structured generated by Kaitai Struct.
|
10
|
-
# Stores stream object that this object was parsed from in {#_io},
|
11
|
-
# stores reference to parent structure in {#_parent} and root
|
12
|
-
# structure in {#_root} and provides a few helper methods.
|
13
|
-
class Struct
|
14
|
-
|
15
|
-
def initialize(_io, _parent = nil, _root = self)
|
16
|
-
@_io = _io
|
17
|
-
@_parent = _parent
|
18
|
-
@_root = _root
|
19
|
-
end
|
20
|
-
|
21
|
-
##
|
22
|
-
# Factory method to instantiate a Kaitai Struct-powered structure,
|
23
|
-
# parsing it from a local file with a given filename.
|
24
|
-
# @param filename [String] local file to parse
|
25
|
-
def self.from_file(filename)
|
26
|
-
self.new(Stream.open(filename))
|
27
|
-
end
|
28
|
-
|
29
|
-
##
|
30
|
-
# Implementation of {Object#inspect} to aid debugging (at the very
|
31
|
-
# least, to aid exception raising) for KS-based classes. This one
|
32
|
-
# uses a bit terser syntax than Ruby's default one, purposely skips
|
33
|
-
# any internal fields (i.e. starting with `_`, such as `_io`,
|
34
|
-
# `_parent` and `_root`) to reduce confusion, and does no
|
35
|
-
# recursivity tracking (as proper general-purpose `inspect`
|
36
|
-
# implementation should do) because there are no endless recursion
|
37
|
-
# in KS-based classes by design (except for already mentioned
|
38
|
-
# internal navigation variables).
|
39
|
-
def inspect
|
40
|
-
vars = []
|
41
|
-
instance_variables.each { |nsym|
|
42
|
-
nstr = nsym.to_s
|
43
|
-
|
44
|
-
# skip all internal variables
|
45
|
-
next if nstr[0..1] == '@_'
|
46
|
-
|
47
|
-
# strip mandatory `@` at the beginning of the name for brevity
|
48
|
-
nstr = nstr[1..-1]
|
49
|
-
|
50
|
-
nvalue = instance_variable_get(nsym).inspect
|
51
|
-
|
52
|
-
vars << "#{nstr}=#{nvalue}"
|
53
|
-
}
|
54
|
-
|
55
|
-
"#{self.class}(#{vars.join(' ')})"
|
56
|
-
end
|
57
|
-
|
58
|
-
attr_reader :_io, :_parent, :_root
|
59
|
-
end
|
60
|
-
|
61
|
-
##
|
62
|
-
# Kaitai::Struct::Stream is an implementation of
|
63
|
-
# {https://github.com/kaitai-io/kaitai_struct/wiki/Kaitai-Struct-stream-API
|
64
|
-
# Kaitai Struct stream API} for Ruby. It's implemented as a wrapper
|
65
|
-
# for generic IO objects.
|
66
|
-
#
|
67
|
-
# It provides a wide variety of simple methods to read (parse) binary
|
68
|
-
# representations of primitive types, such as integer and floating
|
69
|
-
# point numbers, byte arrays and strings, and also provides stream
|
70
|
-
# positioning / navigation methods with unified cross-language and
|
71
|
-
# cross-toolkit semantics.
|
72
|
-
#
|
73
|
-
# Typically, end users won't access Kaitai Stream class manually, but
|
74
|
-
# would describe a binary structure format using .ksy language and
|
75
|
-
# then would use Kaitai Struct compiler to generate source code in
|
76
|
-
# desired target language. That code, in turn, would use this class
|
77
|
-
# and API to do the actual parsing job.
|
78
|
-
class Stream
|
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
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
#
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
#
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
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
|
-
#
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
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
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
mask
|
312
|
-
# derive reading result
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
@
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
#
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
#
|
377
|
-
# @
|
378
|
-
def
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
#
|
405
|
-
|
406
|
-
#
|
407
|
-
|
408
|
-
|
409
|
-
#
|
410
|
-
#
|
411
|
-
#
|
412
|
-
#
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
def self.
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module Kaitai
|
4
|
+
module Struct
|
5
|
+
|
6
|
+
VERSION = '0.9'
|
7
|
+
|
8
|
+
##
|
9
|
+
# Common base class for all structured generated by Kaitai Struct.
|
10
|
+
# Stores stream object that this object was parsed from in {#_io},
|
11
|
+
# stores reference to parent structure in {#_parent} and root
|
12
|
+
# structure in {#_root} and provides a few helper methods.
|
13
|
+
class Struct
|
14
|
+
|
15
|
+
def initialize(_io, _parent = nil, _root = self)
|
16
|
+
@_io = _io
|
17
|
+
@_parent = _parent
|
18
|
+
@_root = _root
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Factory method to instantiate a Kaitai Struct-powered structure,
|
23
|
+
# parsing it from a local file with a given filename.
|
24
|
+
# @param filename [String] local file to parse
|
25
|
+
def self.from_file(filename)
|
26
|
+
self.new(Stream.open(filename))
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Implementation of {Object#inspect} to aid debugging (at the very
|
31
|
+
# least, to aid exception raising) for KS-based classes. This one
|
32
|
+
# uses a bit terser syntax than Ruby's default one, purposely skips
|
33
|
+
# any internal fields (i.e. starting with `_`, such as `_io`,
|
34
|
+
# `_parent` and `_root`) to reduce confusion, and does no
|
35
|
+
# recursivity tracking (as proper general-purpose `inspect`
|
36
|
+
# implementation should do) because there are no endless recursion
|
37
|
+
# in KS-based classes by design (except for already mentioned
|
38
|
+
# internal navigation variables).
|
39
|
+
def inspect
|
40
|
+
vars = []
|
41
|
+
instance_variables.each { |nsym|
|
42
|
+
nstr = nsym.to_s
|
43
|
+
|
44
|
+
# skip all internal variables
|
45
|
+
next if nstr[0..1] == '@_'
|
46
|
+
|
47
|
+
# strip mandatory `@` at the beginning of the name for brevity
|
48
|
+
nstr = nstr[1..-1]
|
49
|
+
|
50
|
+
nvalue = instance_variable_get(nsym).inspect
|
51
|
+
|
52
|
+
vars << "#{nstr}=#{nvalue}"
|
53
|
+
}
|
54
|
+
|
55
|
+
"#{self.class}(#{vars.join(' ')})"
|
56
|
+
end
|
57
|
+
|
58
|
+
attr_reader :_io, :_parent, :_root
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# Kaitai::Struct::Stream is an implementation of
|
63
|
+
# {https://github.com/kaitai-io/kaitai_struct/wiki/Kaitai-Struct-stream-API
|
64
|
+
# Kaitai Struct stream API} for Ruby. It's implemented as a wrapper
|
65
|
+
# for generic IO objects.
|
66
|
+
#
|
67
|
+
# It provides a wide variety of simple methods to read (parse) binary
|
68
|
+
# representations of primitive types, such as integer and floating
|
69
|
+
# point numbers, byte arrays and strings, and also provides stream
|
70
|
+
# positioning / navigation methods with unified cross-language and
|
71
|
+
# cross-toolkit semantics.
|
72
|
+
#
|
73
|
+
# Typically, end users won't access Kaitai Stream class manually, but
|
74
|
+
# would describe a binary structure format using .ksy language and
|
75
|
+
# then would use Kaitai Struct compiler to generate source code in
|
76
|
+
# desired target language. That code, in turn, would use this class
|
77
|
+
# and API to do the actual parsing job.
|
78
|
+
class Stream
|
79
|
+
##
|
80
|
+
# Unused since Kaitai Struct Compiler v0.9+ - compatibility with
|
81
|
+
# older versions.
|
82
|
+
#
|
83
|
+
# Exception class for an error that occurs when some fixed content
|
84
|
+
# was expected to appear, but actual data read was different.
|
85
|
+
class UnexpectedDataError < Exception
|
86
|
+
def initialize(actual, expected)
|
87
|
+
super("Unexpected fixed contents: got #{Stream.format_hex(actual)}, was waiting for #{Stream.format_hex(expected)}")
|
88
|
+
@actual = actual
|
89
|
+
@expected = expected
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
##
|
95
|
+
# Constructs new Kaitai Stream object.
|
96
|
+
# @param arg [String, IO] if String, it will be used as byte array to read data from;
|
97
|
+
# if IO, if will be used literally as source of data
|
98
|
+
def initialize(arg)
|
99
|
+
if arg.is_a?(String)
|
100
|
+
@_io = StringIO.new(arg)
|
101
|
+
elsif arg.is_a?(IO)
|
102
|
+
@_io = arg
|
103
|
+
else
|
104
|
+
raise TypeError.new('can be initialized with IO or String only')
|
105
|
+
end
|
106
|
+
align_to_byte
|
107
|
+
end
|
108
|
+
|
109
|
+
##
|
110
|
+
# Convenience method to create a Kaitai Stream object, opening a
|
111
|
+
# local file with a given filename.
|
112
|
+
# @param filename [String] local file to open
|
113
|
+
def self.open(filename)
|
114
|
+
self.new(File.open(filename, 'rb:ASCII-8BIT'))
|
115
|
+
end
|
116
|
+
|
117
|
+
##
|
118
|
+
# Closes underlying IO object.
|
119
|
+
def close
|
120
|
+
@_io.close
|
121
|
+
end
|
122
|
+
|
123
|
+
# Test endianness of the platform
|
124
|
+
@@big_endian = [0x0102].pack('s') == [0x0102].pack('n')
|
125
|
+
|
126
|
+
# @!group Stream positioning
|
127
|
+
|
128
|
+
##
|
129
|
+
# Check if stream pointer is at the end of stream.
|
130
|
+
# @return [true, false] true if we are located at the end of the stream
|
131
|
+
def eof?; @_io.eof? and @bits_left == 0; end
|
132
|
+
|
133
|
+
##
|
134
|
+
# Set stream pointer to designated position.
|
135
|
+
# @param x [Fixnum] new position (offset in bytes from the beginning of the stream)
|
136
|
+
def seek(x); @_io.seek(x); end
|
137
|
+
|
138
|
+
##
|
139
|
+
# Get current position of a stream pointer.
|
140
|
+
# @return [Fixnum] pointer position, number of bytes from the beginning of the stream
|
141
|
+
def pos; @_io.pos; end
|
142
|
+
|
143
|
+
##
|
144
|
+
# Get total size of the stream in bytes.
|
145
|
+
# @return [Fixnum] size of the stream in bytes
|
146
|
+
def size; @_io.size; end
|
147
|
+
|
148
|
+
# @!endgroup
|
149
|
+
|
150
|
+
# @!group Integer numbers
|
151
|
+
|
152
|
+
# ------------------------------------------------------------------------
|
153
|
+
# Signed
|
154
|
+
# ------------------------------------------------------------------------
|
155
|
+
|
156
|
+
def read_s1
|
157
|
+
read_bytes(1).unpack('c')[0]
|
158
|
+
end
|
159
|
+
|
160
|
+
# ........................................................................
|
161
|
+
# Big-endian
|
162
|
+
# ........................................................................
|
163
|
+
|
164
|
+
def read_s2be
|
165
|
+
to_signed(read_u2be, SIGN_MASK_16)
|
166
|
+
end
|
167
|
+
|
168
|
+
def read_s4be
|
169
|
+
to_signed(read_u4be, SIGN_MASK_32)
|
170
|
+
end
|
171
|
+
|
172
|
+
if @@big_endian
|
173
|
+
def read_s8be
|
174
|
+
read_bytes(8).unpack('q')[0]
|
175
|
+
end
|
176
|
+
else
|
177
|
+
def read_s8be
|
178
|
+
to_signed(read_u8be, SIGN_MASK_64)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# ........................................................................
|
183
|
+
# Little-endian
|
184
|
+
# ........................................................................
|
185
|
+
|
186
|
+
def read_s2le
|
187
|
+
to_signed(read_u2le, SIGN_MASK_16)
|
188
|
+
end
|
189
|
+
|
190
|
+
def read_s4le
|
191
|
+
to_signed(read_u4le, SIGN_MASK_32)
|
192
|
+
end
|
193
|
+
|
194
|
+
unless @@big_endian
|
195
|
+
def read_s8le
|
196
|
+
read_bytes(8).unpack('q')[0]
|
197
|
+
end
|
198
|
+
else
|
199
|
+
def read_s8le
|
200
|
+
to_signed(read_u8le, SIGN_MASK_64)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# ------------------------------------------------------------------------
|
205
|
+
# Unsigned
|
206
|
+
# ------------------------------------------------------------------------
|
207
|
+
|
208
|
+
def read_u1
|
209
|
+
read_bytes(1).unpack('C')[0]
|
210
|
+
end
|
211
|
+
|
212
|
+
# ........................................................................
|
213
|
+
# Big-endian
|
214
|
+
# ........................................................................
|
215
|
+
|
216
|
+
def read_u2be
|
217
|
+
read_bytes(2).unpack('n')[0]
|
218
|
+
end
|
219
|
+
|
220
|
+
def read_u4be
|
221
|
+
read_bytes(4).unpack('N')[0]
|
222
|
+
end
|
223
|
+
|
224
|
+
if @@big_endian
|
225
|
+
def read_u8be
|
226
|
+
read_bytes(8).unpack('Q')[0]
|
227
|
+
end
|
228
|
+
else
|
229
|
+
def read_u8be
|
230
|
+
a, b = read_bytes(8).unpack('NN')
|
231
|
+
(a << 32) + b
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# ........................................................................
|
236
|
+
# Little-endian
|
237
|
+
# ........................................................................
|
238
|
+
|
239
|
+
def read_u2le
|
240
|
+
read_bytes(2).unpack('v')[0]
|
241
|
+
end
|
242
|
+
|
243
|
+
def read_u4le
|
244
|
+
read_bytes(4).unpack('V')[0]
|
245
|
+
end
|
246
|
+
|
247
|
+
unless @@big_endian
|
248
|
+
def read_u8le
|
249
|
+
read_bytes(8).unpack('Q')[0]
|
250
|
+
end
|
251
|
+
else
|
252
|
+
def read_u8le
|
253
|
+
a, b = read_bytes(8).unpack('VV')
|
254
|
+
(b << 32) + a
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
# @!endgroup
|
259
|
+
|
260
|
+
# @!group Floating point numbers
|
261
|
+
|
262
|
+
# ------------------------------------------------------------------------
|
263
|
+
# Big-endian
|
264
|
+
# ------------------------------------------------------------------------
|
265
|
+
|
266
|
+
def read_f4be
|
267
|
+
read_bytes(4).unpack('g')[0]
|
268
|
+
end
|
269
|
+
|
270
|
+
def read_f8be
|
271
|
+
read_bytes(8).unpack('G')[0]
|
272
|
+
end
|
273
|
+
|
274
|
+
# ------------------------------------------------------------------------
|
275
|
+
# Little-endian
|
276
|
+
# ------------------------------------------------------------------------
|
277
|
+
|
278
|
+
def read_f4le
|
279
|
+
read_bytes(4).unpack('e')[0]
|
280
|
+
end
|
281
|
+
|
282
|
+
def read_f8le
|
283
|
+
read_bytes(8).unpack('E')[0]
|
284
|
+
end
|
285
|
+
|
286
|
+
# @!endgroup
|
287
|
+
|
288
|
+
# @!group Unaligned bit values
|
289
|
+
|
290
|
+
def align_to_byte
|
291
|
+
@bits_left = 0
|
292
|
+
@bits = 0
|
293
|
+
end
|
294
|
+
|
295
|
+
def read_bits_int_be(n)
|
296
|
+
bits_needed = n - @bits_left
|
297
|
+
if bits_needed > 0
|
298
|
+
# 1 bit => 1 byte
|
299
|
+
# 8 bits => 1 byte
|
300
|
+
# 9 bits => 2 bytes
|
301
|
+
bytes_needed = ((bits_needed - 1) / 8) + 1
|
302
|
+
buf = read_bytes(bytes_needed)
|
303
|
+
buf.each_byte { |byte|
|
304
|
+
@bits <<= 8
|
305
|
+
@bits |= byte
|
306
|
+
@bits_left += 8
|
307
|
+
}
|
308
|
+
end
|
309
|
+
|
310
|
+
# raw mask with required number of 1s, starting from lowest bit
|
311
|
+
mask = (1 << n) - 1
|
312
|
+
# shift @bits to align the highest bits with the mask & derive reading result
|
313
|
+
shift_bits = @bits_left - n
|
314
|
+
res = (@bits >> shift_bits) & mask
|
315
|
+
# clear top bits that we've just read => AND with 1s
|
316
|
+
@bits_left -= n
|
317
|
+
mask = (1 << @bits_left) - 1
|
318
|
+
@bits &= mask
|
319
|
+
|
320
|
+
res
|
321
|
+
end
|
322
|
+
|
323
|
+
# Unused since Kaitai Struct Compiler v0.9+ - compatibility with
|
324
|
+
# older versions.
|
325
|
+
def read_bits_int(n)
|
326
|
+
read_bits_int_be(n)
|
327
|
+
end
|
328
|
+
|
329
|
+
def read_bits_int_le(n)
|
330
|
+
bits_needed = n - @bits_left
|
331
|
+
if bits_needed > 0
|
332
|
+
# 1 bit => 1 byte
|
333
|
+
# 8 bits => 1 byte
|
334
|
+
# 9 bits => 2 bytes
|
335
|
+
bytes_needed = ((bits_needed - 1) / 8) + 1
|
336
|
+
buf = read_bytes(bytes_needed)
|
337
|
+
buf.each_byte { |byte|
|
338
|
+
@bits |= (byte << @bits_left)
|
339
|
+
@bits_left += 8
|
340
|
+
}
|
341
|
+
end
|
342
|
+
|
343
|
+
# raw mask with required number of 1s, starting from lowest bit
|
344
|
+
mask = (1 << n) - 1
|
345
|
+
# derive reading result
|
346
|
+
res = @bits & mask
|
347
|
+
# remove bottom bits that we've just read by shifting
|
348
|
+
@bits >>= n
|
349
|
+
@bits_left -= n
|
350
|
+
|
351
|
+
res
|
352
|
+
end
|
353
|
+
|
354
|
+
# @!endgroup
|
355
|
+
|
356
|
+
# @!group Byte arrays
|
357
|
+
|
358
|
+
##
|
359
|
+
# Reads designated number of bytes from the stream.
|
360
|
+
# @param n [Fixnum] number of bytes to read
|
361
|
+
# @return [String] read bytes as byte array
|
362
|
+
# @raise [EOFError] if there were less bytes than requested
|
363
|
+
# available in the stream
|
364
|
+
def read_bytes(n)
|
365
|
+
r = @_io.read(n)
|
366
|
+
if r
|
367
|
+
rl = r.bytesize
|
368
|
+
else
|
369
|
+
rl = 0
|
370
|
+
end
|
371
|
+
raise EOFError.new("attempted to read #{n} bytes, got only #{rl}") if rl < n
|
372
|
+
r
|
373
|
+
end
|
374
|
+
|
375
|
+
##
|
376
|
+
# Reads all the remaining bytes in a stream as byte array.
|
377
|
+
# @return [String] all remaining bytes in a stream as byte array
|
378
|
+
def read_bytes_full
|
379
|
+
@_io.read
|
380
|
+
end
|
381
|
+
|
382
|
+
def read_bytes_term(term, include_term, consume_term, eos_error)
|
383
|
+
r = ''
|
384
|
+
loop {
|
385
|
+
if @_io.eof?
|
386
|
+
if eos_error
|
387
|
+
raise EOFError.new("end of stream reached, but no terminator #{term} found")
|
388
|
+
else
|
389
|
+
return r
|
390
|
+
end
|
391
|
+
end
|
392
|
+
c = @_io.getc
|
393
|
+
if c.ord == term
|
394
|
+
r << c if include_term
|
395
|
+
@_io.seek(@_io.pos - 1) unless consume_term
|
396
|
+
return r
|
397
|
+
end
|
398
|
+
r << c
|
399
|
+
}
|
400
|
+
end
|
401
|
+
|
402
|
+
##
|
403
|
+
# Unused since Kaitai Struct Compiler v0.9+ - compatibility with
|
404
|
+
# older versions.
|
405
|
+
#
|
406
|
+
# Reads next len bytes from the stream and ensures that they match
|
407
|
+
# expected fixed byte array. If they differ, throws a
|
408
|
+
# {UnexpectedDataError} runtime exception.
|
409
|
+
# @param expected [String] contents to be expected
|
410
|
+
# @return [String] read bytes as byte array, which are guaranteed to
|
411
|
+
# equal to expected
|
412
|
+
# @raise [UnexpectedDataError]
|
413
|
+
def ensure_fixed_contents(expected)
|
414
|
+
len = expected.bytesize
|
415
|
+
actual = @_io.read(len)
|
416
|
+
raise UnexpectedDataError.new(actual, expected) if actual != expected
|
417
|
+
actual
|
418
|
+
end
|
419
|
+
|
420
|
+
def self.bytes_strip_right(bytes, pad_byte)
|
421
|
+
new_len = bytes.length
|
422
|
+
while new_len > 0 and bytes.getbyte(new_len - 1) == pad_byte
|
423
|
+
new_len -= 1
|
424
|
+
end
|
425
|
+
|
426
|
+
bytes[0, new_len]
|
427
|
+
end
|
428
|
+
|
429
|
+
def self.bytes_terminate(bytes, term, include_term)
|
430
|
+
new_len = 0
|
431
|
+
max_len = bytes.length
|
432
|
+
while bytes.getbyte(new_len) != term and new_len < max_len
|
433
|
+
new_len += 1
|
434
|
+
end
|
435
|
+
new_len += 1 if include_term and new_len < max_len
|
436
|
+
bytes[0, new_len]
|
437
|
+
end
|
438
|
+
|
439
|
+
# @!endgroup
|
440
|
+
|
441
|
+
# @!group Byte array processing
|
442
|
+
|
443
|
+
##
|
444
|
+
# Performs a XOR processing with given data, XORing every byte of
|
445
|
+
# input with a single given value. Uses pure Ruby implementation suggested
|
446
|
+
# by [Thomas Leitner](https://github.com/gettalong), borrowed from
|
447
|
+
# https://github.com/fny/xorcist/blob/master/bin/benchmark
|
448
|
+
# @param data [String] data to process
|
449
|
+
# @param key [Fixnum] value to XOR with
|
450
|
+
# @return [String] processed data
|
451
|
+
def self.process_xor_one(data, key)
|
452
|
+
out = data.dup
|
453
|
+
i = 0
|
454
|
+
max = data.length
|
455
|
+
while i < max
|
456
|
+
out.setbyte(i, data.getbyte(i) ^ key)
|
457
|
+
i += 1
|
458
|
+
end
|
459
|
+
out
|
460
|
+
end
|
461
|
+
|
462
|
+
##
|
463
|
+
# Performs a XOR processing with given data, XORing every byte of
|
464
|
+
# input with a key array, repeating key array many times, if
|
465
|
+
# necessary (i.e. if data array is longer than key array).
|
466
|
+
# Uses pure Ruby implementation suggested by
|
467
|
+
# [Thomas Leitner](https://github.com/gettalong), borrowed from
|
468
|
+
# https://github.com/fny/xorcist/blob/master/bin/benchmark
|
469
|
+
# @param data [String] data to process
|
470
|
+
# @param key [String] array of bytes to XOR with
|
471
|
+
# @return [String] processed data
|
472
|
+
def self.process_xor_many(data, key)
|
473
|
+
out = data.dup
|
474
|
+
kl = key.length
|
475
|
+
ki = 0
|
476
|
+
i = 0
|
477
|
+
max = data.length
|
478
|
+
while i < max
|
479
|
+
out.setbyte(i, data.getbyte(i) ^ key.getbyte(ki))
|
480
|
+
ki += 1
|
481
|
+
ki = 0 if ki >= kl
|
482
|
+
i += 1
|
483
|
+
end
|
484
|
+
out
|
485
|
+
end
|
486
|
+
|
487
|
+
##
|
488
|
+
# Performs a circular left rotation shift for a given buffer by a
|
489
|
+
# given amount of bits, using groups of groupSize bytes each
|
490
|
+
# time. Right circular rotation should be performed using this
|
491
|
+
# procedure with corrected amount.
|
492
|
+
# @param data [String] source data to process
|
493
|
+
# @param amount [Fixnum] number of bits to shift by
|
494
|
+
# @param group_size [Fixnum] number of bytes per group to shift
|
495
|
+
# @return [String] copy of source array with requested shift applied
|
496
|
+
def self.process_rotate_left(data, amount, group_size)
|
497
|
+
raise NotImplementedError.new("unable to rotate group #{group_size} bytes yet") unless group_size == 1
|
498
|
+
|
499
|
+
mask = group_size * 8 - 1
|
500
|
+
anti_amount = -amount & mask
|
501
|
+
|
502
|
+
# NB: actually, left bit shift (<<) in Ruby would have required
|
503
|
+
# truncation to type_bits size (i.e. something like "& 0xff" for
|
504
|
+
# group_size == 8), but we can skip this one, because later these
|
505
|
+
# number would be packed with Array#pack, which will do truncation
|
506
|
+
# anyway
|
507
|
+
|
508
|
+
data.bytes.map { |x| (x << amount) | (x >> anti_amount) }.pack('C*')
|
509
|
+
end
|
510
|
+
|
511
|
+
# @!endgroup
|
512
|
+
|
513
|
+
##
|
514
|
+
# Resolves value using enum: if the value is not found in the map,
|
515
|
+
# we'll just use literal value per se.
|
516
|
+
def self.resolve_enum(enum_map, value)
|
517
|
+
enum_map[value] || value
|
518
|
+
end
|
519
|
+
|
520
|
+
# ========================================================================
|
521
|
+
|
522
|
+
private
|
523
|
+
SIGN_MASK_16 = (1 << (16 - 1))
|
524
|
+
SIGN_MASK_32 = (1 << (32 - 1))
|
525
|
+
SIGN_MASK_64 = (1 << (64 - 1))
|
526
|
+
|
527
|
+
def to_signed(x, mask)
|
528
|
+
(x & ~mask) - (x & mask)
|
529
|
+
end
|
530
|
+
|
531
|
+
def self.format_hex(arr)
|
532
|
+
arr.unpack('H*')[0].gsub(/(..)/, '\1 ').chop
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
##
|
537
|
+
# Common ancestor for all error originating from Kaitai Struct usage.
|
538
|
+
# Stores KSY source path, pointing to an element supposedly guilty of
|
539
|
+
# an error.
|
540
|
+
class KaitaiStructError < Exception
|
541
|
+
def initialize(msg, src_path)
|
542
|
+
super("#{src_path}: #{msg}")
|
543
|
+
@src_path = src_path
|
544
|
+
end
|
545
|
+
end
|
546
|
+
|
547
|
+
##
|
548
|
+
# Error that occurs when default endianness should be decided with
|
549
|
+
# a switch, but nothing matches (although using endianness expression
|
550
|
+
# implies that there should be some positive result).
|
551
|
+
class UndecidedEndiannessError < KaitaiStructError
|
552
|
+
def initialize(src_path)
|
553
|
+
super("unable to decide on endianness for a type", src_path)
|
554
|
+
end
|
555
|
+
end
|
556
|
+
|
557
|
+
##
|
558
|
+
# Common ancestor for all validation failures. Stores pointer to
|
559
|
+
# KaitaiStream IO object which was involved in an error.
|
560
|
+
class ValidationFailedError < KaitaiStructError
|
561
|
+
def initialize(msg, io, src_path)
|
562
|
+
super("at pos #{io.pos}: validation failed: #{msg}", src_path)
|
563
|
+
@io = io
|
564
|
+
end
|
565
|
+
end
|
566
|
+
|
567
|
+
##
|
568
|
+
# Signals validation failure: we required "actual" value to be equal to
|
569
|
+
# "expected", but it turned out that it's not.
|
570
|
+
class ValidationNotEqualError < ValidationFailedError
|
571
|
+
def initialize(expected, actual, io, src_path)
|
572
|
+
super("not equal, expected #{expected.inspect}, but got #{actual.inspect}", io, src_path)
|
573
|
+
@expected = expected
|
574
|
+
@actual = actual
|
575
|
+
end
|
576
|
+
end
|
577
|
+
|
578
|
+
##
|
579
|
+
# Signals validation failure: we required "actual" value to be greater
|
580
|
+
# than or equal to "min", but it turned out that it's not.
|
581
|
+
class ValidationLessThanError < ValidationFailedError
|
582
|
+
def initialize(min, actual, io, src_path)
|
583
|
+
super("not in range, min #{min.inspect}, but got #{actual.inspect}", io, src_path)
|
584
|
+
@min = min
|
585
|
+
@actual = actual
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
589
|
+
##
|
590
|
+
# Signals validation failure: we required "actual" value to be less
|
591
|
+
# than or equal to "max", but it turned out that it's not.
|
592
|
+
class ValidationGreaterThanError < ValidationFailedError
|
593
|
+
def initialize(max, actual, io, src_path)
|
594
|
+
super("not in range, max #{max.inspect}, but got #{actual.inspect}", io, src_path)
|
595
|
+
@max = max
|
596
|
+
@actual = actual
|
597
|
+
end
|
598
|
+
end
|
599
|
+
|
600
|
+
##
|
601
|
+
# Signals validation failure: we required "actual" value to be any of
|
602
|
+
# the given list, but it turned out that it's not.
|
603
|
+
class ValidationNotAnyOfError < ValidationFailedError
|
604
|
+
def initialize(actual, io, src_path)
|
605
|
+
super("not any of the list, got #{actual.inspect}", io, src_path)
|
606
|
+
@actual = actual
|
607
|
+
end
|
608
|
+
end
|
609
|
+
|
610
|
+
##
|
611
|
+
# Signals validation failure: we required "actual" value to match
|
612
|
+
# the expression, but it turned out that it doesn't.
|
613
|
+
class ValidationExprError < ValidationFailedError
|
614
|
+
def initialize(actual, io, src_path)
|
615
|
+
super("not matching the expression, got #{actual.inspect}", io, src_path)
|
616
|
+
@actual = actual
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
end
|
621
|
+
end
|