1+ #!/usr/bin/env ruby -w
2+
3+ # encoding: utf-8
4+
15module TNetStrings
26 def self . dump ( data )
37 case data
4- when String , Symbol then "#{ data . length } :#{ data } ,"
8+ when String then "#{ data . bytesize } :#{ data . bytes . pack ( 'C*' ) } ,"
9+ when Symbol then "#{ data . to_s . length } :#{ data . to_s } ,"
510 when Fixnum then "#{ data . to_s . length } :#{ data . to_s } #"
611 when Float then "#{ data . to_s . length } :#{ data . to_s } ^"
712 when TrueClass then "4:true!"
@@ -10,11 +15,17 @@ def self.dump(data)
1015 when Array then dump_array ( data )
1116 when Hash then dump_hash ( data )
1217 else
13- raise "Can't serialize stuff that's '#{ data . class } '."
18+ if data . respond_to? ( :to_s )
19+ s = data . to_s
20+ "#{ s . length } :#{ s } ,"
21+ else
22+ raise "Can't serialize stuff that's '#{ data . class } '."
23+ end
1424 end
1525 end
1626
1727 def self . parse ( data )
28+ raise "Invalid data." if data . empty?
1829 payload , payload_type , remain = parse_payload ( data )
1930
2031 value = case payload_type
@@ -42,7 +53,7 @@ def self.parse_payload(data)
4253 if len == 0
4354 payload = ''
4455 else
45- payload , extra = extra [ 0 ..len -1 ] , extra [ len ..-1 ]
56+ payload , extra = extra . byteslice ( 0 ..len -1 ) , extra . byteslice ( len ..-1 )
4657 end
4758 payload_type , remain = extra [ 0 ] , extra [ 1 ..-1 ]
4859
@@ -128,6 +139,22 @@ def test_parse_strings
128139 assert_equal "" , r
129140 end
130141
142+ def test_parse_utf8_bytes
143+ n = "3:foo,2:\u00B5 ,4:\xf0 \x9f \x98 \x87 ,"
144+
145+ s , r = TNetStrings ::parse ( n )
146+ assert_equal "foo" , s
147+ assert_equal "2:µ,4:😇," , r
148+
149+ s , r = TNetStrings ::parse ( r )
150+ assert_equal "µ" , s
151+ assert_equal "4:\u{1F607} ," . force_encoding ( 'UTF-8' ) , r
152+
153+ s , r = TNetStrings ::parse ( r )
154+ assert_equal "😇" , s
155+ assert_equal "" , r
156+ end
157+
131158 def test_parse_fixnum
132159 n = "2:42#"
133160
@@ -240,6 +267,27 @@ def test_parse_hash
240267 assert_equal "" , r
241268 end
242269
270+ def test_parse_exceeding
271+ s = "3:foo,4:true!"
272+
273+ v , r = TNetStrings ::parse ( s )
274+ assert_equal "foo" , v
275+ assert_equal "4:true!" , r
276+
277+ v , r = TNetStrings ::parse ( r )
278+ assert_equal true , v
279+ assert_equal "" , r
280+
281+ msg = ""
282+ begin
283+ v , r = TNetStrings ::parse ( r )
284+ rescue => e
285+ msg = e . message
286+ end
287+ assert_equal "Invalid data." , msg
288+ assert_equal "" , r
289+ end
290+
243291 def test_dump_strings
244292 s = TNetStrings ::dump ( "foobar" )
245293 assert_equal "6:foobar," , s
@@ -248,6 +296,17 @@ def test_dump_strings
248296 assert_equal "6:foobar,3:baz," , s
249297 end
250298
299+ def test_dump_byte_strings
300+ s = TNetStrings ::dump ( "ßöö" )
301+ assert_equal "6:\xC3 \x9F \xC3 \xB6 \xC3 \xB6 ," . unpack ( 'C*' ) , s . bytes
302+
303+ s << TNetStrings ::dump ( "µ" )
304+ assert_equal "6:ßöö,2:µ," , s . force_encoding ( "UTF-8" )
305+
306+ s << TNetStrings ::dump ( "\u{1F607} " ) . force_encoding ( 'UTF-8' )
307+ assert_equal "6:ßöö,2:µ,4:😇," , s . force_encoding ( "UTF-8" )
308+ end
309+
251310 def test_dump_symbol
252311 s = TNetStrings ::dump ( :foobarbaz )
253312 assert_equal "9:foobarbaz," , s
0 commit comments