Skip to content

Commit 4e5e3dd

Browse files
committed
Initial commit.
0 parents  commit 4e5e3dd

File tree

6 files changed

+352
-0
lines changed

6 files changed

+352
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.DS_Store
2+
Gemfile.lock
3+
*.gem

Gemfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
source 'https://rubygems.org'
2+
3+
gemspec

LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2014 Alex Brem
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is
8+
furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in
11+
all copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

README.md

Whitespace-only changes.

lib/tnetstrings.rb

Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
module TNetStrings
2+
def self.dump(data)
3+
case data
4+
when String, Symbol then "#{data.length}:#{data},"
5+
when Fixnum then "#{data.to_s.length}:#{data.to_s}#"
6+
when Float then "#{data.to_s.length}:#{data.to_s}^"
7+
when TrueClass then "4:true!"
8+
when FalseClass then "5:false!"
9+
when NilClass then "0:~"
10+
when Array then dump_array(data)
11+
when Hash then dump_hash(data)
12+
else
13+
raise "Can't serialize stuff that's '#{data.class}'."
14+
end
15+
end
16+
17+
def self.parse(data)
18+
payload, payload_type, remain = parse_payload(data)
19+
20+
value = case payload_type
21+
when ',' then payload
22+
when '#' then payload.to_i
23+
when '^' then payload.to_f
24+
when '!' then payload == 'true'
25+
when ']' then parse_array(payload)
26+
when '}' then parse_hash(payload)
27+
when '~'
28+
raise "Payload must be 0 length for null." unless payload.length == 0
29+
nil
30+
else
31+
raise "Invalid payload type: #{payload_type}"
32+
end
33+
34+
[ value, remain ]
35+
end
36+
37+
def self.parse_payload(data)
38+
raise "Invalid payload type: #{payload_type}" if data.empty?
39+
40+
len, extra = data.split(':', 2)
41+
len = len.to_i
42+
if len == 0
43+
payload = ''
44+
else
45+
payload, extra = extra[0..len-1], extra[len..-1]
46+
end
47+
payload_type, remain = extra[0], extra[1..-1]
48+
49+
[ payload, payload_type, remain ]
50+
end
51+
52+
private
53+
54+
def self.parse_array(data)
55+
arr = []
56+
return arr if data.empty?
57+
58+
begin
59+
value, data = parse(data)
60+
arr << value
61+
end while not data.empty?
62+
63+
arr
64+
end
65+
66+
def self.parse_pair(data)
67+
key, extra = parse(data)
68+
raise "Unbalanced hash" if extra.empty?
69+
value, extra = parse(extra)
70+
71+
[ key, value, extra ]
72+
end
73+
74+
def self.parse_hash(data)
75+
hsh = {}
76+
return hsh if data.empty?
77+
78+
begin
79+
key, value, data = parse_pair(data)
80+
hsh[key.to_sym] = value
81+
end while not data.empty?
82+
83+
hsh
84+
end
85+
86+
def self.dump_array(data)
87+
payload = ""
88+
data.each { |v| payload << dump(v) }
89+
"#{payload.length}:#{payload}]"
90+
end
91+
92+
def self.dump_hash(data)
93+
payload = ""
94+
data.each do |k,v|
95+
payload << dump(k.to_s)
96+
payload << dump(v)
97+
end
98+
"#{payload.length}:#{payload}}"
99+
end
100+
end
101+
102+
if $0 == __FILE__
103+
require 'minitest/autorun'
104+
105+
class NmsgTestSocket < MiniTest::Unit::TestCase
106+
107+
def test_parse_string
108+
n = "3:foo,"
109+
110+
s, r = TNetStrings::parse(n)
111+
assert_equal "foo", s
112+
assert_equal "", r
113+
end
114+
115+
def test_parse_strings
116+
n = "3:foo,3:bar,6:foobar,"
117+
118+
s, r = TNetStrings::parse(n)
119+
assert_equal "foo", s
120+
assert_equal "3:bar,6:foobar,", r
121+
122+
s, r = TNetStrings::parse(r)
123+
assert_equal "bar", s
124+
assert_equal "6:foobar,", r
125+
126+
s, r = TNetStrings::parse(r)
127+
assert_equal "foobar", s
128+
assert_equal "", r
129+
end
130+
131+
def test_parse_fixnum
132+
n = "2:42#"
133+
134+
i, r = TNetStrings::parse(n)
135+
assert_equal 42, i
136+
assert_equal "", r
137+
end
138+
139+
def test_parse_fixnums
140+
n = "2:42#1:7#6:123456#"
141+
142+
i, r = TNetStrings::parse(n)
143+
assert_equal 42, i
144+
assert_equal "1:7#6:123456#", r
145+
146+
i, r = TNetStrings::parse(r)
147+
assert_equal 7, i
148+
assert_equal "6:123456#", r
149+
150+
i, r = TNetStrings::parse(r)
151+
assert_equal 123_456, i
152+
assert_equal "", r
153+
end
154+
155+
def test_parse_float
156+
n = "9:3.1415926^"
157+
158+
f, r = TNetStrings::parse(n)
159+
assert_equal 3.1415926, f
160+
assert_equal "", r
161+
end
162+
163+
def test_parse_bool
164+
n = "4:true!"
165+
166+
b, r = TNetStrings::parse(n)
167+
assert_equal true, b
168+
assert_equal "", r
169+
170+
n = "5:false!"
171+
172+
b, r = TNetStrings::parse(n)
173+
assert_equal false, b
174+
assert_equal "", r
175+
end
176+
177+
def test_parse_nil
178+
n = "0:~"
179+
180+
v, r = TNetStrings::parse(n)
181+
assert_equal nil, v
182+
assert_equal "", r
183+
end
184+
185+
def test_parse_mixed
186+
n = "2:ok,4:true!3:313#"
187+
188+
v, r = TNetStrings::parse(n)
189+
assert_equal "ok", v
190+
assert_equal "4:true!3:313#", r
191+
192+
v, r = TNetStrings::parse(r)
193+
assert_equal true, v
194+
assert_equal "3:313#", r
195+
196+
v, r = TNetStrings::parse(r)
197+
assert_equal 313, v
198+
assert_equal "", r
199+
end
200+
201+
def test_parse_array
202+
s, r = TNetStrings::parse("0:]")
203+
assert_equal [], s
204+
assert_equal "", r
205+
206+
s, r = TNetStrings::parse("18:3:foo,3:bar,3:baz,]")
207+
assert_equal [ "foo", "bar", "baz" ], s
208+
assert_equal "", r
209+
210+
a = "0:]18:3:foo,3:bar,3:baz,]37:5:false!6:3.1415^2:42#0:~4:true!2:ok,]"
211+
s, r = TNetStrings::parse(a)
212+
assert_equal [], s
213+
assert_equal "18:3:foo,3:bar,3:baz,]37:5:false!6:3.1415^2:42#0:~4:true!2:ok,]", r
214+
215+
s, r = TNetStrings::parse(r)
216+
assert_equal [ "foo", "bar", "baz" ], s
217+
assert_equal "37:5:false!6:3.1415^2:42#0:~4:true!2:ok,]", r
218+
219+
s, r = TNetStrings::parse(r)
220+
assert_equal [ false, 3.1415, 42, nil, true, "ok" ], s
221+
assert_equal "", r
222+
end
223+
224+
def test_parse_hash
225+
h, r = TNetStrings::parse("0:}")
226+
assert_equal Hash.new, h
227+
assert_equal "", r
228+
229+
h, r = TNetStrings::parse("12:3:foo,3:bar,}")
230+
assert_equal({foo: "bar"}, h)
231+
assert_equal "", r
232+
233+
n = "26:3:cat,4:meow,3:dog,4:bark,}12:3:cow,3:moo,}"
234+
h, r = TNetStrings::parse(n)
235+
assert_equal({ cat: "meow", dog: "bark" }, h)
236+
assert_equal "12:3:cow,3:moo,}", r
237+
238+
h, r = TNetStrings::parse(r)
239+
assert_equal({ cow: "moo" }, h)
240+
assert_equal "", r
241+
end
242+
243+
def test_dump_strings
244+
s = TNetStrings::dump("foobar")
245+
assert_equal "6:foobar,", s
246+
247+
s << TNetStrings::dump("baz")
248+
assert_equal "6:foobar,3:baz,", s
249+
end
250+
251+
def test_dump_symbol
252+
s = TNetStrings::dump(:foobarbaz)
253+
assert_equal "9:foobarbaz,", s
254+
end
255+
256+
def test_dump_fixnums
257+
s = TNetStrings::dump(65_537)
258+
assert_equal "5:65537#", s
259+
260+
s << TNetStrings::dump(42)
261+
assert_equal "5:65537#2:42#", s
262+
263+
s << TNetStrings::dump(300_000_000)
264+
assert_equal "5:65537#2:42#9:300000000#", s
265+
end
266+
267+
def test_dump_floats
268+
s = TNetStrings::dump(3.1415926)
269+
assert_equal "9:3.1415926^", s
270+
271+
s << TNetStrings::dump(0.0000000000667)
272+
assert_equal "9:3.1415926^8:6.67e-11^", s
273+
end
274+
275+
def test_dump_bool
276+
s = TNetStrings::dump(false)
277+
assert_equal "5:false!", s
278+
279+
s << TNetStrings::dump(true)
280+
assert_equal "5:false!4:true!", s
281+
end
282+
283+
def test_dump_nil
284+
s = TNetStrings::dump(nil)
285+
assert_equal "0:~", s
286+
end
287+
288+
def test_dump_array
289+
s = TNetStrings::dump([])
290+
assert_equal "0:]", s
291+
292+
s << TNetStrings::dump([ "foo", "bar", "baz" ])
293+
assert_equal "0:]18:3:foo,3:bar,3:baz,]", s
294+
295+
s << TNetStrings::dump([ false, 3.1415, 42, nil, true, "ok" ])
296+
assert_equal "0:]18:3:foo,3:bar,3:baz,]37:5:false!6:3.1415^2:42#0:~4:true!2:ok,]", s
297+
end
298+
299+
def test_dump_hash
300+
s = TNetStrings::dump({})
301+
assert_equal "0:}", s
302+
303+
s << TNetStrings::dump({ foo: "bar"})
304+
assert_equal "0:}12:3:foo,3:bar,}", s
305+
end
306+
end
307+
end

tnetstrings.gemspec

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Gem::Specification.new do |s|
2+
s.name = 'tnetstrings'
3+
s.version = '0.1.0'
4+
s.license = 'MIT'
5+
s.author = 'Alex Brem'
6+
s.email = '[email protected]'
7+
s.homepage = 'https://github.com/localhost/tnetstrings'
8+
s.summary = %Q{tnetstrings for ruby}
9+
s.description = %Q{tnetstrings for ruby}
10+
11+
s.files = [
12+
'tnetstrings.gemspec',
13+
'lib/tnetstrings.rb',
14+
'Gemfile',
15+
'LICENSE',
16+
'README.md'
17+
]
18+
19+
s.has_rdoc = false
20+
end

0 commit comments

Comments
 (0)