binary-protocol 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7acca312b640e28dfc66b2bad980b78ee035d76c
4
+ data.tar.gz: c339bdb3911d544e5e47aeea983fed415ed52be7
5
+ SHA512:
6
+ metadata.gz: 1e4fe348d8f69a6d83d7c64e45adf8b7eec9e51f3c29b599a83d1f4dae537c7cddc07feaf68461d7ae60567c2af537a6afd90aaf562be0cd63ab7d70378a13ac
7
+ data.tar.gz: da21c64e0281573263101ee3a77ab253987228cdcbeeab09bf2b89e1bb46afe1c59d2bfb0c215e23df82ab099ee0f803de2114de995df6c8933d50702dc50094
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .rvmrc
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in binary-protocol.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Andrew Bennett
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ # Binary::Protocol
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'binary-protocol'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install binary-protocol
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'binary/protocol/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "binary-protocol"
8
+ spec.version = Binary::Protocol::VERSION
9
+ spec.authors = ["Andrew Bennett"]
10
+ spec.email = ["andrew@pagodabox.com"]
11
+ spec.description = %q{Helpful DSL for reading and writing binary protocols}
12
+ spec.summary = %q{Helpful DSL for reading and writing binary protocols}
13
+ spec.homepage = "https://github.com/potatosalad/binary-protocol"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "pry"
23
+ spec.add_development_dependency "rake"
24
+ end
@@ -0,0 +1,397 @@
1
+ require "binary/protocol/version"
2
+ require "binary/protocol/extensions"
3
+
4
+ module Binary
5
+ module Protocol
6
+
7
+ BYTES_8 = 1.freeze
8
+ BYTES_16 = 2.freeze
9
+ BYTES_32 = 4.freeze
10
+ BYTES_64 = 8.freeze
11
+
12
+ INT8_PACK = 'c'.freeze # 8-bit signed (signed char)
13
+ INT16_PACK = 's'.freeze # 16-bit signed, native endian (int16_t)
14
+ INT32_PACK = 'l'.freeze # 32-bit signed, native endian (int32_t)
15
+ INT64_PACK = 'q'.freeze # 64-bit signed, native endian (int64_t)
16
+ INT16BE_PACK = 's>'.freeze # 16-bit signed, big-endian
17
+ INT32BE_PACK = 'l>'.freeze # 32-bit signed, big-endian
18
+ INT64BE_PACK = 'q>'.freeze # 64-bit signed, big-endian
19
+ INT16LE_PACK = 's<'.freeze # 16-bit signed, little-endian
20
+ INT32LE_PACK = 'l<'.freeze # 32-bit signed, little-endian
21
+ INT64LE_PACK = 'q<'.freeze # 64-bit signed, little-endian
22
+
23
+ UINT8_PACK = 'C'.freeze # 8-bit unsigned (unsigned char)
24
+ UINT16_PACK = 'S'.freeze # 16-bit unsigned, native endian (uint16_t)
25
+ UINT32_PACK = 'L'.freeze # 32-bit unsigned, native endian (uint32_t)
26
+ UINT64_PACK = 'Q'.freeze # 64-bit unsigned, native endian (uint64_t)
27
+ UINT16BE_PACK = 'n'.freeze # 16-bit unsigned, network (big-endian) byte order
28
+ UINT32BE_PACK = 'N'.freeze # 32-bit unsigned, network (big-endian) byte order
29
+ UINT64BE_PACK = 'Q>'.freeze # 64-bit unsigned, network (big-endian) byte order
30
+ UINT16LE_PACK = 'v'.freeze # 16-bit unsigned, VAX (little-endian) byte order
31
+ UINT32LE_PACK = 'V'.freeze # 32-bit unsigned, VAX (little-endian) byte order
32
+ UINT64LE_PACK = 'Q<'.freeze # 64-bit unsigned, VAX (little-endian) byte order
33
+
34
+ SINGLE_PACK = 'F'.freeze # 32-bit single-precision, native format
35
+ DOUBLE_PACK = 'D'.freeze # 64-bit double-precision, native format
36
+ SINGLEBE_PACK = 'g'.freeze # 32-bit sinlge-precision, network (big-endian) byte order
37
+ DOUBLEBE_PACK = 'G'.freeze # 64-bit double-precision, network (big-endian) byte order
38
+ SINGLELE_PACK = 'e'.freeze # 32-bit sinlge-precision, little-endian byte order
39
+ DOUBLELE_PACK = 'E'.freeze # 64-bit double-precision, little-endian byte order
40
+
41
+ class << self
42
+
43
+ # Extends the including class with +ClassMethods+.
44
+ #
45
+ # @param [Class] subclass the inheriting class
46
+ def included(base)
47
+ super
48
+
49
+ base.extend ClassMethods
50
+ end
51
+
52
+ private :included
53
+ end
54
+
55
+ # Provides a DSL for defining struct-like fields for building
56
+ # binary messages.
57
+ #
58
+ # @example
59
+ # class Command
60
+ # include Binary::Protocol
61
+ #
62
+ # int32 :length
63
+ # end
64
+ #
65
+ # Command.fields # => [:length]
66
+ # command = Command.new
67
+ # command.length = 12
68
+ # command.serialize_length("") # => "\f\x00\x00\x00"
69
+ module ClassMethods
70
+
71
+ # @return [Array] the methods to run in order for serialiation
72
+ def serialization
73
+ @serialization ||= []
74
+ end
75
+
76
+ # @return [Array] the fields defined for this message
77
+ def fields
78
+ @fields ||= []
79
+ end
80
+
81
+ %w[
82
+ int8
83
+ int16
84
+ int32
85
+ int64
86
+ int16be
87
+ int32be
88
+ int64be
89
+ int16le
90
+ int32le
91
+ int64le
92
+ uint8
93
+ uint16
94
+ uint32
95
+ uint64
96
+ uint16be
97
+ uint32be
98
+ uint64be
99
+ uint16le
100
+ uint32le
101
+ uint64le
102
+ ].each do |name|
103
+ pack_const = "#{name.upcase}_PACK".intern
104
+ size_const = "BYTES_#{name.match(/(\d+)/)[0]}".intern
105
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
106
+ def #{name}(name, options = {}, &block)
107
+ __bytes__(#{pack_const.inspect}, #{size_const.inspect}, name, options, &block)
108
+ end
109
+ RUBY
110
+ end
111
+
112
+ %w[
113
+ single
114
+ singlebe
115
+ singlele
116
+ double
117
+ doublebe
118
+ doublele
119
+ ].each do |name|
120
+ pack_const = "#{name.upcase}_PACK".intern
121
+ size_const = !!(name =~ /single/) ? "BYTES_32".intern : "BYTES_64".intern
122
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
123
+ def #{name}(name, options = {}, &block)
124
+ __bytes__(#{pack_const.inspect}, #{size_const.inspect}, name, options, &block)
125
+ end
126
+ RUBY
127
+ end
128
+
129
+ # Declare a string field.
130
+ #
131
+ # @example
132
+ # class Message
133
+ # include Binary::Protocol
134
+ # string :collection
135
+ # end
136
+ #
137
+ # @param [String] name the name of this field
138
+ def string(name, options = {})
139
+ if options.key?(:always)
140
+ __define_always__(name, options[:always])
141
+ else
142
+ if options.key?(:default)
143
+ __define_default__(name, options[:default])
144
+ else
145
+ attr_accessor name
146
+ end
147
+
148
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
149
+ def deserialize_#{name}(buffer)
150
+ raise NotImplementedError
151
+ end
152
+ RUBY
153
+ end
154
+
155
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
156
+ def serialize_#{name}(buffer)
157
+ buffer << #{name}
158
+ end
159
+ RUBY
160
+
161
+ serialization << :"serialize_#{name}"
162
+ fields << name
163
+ end
164
+
165
+ # Declare a null terminated string field.
166
+ #
167
+ # @example
168
+ # class Message
169
+ # include Binary::Protocol
170
+ # stringz :collection
171
+ # end
172
+ #
173
+ # @param [String] name the name of this field
174
+ def stringz(name, options = {})
175
+ if options.key?(:always)
176
+ __define_always__(name, options[:always])
177
+ else
178
+ if options.key?(:default)
179
+ __define_default__(name, options[:default])
180
+ else
181
+ attr_accessor name
182
+ end
183
+
184
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
185
+ def deserialize_#{name}(buffer)
186
+ raise NotImplementedError
187
+ end
188
+ RUBY
189
+ end
190
+
191
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
192
+ def serialize_#{name}(buffer)
193
+ buffer << #{name}
194
+ buffer << 0
195
+ end
196
+ RUBY
197
+
198
+ serialization << :"serialize_#{name}"
199
+ fields << name
200
+ end
201
+
202
+ # Declares the protocol class as complete, and defines its serialization
203
+ # method from the declared fields.
204
+ def finalize
205
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
206
+ def serialize(buffer = "")
207
+ #{serialization.map { |command| "#{command}(buffer)" }.join("\n")}
208
+ buffer
209
+ end
210
+ alias to_s serialize
211
+ RUBY
212
+ end
213
+
214
+ def deserialize(buffer = nil, &block)
215
+ if block_given?
216
+ re_define_method(:deserialize, &block)
217
+ else
218
+ message = allocate
219
+ message.deserialize(buffer)
220
+ message
221
+ end
222
+ end
223
+
224
+ protected
225
+
226
+ def __bytes__(pack_const, size_const, name, options = {}, &block)
227
+ if block_given?
228
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
229
+ def before_serialize_#{name}(buffer)
230
+ @#{name}_start = buffer.bytesize
231
+ end
232
+ RUBY
233
+
234
+ serialization << :"before_serialize_#{name}"
235
+ end
236
+
237
+ if options.key?(:always)
238
+ __define_always__(name, options[:always])
239
+ else
240
+ if options.key?(:default)
241
+ __define_default__(name, options[:default])
242
+ else
243
+ attr_accessor name
244
+ end
245
+
246
+ if options[:type] == :array
247
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
248
+ def deserialize_#{name}(buffer)
249
+ raise NotImplementedError
250
+ end
251
+ RUBY
252
+ else
253
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
254
+ def deserialize_#{name}(buffer)
255
+ self.#{name}, = buffer.read(#{size_const}).unpack(#{pack_const})
256
+ end
257
+ RUBY
258
+ end
259
+ end
260
+
261
+ if options[:type] == :array
262
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
263
+ def serialize_#{name}(buffer)
264
+ buffer << #{name}.pack(#{pack_const}+"*")
265
+ end
266
+ RUBY
267
+ else
268
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
269
+ def serialize_#{name}(buffer)
270
+ buffer << [#{name}].pack(#{pack_const})
271
+ end
272
+ RUBY
273
+ end
274
+
275
+ serialization << :"serialize_#{name}"
276
+
277
+ if block_given?
278
+ returning = fields << name
279
+ instance_eval(&block)
280
+
281
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
282
+ def after_serialize_#{name}(buffer)
283
+ self.#{name} = buffer.bytesize - @#{name}_start - #{options[:inclusive] ? 0 : size_const}
284
+ buffer[@#{name}_start, #{size_const}] = serialize_#{name} ""
285
+ end
286
+ RUBY
287
+
288
+ serialization << :"after_serialize_#{name}"
289
+
290
+ returning
291
+ else
292
+ fields << name
293
+ end
294
+ end
295
+
296
+ def __define_always__(name, always)
297
+ if always.respond_to?(:call)
298
+ re_define_method(name, &always)
299
+ else
300
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
301
+ def #{name}
302
+ @#{name} ||= #{always.inspect}
303
+ end
304
+ RUBY
305
+ end
306
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
307
+ def deserialize_#{name}(buffer)
308
+ # do nothing
309
+ end
310
+ RUBY
311
+ end
312
+
313
+ def __define_default__(name, default)
314
+ attr_writer name
315
+
316
+ if default.respond_to?(:call)
317
+ dval = :"__#{name}_default_value__"
318
+ ivar = :"@#{name}"
319
+ re_define_method(dval, &default)
320
+ re_define_method(name) do
321
+ if instance_variable_defined?(ivar)
322
+ instance_variable_get(ivar)
323
+ else
324
+ instance_variable_set(ivar, __send__(dval))
325
+ end
326
+ end
327
+ else
328
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
329
+ def #{name}
330
+ @#{name} ||= #{default.inspect}
331
+ end
332
+ RUBY
333
+ end
334
+ end
335
+
336
+ private
337
+
338
+ # This ensures that subclasses of the primary protocol classes have
339
+ # identical fields.
340
+ def inherited(subclass)
341
+ super
342
+
343
+ subclass.serialization.replace serialization
344
+ subclass.fields.replace fields
345
+ end
346
+
347
+ end
348
+
349
+ # Default implementation for a message is to do nothing when receiving
350
+ # replies.
351
+ #
352
+ # @example Receive replies.
353
+ # message.receive_replies(connection)
354
+ #
355
+ # @param [ Connection ] connection The connection.
356
+ #
357
+ # @since 1.0.0
358
+ #
359
+ # @return [ nil ] nil.
360
+ def receive_replies(connection); end
361
+
362
+ def deserialize(buffer)
363
+ self.class.fields.each do |field|
364
+ __send__(:"deserialize_#{field}", buffer)
365
+ end
366
+ self
367
+ end
368
+
369
+ # Serializes the message and all of its fields to a new buffer or to the
370
+ # provided buffer.
371
+ #
372
+ # @param [String] buffer a buffer to serialize to
373
+ # @return [String] the result of serliazing this message
374
+ def serialize(buffer = "")
375
+ raise NotImplementedError, "This method is generated after calling #finalize on a message class"
376
+ end
377
+ alias to_s serialize
378
+
379
+ # @return [String] the nicely formatted version of the message
380
+ def inspect
381
+ fields = self.class.fields.map do |field|
382
+ "@#{field}=" + __send__(field).inspect
383
+ end
384
+ "#<#{self.class.name} " <<
385
+ "#{fields * " "}>"
386
+ end
387
+
388
+ def pretty_inspect
389
+ fields = self.class.fields.map do |field|
390
+ "@#{field}=" + __send__(field).inspect
391
+ end
392
+ "#<#{self.class.name}\n" <<
393
+ " #{fields * "\n "}>"
394
+ end
395
+
396
+ end
397
+ end
@@ -0,0 +1 @@
1
+ require "binary/protocol/extensions/module"
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+ module Binary
3
+ module Protocol
4
+ module Extensions
5
+ module Module
6
+
7
+ # Redefine the method. Will undef the method if it exists or simply
8
+ # just define it.
9
+ #
10
+ # @example Redefine the method.
11
+ # Object.re_define_method("exists?") do
12
+ # self
13
+ # end
14
+ #
15
+ # @param [ String, Symbol ] name The name of the method.
16
+ # @param [ Proc ] block The method body.
17
+ #
18
+ # @return [ Method ] The new method.
19
+ #
20
+ # @since 3.0.0
21
+ def re_define_method(name, &block)
22
+ undef_method(name) if method_defined?(name)
23
+ define_method(name, &block)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ unless ::Module.respond_to?(:re_define_method, true)
31
+ ::Module.__send__(:include, Binary::Protocol::Extensions::Module)
32
+ end
@@ -0,0 +1,5 @@
1
+ module Binary
2
+ module Protocol
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: binary-protocol
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Andrew Bennett
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-03-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Helpful DSL for reading and writing binary protocols
56
+ email:
57
+ - andrew@pagodabox.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - Gemfile
64
+ - LICENSE.txt
65
+ - README.md
66
+ - Rakefile
67
+ - binary-protocol.gemspec
68
+ - lib/binary/protocol.rb
69
+ - lib/binary/protocol/extensions.rb
70
+ - lib/binary/protocol/extensions/module.rb
71
+ - lib/binary/protocol/version.rb
72
+ homepage: https://github.com/potatosalad/binary-protocol
73
+ licenses:
74
+ - MIT
75
+ metadata: {}
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubyforge_project:
92
+ rubygems_version: 2.0.2
93
+ signing_key:
94
+ specification_version: 4
95
+ summary: Helpful DSL for reading and writing binary protocols
96
+ test_files: []