kaitai-struct 0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/README.md +65 -0
- data/kaitai-struct.gemspec +31 -0
- data/lib/kaitai/struct/struct.rb +237 -0
- metadata +52 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b07542b0b53559b463ae219d91b5132ae32443f1
|
4
|
+
data.tar.gz: 70117ba80efbb033f7ab465e4226b42a3c4eb238
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 902540aad3e004cfae1a1771b462c85bbfdcdc87d0dbbe2b05d3d08fd5277d2d539eb0ed0c8cf788eac4f44afda293af5b6a624f3d47161100f67ba01343716c
|
7
|
+
data.tar.gz: 25fb636cef7318d76d5139e255b81a9ee4caa78f9a4b440bb4dda437e05720ca0d919bc6bcc97ea93c31952dd3447989e0cc3af72357e8fc5c6bf0e3da8f205b
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
/test/compiled
|
data/README.md
ADDED
@@ -0,0 +1,65 @@
|
|
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](https://github.com/kaitai-io/kaitai_struct/)
|
12
|
+
* [About API implemented in this library](https://github.com/kaitai-io/kaitai_struct/wiki/Kaitai-Struct-stream-API)
|
13
|
+
* [Ruby-specific notes](https://github.com/kaitai-io/kaitai_struct/wiki/Ruby)
|
14
|
+
|
15
|
+
## Installing
|
16
|
+
|
17
|
+
### Using `Gemfile`
|
18
|
+
|
19
|
+
If your project uses Bundler, just include the line
|
20
|
+
|
21
|
+
```
|
22
|
+
gem 'kaitai-struct'
|
23
|
+
```
|
24
|
+
|
25
|
+
in your project's `Gemfile`.
|
26
|
+
|
27
|
+
### Using `gem install`
|
28
|
+
|
29
|
+
If you have a RubyGems package manager installed, you can use command
|
30
|
+
|
31
|
+
```
|
32
|
+
gem install kaitai-struct
|
33
|
+
```
|
34
|
+
|
35
|
+
to install this runtime library.
|
36
|
+
|
37
|
+
### Manually
|
38
|
+
|
39
|
+
This library is intentionally kept as very simple, one-file `.rb`
|
40
|
+
file. One can just copy it to your project from this
|
41
|
+
repository. Usually you won't `require` it directly, it will be loaded
|
42
|
+
by Ruby source code generate by Kaitai Struct compiler.
|
43
|
+
|
44
|
+
## Licensing
|
45
|
+
|
46
|
+
Copyright 2015-2016 Kaitai Project: MIT license
|
47
|
+
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
49
|
+
a copy of this software and associated documentation files (the
|
50
|
+
"Software"), to deal in the Software without restriction, including
|
51
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
52
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
53
|
+
permit persons to whom the Software is furnished to do so, subject to
|
54
|
+
the following conditions:
|
55
|
+
|
56
|
+
The above copyright notice and this permission notice shall be
|
57
|
+
included in all copies or substantial portions of the Software.
|
58
|
+
|
59
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
60
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
61
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
62
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
63
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
64
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
65
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +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
|
+
}
|
@@ -0,0 +1,237 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module Kaitai
|
4
|
+
module Struct
|
5
|
+
|
6
|
+
VERSION = '0.3'
|
7
|
+
|
8
|
+
class Struct
|
9
|
+
|
10
|
+
def initialize(_io, _parent = nil, _root = self)
|
11
|
+
@_io = _io
|
12
|
+
@_parent = _parent
|
13
|
+
@_root = _root
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.from_file(filename)
|
17
|
+
self.new(Stream.open(filename))
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :_io
|
21
|
+
end
|
22
|
+
|
23
|
+
class Stream
|
24
|
+
class UnexpectedDataError < Exception
|
25
|
+
def initialize(actual, expected)
|
26
|
+
super("Unexpected fixed contents: got #{Stream.format_hex(actual)}, was waiting for #{Stream.format_hex(expected)}")
|
27
|
+
@actual = actual
|
28
|
+
@expected = expected
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize(arg)
|
33
|
+
if arg.is_a?(String)
|
34
|
+
@_io = StringIO.new(arg)
|
35
|
+
elsif arg.is_a?(IO)
|
36
|
+
@_io = arg
|
37
|
+
else
|
38
|
+
raise TypeError.new('can be initialized with IO or String only')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.open(filename)
|
43
|
+
self.new(File.open(filename, 'rb:ASCII-8BIT'))
|
44
|
+
end
|
45
|
+
|
46
|
+
# ========================================================================
|
47
|
+
# Forwarding of IO API calls
|
48
|
+
# ========================================================================
|
49
|
+
|
50
|
+
def eof?; @_io.eof?; end
|
51
|
+
def seek(x); @_io.seek(x); end
|
52
|
+
def pos; @_io.pos; end
|
53
|
+
|
54
|
+
# Test endianness of the platform
|
55
|
+
@@big_endian = [0x0102].pack('s') == [0x0102].pack('n')
|
56
|
+
|
57
|
+
def ensure_fixed_contents(size, expected)
|
58
|
+
buf = @_io.read(size)
|
59
|
+
actual = buf.bytes
|
60
|
+
if actual != expected
|
61
|
+
raise UnexpectedDataError.new(actual, expected)
|
62
|
+
end
|
63
|
+
buf
|
64
|
+
end
|
65
|
+
|
66
|
+
# ========================================================================
|
67
|
+
# Unsigned
|
68
|
+
# ========================================================================
|
69
|
+
|
70
|
+
def read_u1
|
71
|
+
read_bytes(1).unpack('C')[0]
|
72
|
+
end
|
73
|
+
|
74
|
+
def read_u2le
|
75
|
+
read_bytes(2).unpack('v')[0]
|
76
|
+
end
|
77
|
+
|
78
|
+
def read_u4le
|
79
|
+
read_bytes(4).unpack('V')[0]
|
80
|
+
end
|
81
|
+
|
82
|
+
unless @@big_endian
|
83
|
+
def read_u8le
|
84
|
+
read_bytes(8).unpack('Q')[0]
|
85
|
+
end
|
86
|
+
else
|
87
|
+
def read_u8le
|
88
|
+
a, b = read_bytes(8).unpack('VV')
|
89
|
+
(b << 32) + a
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def read_u2be
|
94
|
+
read_bytes(2).unpack('n')[0]
|
95
|
+
end
|
96
|
+
|
97
|
+
def read_u4be
|
98
|
+
read_bytes(4).unpack('N')[0]
|
99
|
+
end
|
100
|
+
|
101
|
+
if @@big_endian
|
102
|
+
def read_u8be
|
103
|
+
read_bytes(8).unpack('Q')[0]
|
104
|
+
end
|
105
|
+
else
|
106
|
+
def read_u8be
|
107
|
+
a, b = read_bytes(8).unpack('NN')
|
108
|
+
(a << 32) + b
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# ========================================================================
|
113
|
+
# Signed
|
114
|
+
# ========================================================================
|
115
|
+
|
116
|
+
def read_s1
|
117
|
+
read_bytes(1).unpack('c')[0]
|
118
|
+
end
|
119
|
+
|
120
|
+
def read_s2le
|
121
|
+
to_signed(read_u2le, SIGN_MASK_16)
|
122
|
+
end
|
123
|
+
|
124
|
+
def read_s4le
|
125
|
+
to_signed(read_u4le, SIGN_MASK_32)
|
126
|
+
end
|
127
|
+
|
128
|
+
unless @@big_endian
|
129
|
+
def read_s8le
|
130
|
+
read_bytes(8).unpack('q')[0]
|
131
|
+
end
|
132
|
+
else
|
133
|
+
def read_s8le
|
134
|
+
to_signed(read_u8le, SIGN_MASK_64)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def read_s2be
|
139
|
+
to_signed(read_u2be, SIGN_MASK_16)
|
140
|
+
end
|
141
|
+
|
142
|
+
def read_s4be
|
143
|
+
to_signed(read_u4be, SIGN_MASK_32)
|
144
|
+
end
|
145
|
+
|
146
|
+
if @@big_endian
|
147
|
+
def read_s8be
|
148
|
+
read_bytes(8).unpack('q')[0]
|
149
|
+
end
|
150
|
+
else
|
151
|
+
def read_s8be
|
152
|
+
to_signed(read_u8be, SIGN_MASK_64)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# ========================================================================
|
157
|
+
|
158
|
+
def read_bytes_full
|
159
|
+
@_io.read
|
160
|
+
end
|
161
|
+
|
162
|
+
def read_bytes(n)
|
163
|
+
r = @_io.read(n)
|
164
|
+
if r
|
165
|
+
rl = r.bytesize
|
166
|
+
else
|
167
|
+
rl = 0
|
168
|
+
end
|
169
|
+
raise EOFError.new("attempted to read #{n} bytes, got only #{rl}") if rl < n
|
170
|
+
r
|
171
|
+
end
|
172
|
+
|
173
|
+
# ========================================================================
|
174
|
+
|
175
|
+
def read_str_eos(encoding)
|
176
|
+
read_bytes_full.force_encoding(encoding)
|
177
|
+
end
|
178
|
+
|
179
|
+
def read_str_byte_limit(byte_size, encoding)
|
180
|
+
read_bytes(byte_size).force_encoding(encoding)
|
181
|
+
end
|
182
|
+
|
183
|
+
def read_strz(encoding, term, include_term, consume_term, eos_error)
|
184
|
+
r = ''
|
185
|
+
loop {
|
186
|
+
if @_io.eof?
|
187
|
+
if eos_error
|
188
|
+
raise EOFError.new("end of stream reached, but no terminator #{term} found")
|
189
|
+
else
|
190
|
+
return r.force_encoding(encoding)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
c = @_io.getc
|
194
|
+
if c.ord == term
|
195
|
+
r << c if include_term
|
196
|
+
@_io.seek(@_io.pos - 1) unless consume_term
|
197
|
+
return r.force_encoding(encoding)
|
198
|
+
end
|
199
|
+
r << c
|
200
|
+
}
|
201
|
+
end
|
202
|
+
|
203
|
+
# ========================================================================
|
204
|
+
|
205
|
+
def process_rotate_left(data, amount, group_size)
|
206
|
+
raise NotImplementedError.new("unable to rotate group #{group_size} bytes yet") unless group_size == 1
|
207
|
+
|
208
|
+
mask = group_size * 8 - 1
|
209
|
+
anti_amount = -amount & mask
|
210
|
+
|
211
|
+
# NB: actually, left bit shift (<<) in Ruby would have required
|
212
|
+
# truncation to type_bits size (i.e. something like "& 0xff" for
|
213
|
+
# group_size == 8), but we can skip this one, because later these
|
214
|
+
# number would be packed with Array#pack, which will do truncation
|
215
|
+
# anyway
|
216
|
+
|
217
|
+
data.bytes.map { |x| (x << amount) | (x >> anti_amount) }.pack('C*')
|
218
|
+
end
|
219
|
+
|
220
|
+
# ========================================================================
|
221
|
+
|
222
|
+
private
|
223
|
+
SIGN_MASK_16 = (1 << (16 - 1))
|
224
|
+
SIGN_MASK_32 = (1 << (32 - 1))
|
225
|
+
SIGN_MASK_64 = (1 << (64 - 1))
|
226
|
+
|
227
|
+
def to_signed(x, mask)
|
228
|
+
(x & ~mask) - (x & mask)
|
229
|
+
end
|
230
|
+
|
231
|
+
def self.format_hex(arr)
|
232
|
+
arr.map { |x| sprintf('%02X', x) }.join(' ')
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
237
|
+
end
|
metadata
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kaitai-struct
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.3'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mikhail Yakshin
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-04-19 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: |
|
14
|
+
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.
|
15
|
+
|
16
|
+
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.
|
17
|
+
|
18
|
+
This package provides small runtime library used by code generated by Kaitai Struct compiler.
|
19
|
+
email: greycat@kaitai.io
|
20
|
+
executables: []
|
21
|
+
extensions: []
|
22
|
+
extra_rdoc_files: []
|
23
|
+
files:
|
24
|
+
- ".gitignore"
|
25
|
+
- README.md
|
26
|
+
- kaitai-struct.gemspec
|
27
|
+
- lib/kaitai/struct/struct.rb
|
28
|
+
homepage: http://kaitai.io
|
29
|
+
licenses:
|
30
|
+
- MIT
|
31
|
+
metadata: {}
|
32
|
+
post_install_message:
|
33
|
+
rdoc_options: []
|
34
|
+
require_paths:
|
35
|
+
- lib
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
requirements: []
|
47
|
+
rubyforge_project:
|
48
|
+
rubygems_version: 2.5.1
|
49
|
+
signing_key:
|
50
|
+
specification_version: 4
|
51
|
+
summary: 'Kaitai Struct: runtime library for Ruby'
|
52
|
+
test_files: []
|