mplight 1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +52 -0
  3. data/README.md +64 -0
  4. data/lib/mplight/bufferio.rb +36 -0
  5. data/lib/mplight.rb +312 -0
  6. data/testit +123 -0
  7. metadata +51 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0c3b91fcd4e88bbb134216d55ff1945c6dc7aefcb72ef798eceb39b1498838f7
4
+ data.tar.gz: 74666ea4879a2f9e56ad77d9c4b53972da6d5f6a45bdb7cb2837b5eeeeaaf529
5
+ SHA512:
6
+ metadata.gz: d54904ba3e5bf7ab1cf1f17457b78f54d89791c376158dc305b5477fa4ca77fb1ad43f7f8c312da3ecb5dd2fd54265365d18e50e85f10468e63e3ac555f71d8b
7
+ data.tar.gz: 4b8a193ee07474925f0be252b4b743945dd60344b0cac9d187b2b7f7aa1bbceba0f9cf74833c9226a078cc271f807726d2ecb1b9f25073706142c66c6341610a
data/LICENSE ADDED
@@ -0,0 +1,52 @@
1
+ # BSD-2-clause license, extended by language use conditions
2
+
3
+ Copyright (C) 2024, Bertram Scharpf <software@bertram-scharpf.de>.
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are
8
+ met:
9
+
10
+ * Redistributions of source code must retain the above copyright
11
+ notice, this list of conditions and the following disclaimer.
12
+
13
+ * Redistributions in binary form must reproduce the above copyright
14
+ notice, this list of conditions and the following disclaimer in
15
+ the documentation and/or other materials provided with the
16
+ distribution.
17
+
18
+ * Redistributions must not contain any clauses about anticipated
19
+ harassment or discrimination, nor must they be held in a so-called
20
+ "inclusive language". As far as German language is used, the
21
+ conditions mentioned below additionally apply.
22
+
23
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
24
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
26
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
27
+ OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
+
35
+
36
+ ## Use of the German Language
37
+
38
+ Beim Gebrauch deutscher Sprache sind Weiterentwicklungen und
39
+ -verbreitungen nur gestattet unter Einhaltung folgender
40
+ zusätzlicher Bedingungen:
41
+
42
+ * Keine Verwendung von sogenannter „geschlechtergerechter Sprache“,
43
+ also Anfügen von weiblichen Endungen mit Binnen-I, Sternchen,
44
+ Doppelpunkt, Unterstrich oder ähnlichem, oder Konstruktionen, die
45
+ den Sachverhalt falsch wiedergeben („Radfahrende“, „Studierende“).
46
+
47
+ * Keine Verwendung der „reformierten Rechtschreibung“ von 1996,
48
+ insbesondere Doppel-S am Silbenende, „plazieren“ mit T, sowie
49
+ Großschreibung von Wendungen wie „des weiteren“.
50
+
51
+
52
+ <!-- vim:set ft=markdown : -->
data/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # MPLight
2
+
3
+ - Ruby library
4
+ - Yet another implementation of the [MessagePack](http://msgpack.org)
5
+ protocol/format
6
+ - Designed to be as small as possible. Clean code, no dependecies, no
7
+ frills, no wokeness.
8
+
9
+ RubyGems install:
10
+
11
+ ```
12
+ gem install mplight
13
+ ```
14
+
15
+ If you like to reduce dependencies, you might prefer to copy the ~300
16
+ lines of Ruby code into your project. See
17
+ [Ruby-Nvim](https://github.com/BertramScharpf/ruby-nvim) for an example.
18
+
19
+
20
+ ## Usage
21
+
22
+ ```ruby
23
+ require "mplight"
24
+ IO.pipe do |r,w|
25
+ t = MPLight::Types.new
26
+
27
+ t.extend MPLight::Packer
28
+ t.init_output w
29
+ t.put %w(foo bar baz)
30
+
31
+ t.extend MPLight::Unpacker
32
+ t.init_input r
33
+ puts t.get.inspect
34
+ end
35
+ ```
36
+
37
+ Usage with [Neovim](https://neovim.io/):
38
+
39
+ ```ruby
40
+ require "mplight"
41
+ require "yaml"
42
+ IO.popen %w(nvim --api-info) do |r|
43
+ t = MPLight::Types.new
44
+ t.extend MPLight::Unpacker
45
+ t.init_input r
46
+ puts t.get.to_yaml
47
+ end
48
+ ```
49
+
50
+ See the [`testit`](testit) file for further examples.
51
+
52
+
53
+ ## Sister Project
54
+
55
+ This library was written together with the
56
+ [Ruby-Nvim](https://github.com/BertramScharpf/ruby-nvim).
57
+
58
+
59
+
60
+ ## Copyright
61
+
62
+ * (C) 2024 Bertram Scharpf <software@bertram-scharpf.de>
63
+ * License: [BSD-2-Clause+](LICENSE)
64
+
@@ -0,0 +1,36 @@
1
+ #
2
+ # mplight/bufferio.rb -- Buffer behaving like an IO
3
+ #
4
+
5
+
6
+ module MPLight
7
+
8
+ class BufferIO
9
+
10
+ attr_reader :data
11
+
12
+ def initialize str = nil
13
+ @data = str||""
14
+ end
15
+
16
+ def binmode
17
+ @data.force_encoding Encoding::ASCII_8BIT
18
+ end
19
+ def sync= _ ; end
20
+ def sync ; true ; end
21
+
22
+ def write d
23
+ @data << d
24
+ end
25
+
26
+ def read n
27
+ @data.slice! 0, n
28
+ end
29
+
30
+ def flush
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+
data/lib/mplight.rb ADDED
@@ -0,0 +1,312 @@
1
+ #
2
+ # mplight.rb -- Lightweight MessagePack implementation
3
+ #
4
+
5
+
6
+ module MPLight
7
+
8
+ VERSION = "1.0".freeze
9
+
10
+ class Error ; end
11
+
12
+
13
+ class Types
14
+
15
+ def initialize *args, **kwargs
16
+ @cls = {}
17
+ @ids = {}
18
+ register -1, Time
19
+ end
20
+
21
+ def register id, klass
22
+ @cls[ id], @ids[ klass] = klass, id
23
+ end
24
+
25
+ def dump obj
26
+ t = @ids[ obj.class]
27
+ unless t then
28
+ return if @default_to_string
29
+ raise Error, "Unregistered class: #{obj.class}"
30
+ end
31
+ [ t, obj.to_mpdata]
32
+ end
33
+
34
+ def default_to_string! ; @default_to_string = true ; end
35
+
36
+ def create id, data
37
+ c = @cls[ id]
38
+ c or raise Error, "Unregistered type id: #{obj.class}"
39
+ c.from_mpdata data, *additional_data
40
+ end
41
+
42
+ def additional_data
43
+ []
44
+ end
45
+
46
+ end
47
+
48
+
49
+ module Packer
50
+
51
+ attr_reader :output
52
+
53
+ def init_output output
54
+ @output = output
55
+ @output.binmode
56
+ @output.sync = true
57
+ self
58
+ end
59
+
60
+ def do_output output
61
+ oi = @output
62
+ init_output output
63
+ yield
64
+ ensure
65
+ @output = oi
66
+ end
67
+
68
+ def put obj
69
+ case obj
70
+ when nil then write_fmt 0xc0
71
+ when false then write_fmt 0xc2
72
+ when true then write_fmt 0xc3
73
+ when Integer then
74
+ if obj >= 0 then
75
+ case obj
76
+ when ...0x80 then write_fmt obj
77
+ when ...0x100 then write_fmt 0xcc ; write_pack obj, "C"
78
+ when ...0x10000 then write_fmt 0xcd ; write_pack obj, "S>"
79
+ when ...0x100000000 then write_fmt 0xce ; write_pack obj, "L>"
80
+ when ...0x10000000000000000 then write_fmt 0xcf ; write_pack obj, "Q>"
81
+ else raise ArgumentError, "Integer too large: #{obj}"
82
+ end
83
+ else
84
+ case obj
85
+ when -0x20... then write_fmt obj+256
86
+ when -0x80... then write_fmt 0xd0 ; write_pack obj, "c"
87
+ when -0x8000... then write_fmt 0xd1 ; write_pack obj, "s>"
88
+ when -0x80000000... then write_fmt 0xd2 ; write_pack obj, "l>"
89
+ when -0x8000000000000000... then write_fmt 0xd3 ; write_pack obj, "q>"
90
+ else raise ArgumentError, "Integer too large: #{obj}"
91
+ end
92
+ end
93
+ when Float then
94
+ case
95
+ when false then write_fmt 0xca ; write_pack obj, "g"
96
+ else write_fmt 0xcb ; write_pack obj, "G"
97
+ end
98
+ when String then
99
+ if obj.encoding == Encoding::ASCII_8BIT then
100
+ l = obj.size
101
+ case l
102
+ when ...0x100 then write_fmt 0xc4 ; write_pack l, "C"
103
+ when ...0x10000 then write_fmt 0xc5 ; write_pack l, "S>"
104
+ when ...0x100000000 then write_fmt 0xc6 ; write_pack l, "L>"
105
+ else raise ArgumentError, "Byte array too long: #{l} bytes"
106
+ end
107
+ else
108
+ obj = obj.encode Encoding::UTF_8 unless obj.encoding == Encoding::UTF_8
109
+ l = obj.bytesize
110
+ case l
111
+ when ...0x20 then write_fmt 0xa0+l
112
+ when ...0x100 then write_fmt 0xd9 ; write_pack l, "C"
113
+ when ...0x10000 then write_fmt 0xda ; write_pack l, "S>"
114
+ when ...0x100000000 then write_fmt 0xdb ; write_pack l, "L>"
115
+ else raise ArgumentError, "String too long: #{l} bytes"
116
+ end
117
+ end
118
+ write_pack obj, "A*"
119
+ when Array then
120
+ l = obj.length
121
+ case l
122
+ when ...0x10 then write_fmt 0x90+l
123
+ when ...0x10000 then write_fmt 0xdc ; write_pack l, "S>"
124
+ when ...0x100000000 then write_fmt 0xdd ; write_pack l, "L>"
125
+ else raise ArgumentError, "Array too long: #{l} elements"
126
+ end
127
+ obj.each { |o| put o }
128
+ when Hash then
129
+ l = obj.length
130
+ case l
131
+ when ...0x10 then write_fmt 0x80+l
132
+ when ...0x10000 then write_fmt 0xde ; write_pack l, "S>"
133
+ when ...0x100000000 then write_fmt 0xdf ; write_pack l, "L>"
134
+ else raise ArgumentError, "Hash too long: #{l} keys"
135
+ end
136
+ obj.each { |k,v| put k ; put v }
137
+ when Symbol then
138
+ put obj.to_s
139
+ else
140
+ type, data = dump obj
141
+ type or return put obj.to_s
142
+ l = data.bytesize
143
+ case l
144
+ when 0x01 then write_fmt 0xd4 ; write_pack type, "c"
145
+ when 0x02 then write_fmt 0xd5 ; write_pack type, "c"
146
+ when 0x04 then write_fmt 0xd6 ; write_pack type, "c"
147
+ when 0x08 then write_fmt 0xd7 ; write_pack type, "c"
148
+ when 0x10 then write_fmt 0xd8 ; write_pack type, "c"
149
+ when ...0x100 then write_fmt 0xc7 ; write_pack l, "C" ; write_pack type, "c"
150
+ when ...0x10000 then write_fmt 0xc8 ; write_pack l, "S>" ; write_pack type, "c"
151
+ when ...0x100000000 then write_fmt 0xc9 ; write_pack l, "L>" ; write_pack type, "c"
152
+ else raise ArgumentError, "Object too large: #{l} bytes"
153
+ end
154
+ write_pack data, "A*"
155
+ end
156
+ self
157
+ end
158
+
159
+ private
160
+
161
+ def write_pack i, t
162
+ d = [i].pack t
163
+ @output.write d
164
+ end
165
+
166
+ def write_fmt i
167
+ write_pack i, "C"
168
+ end
169
+
170
+ end
171
+
172
+
173
+ module Unpacker
174
+
175
+ attr_reader :input
176
+
177
+ def init_input input
178
+ @input = input
179
+ @input.binmode
180
+ self
181
+ end
182
+
183
+ def do_input input
184
+ oi = @input
185
+ init_input input
186
+ yield
187
+ ensure
188
+ @input = oi
189
+ end
190
+
191
+ def eof?
192
+ @input.eof?
193
+ end
194
+
195
+ def get
196
+ fmt = (read 1).unpack1 "C"
197
+ case fmt >> 7
198
+ when 0b0 then fmt
199
+ else
200
+ case fmt >> 5
201
+ when 0b111 then fmt - 256
202
+ when 0b101 then get_str fmt&0b11111
203
+ else
204
+ case fmt >> 4
205
+ when 0b1000 then get_hsh fmt&0b1111
206
+ when 0b1001 then get_ary fmt&0b1111
207
+ else
208
+ case fmt
209
+ when 0xc0 then nil
210
+ when 0xc1 then raise ArgumentError, "Illegal format: #{fmt}"
211
+ when 0xc2 then false
212
+ when 0xc3 then true
213
+ when 0xc4 then read get_len1
214
+ when 0xc5 then read get_len2
215
+ when 0xc6 then read get_len4
216
+ when 0xc7 then get_ext get_len1
217
+ when 0xc8 then get_ext get_len2
218
+ when 0xc9 then get_ext get_len4
219
+ when 0xca then (read 4).unpack1 "g"
220
+ when 0xcb then (read 8).unpack1 "G"
221
+ when 0xcc then (read 1).unpack1 "C"
222
+ when 0xcd then (read 2).unpack1 "S>"
223
+ when 0xce then (read 4).unpack1 "L>"
224
+ when 0xcf then (read 8).unpack1 "Q>"
225
+ when 0xd0 then (read 1).unpack1 "c"
226
+ when 0xd1 then (read 2).unpack1 "s>"
227
+ when 0xd2 then (read 4).unpack1 "l>"
228
+ when 0xd3 then (read 8).unpack1 "q>"
229
+ when 0xd4 then get_ext 1
230
+ when 0xd5 then get_ext 2
231
+ when 0xd6 then get_ext 4
232
+ when 0xd7 then get_ext 8
233
+ when 0xd8 then get_ext 16
234
+ when 0xd9 then get_str get_len1
235
+ when 0xda then get_str get_len2
236
+ when 0xdb then get_str get_len4
237
+ when 0xdc then get_ary get_len2
238
+ when 0xdd then get_ary get_len4
239
+ when 0xde then get_hsh get_len2
240
+ when 0xdf then get_hsh get_len4
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end
246
+
247
+ private
248
+
249
+ def read n
250
+ @input.read n
251
+ end
252
+
253
+ def get_len1 ; (read 1).unpack1 "C" ; end
254
+ def get_len2 ; (read 2).unpack1 "S>" ; end
255
+ def get_len4 ; (read 4).unpack1 "L>" ; end
256
+
257
+ def get_str len
258
+ (read len).force_encoding Encoding::UTF_8
259
+ end
260
+
261
+ def get_ary len
262
+ (0...len).map { get }
263
+ end
264
+
265
+ def get_hsh len
266
+ (0...len).inject Hash.new do |h,| k = get ; h[k] = get ; h end
267
+ end
268
+
269
+ def get_ext len
270
+ type = (read 1).unpack1 "c"
271
+ create type, (read len)
272
+ end
273
+
274
+ end
275
+
276
+ end
277
+
278
+
279
+ class Time
280
+
281
+ class <<self
282
+
283
+ def from_mpdata data, *args
284
+ case data.length
285
+ when 4 then
286
+ s, = data.unpack "L>"
287
+ Time.at s
288
+ when 8 then
289
+ t, = data.unpack "Q>"
290
+ n = t >> 34
291
+ t &= 0x3ffffffff
292
+ Time.at t, n, :nanosecond
293
+ when 12 then
294
+ n, s = data.unpack "L>Q>"
295
+ Time.at s, n, :nanosecond
296
+ else
297
+ raise ArgumentError, "Illegal time data: #{data.inspect}"
298
+ end
299
+ end
300
+
301
+ end
302
+
303
+ def to_mpdata
304
+ case
305
+ when tv_nsec.zero? && tv_sec < 0x100000000 then [ tv_sec].pack "L>"
306
+ when tv_sec < 0x400000000 then [ (tv_nsec << 34)|tv_sec].pack "Q>"
307
+ else [ tv_nsec, tv_sec].pack "L>Q>"
308
+ end
309
+ end
310
+
311
+ end
312
+
data/testit ADDED
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # testit -- Test MPLight
5
+ #
6
+
7
+ $:.unshift "./lib"
8
+ require "mplight"
9
+ require "mplight/bufferio"
10
+
11
+
12
+ class Some
13
+ attr_reader :i
14
+ def initialize i
15
+ @i = i
16
+ end
17
+ def == oth ; @i == oth.i ; end
18
+
19
+ class <<self
20
+ def from_mpdata data
21
+ new *(data.unpack1 "C")
22
+ end
23
+ end
24
+ def to_mpdata
25
+ [ @i].pack "C"
26
+ end
27
+ end
28
+
29
+ class Other
30
+ def == oth ; true ; end
31
+
32
+ class <<self
33
+ def from_mpdata data
34
+ data == "x"*50 or raise "Identification failed."
35
+ new
36
+ end
37
+ end
38
+ def to_mpdata
39
+ "x"*50
40
+ end
41
+ end
42
+
43
+
44
+ class Types < MPLight::Types
45
+ include MPLight::Packer
46
+ include MPLight::Unpacker
47
+ def initialize
48
+ super
49
+ register 1, Some
50
+ register 2, Other
51
+ end
52
+ def init_inout f
53
+ init_input f
54
+ init_output f
55
+ end
56
+ end
57
+
58
+
59
+ OBJS = [
60
+ Math::PI, Float::INFINITY,
61
+ "tränenüberströmt", "€ 100,-", "Holleri du dödl di, diri diri dudl dö.",
62
+ ("äöü".force_encoding "ascii-8bit"),
63
+ true, false,
64
+ %w(ant bat cat dog eel fox gnu hen ide jay kea),
65
+ ["x"], ["x"]*100,
66
+ { "voc" => "Roger", "gt" => "Pete", "bs" => "John", "dr" => "Keith", },
67
+ 0, 1, 0x1f, 0x20, 0x7f, 0x80, -31, -32, -33, 0xffff, 0x1000, 0x1001,
68
+ 0xffffffff, 0x100000000, 0x100000001,
69
+ (Some.new 127), Other.new,
70
+ ]
71
+
72
+
73
+
74
+ t = Types.new
75
+
76
+ if $*.delete "-s" then
77
+ require "socket"
78
+ class UNIXServer
79
+ alias accept_orig accept
80
+ def accept ; a = accept_orig ; yield a ; ensure ; a.close ; end
81
+ end
82
+ UNIXServer.open "mplighttest" do |s|
83
+ s.accept { |a|
84
+ t.init_inout a
85
+ OBJS.each do |o|
86
+ t.put o
87
+ u = t.get
88
+ u == o or raise "Didn't work."
89
+ end
90
+ }
91
+ end
92
+
93
+ elsif $*.delete "-c" then
94
+ require "socket"
95
+ begin
96
+ UNIXSocket.open "mplighttest" do |c|
97
+ t.init_inout c
98
+ OBJS.each do |o|
99
+ u = t.get
100
+ puts u.inspect
101
+ t.put o
102
+ end
103
+ end
104
+ ensure
105
+ File.unlink "mplighttest"
106
+ end
107
+
108
+ else
109
+ t.init_inout MPLight::BufferIO.new
110
+ OBJS.each do |o|
111
+ t.put o
112
+ u = t.get
113
+ u == o or raise "Didn't work."
114
+ rescue
115
+ puts o.inspect
116
+ puts u.inspect
117
+ raise
118
+ end
119
+
120
+ end
121
+
122
+ puts "Everthing is fine!"
123
+
metadata ADDED
@@ -0,0 +1,51 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mplight
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.0'
5
+ platform: ruby
6
+ authors:
7
+ - Bertram Scharpf
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-07-13 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: |
14
+ This a a very small (<300 loc) library for
15
+ writing and reading MessagePack data.
16
+ email: software@bertram-scharpf.de
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files:
20
+ - LICENSE
21
+ files:
22
+ - LICENSE
23
+ - README.md
24
+ - lib/mplight.rb
25
+ - lib/mplight/bufferio.rb
26
+ - testit
27
+ homepage: http://github.com/BertramScharpf/mplight
28
+ licenses:
29
+ - LicenseRef-LICENSE
30
+ metadata: {}
31
+ post_install_message:
32
+ rdoc_options: []
33
+ require_paths:
34
+ - lib
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 3.0.0
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ requirements:
46
+ - Just Ruby
47
+ rubygems_version: 3.5.6
48
+ signing_key:
49
+ specification_version: 4
50
+ summary: Lightweight MessagePack tool
51
+ test_files: []