mplight 1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []