multi_json 1.0.3 → 1.0.4

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.
@@ -1,9 +1,7 @@
1
1
  rvm:
2
- - 1.8.6
3
2
  - 1.8.7
4
- - 1.9.1
5
3
  - 1.9.2
4
+ - 1.9.3
6
5
  - jruby
7
6
  - rbx
8
7
  - ree
9
- - ruby-head
data/Gemfile CHANGED
@@ -1,7 +1,6 @@
1
1
  source 'http://rubygems.org'
2
2
 
3
3
  gem 'json', '~> 1.4', :require => nil
4
- gem 'json_pure', '~> 1.4', :require => nil
5
- gem 'yajl-ruby', '~> 0.7', :require => nil
4
+ gem 'yajl-ruby', '~> 1.0', :require => nil
6
5
 
7
6
  gemspec
data/README.md CHANGED
@@ -1,6 +1,4 @@
1
- MultiJSON
2
- =========
3
-
1
+ # MultiJSON
4
2
  Lots of Ruby libraries utilize JSON parsing in some form, and everyone has
5
3
  their favorite JSON library. In order to best support multiple JSON parsers and
6
4
  libraries, <tt>multi_json</tt> is a general-purpose swappable JSON backend
@@ -9,11 +7,13 @@ library. You use it like so:
9
7
  require 'multi_json'
10
8
 
11
9
  MultiJson.engine = :yajl
12
- MultiJson.decode('{"abc":"def"}') # decoded using Yajl
10
+ MultiJson.decode('{ "abc":"def" }') # decoded using Yajl
13
11
 
14
12
  MultiJson.engine = :json_gem
15
13
  MultiJson.engine = MultiJson::Engines::JsonGem # equivalent to previous line
16
- MultiJson.encode({:abc => 'def'}) # encoded using the JSON gem
14
+ MultiJson.encode({ :abc => 'def' }) # encoded using the JSON gem
15
+
16
+ MultiJson.encode({ :abc => 'def' }, :pretty => true) # encoded in a pretty form (ignored if engine is ok_json)
17
17
 
18
18
  The <tt>engine</tt> setter takes either a symbol or a class (to allow for
19
19
  custom JSON parsers) that responds to both <tt>.decode</tt> and
@@ -25,12 +25,10 @@ load any. When loading, libraries are ordered by speed. First Yajl-Ruby, then
25
25
  the JSON gem, then JSON pure. If no JSON library is available, MultiJSON falls
26
26
  back to a bundled version of [OkJson](https://github.com/kr/okjson).
27
27
 
28
- Continuous Integration
29
- ----------------------
30
- [![Build Status](http://travis-ci.org/intridea/multi_json.png)](http://travis-ci.org/intridea/multi_json)
28
+ ## <a name="ci">Continuous Integration</a>
29
+ [![Build Status](https://secure.travis-ci.org/intridea/multi_json.png)](http://travis-ci.org/intridea/multi_json)
31
30
 
32
- Contributing
33
- ------------
31
+ ## <a name="contributing">Contributing</a>
34
32
  In the spirit of [free software](http://www.fsf.org/licensing/essays/free-sw.html), **everyone** is encouraged to help improve this project.
35
33
 
36
34
  Here are some ways *you* can contribute:
@@ -45,17 +43,18 @@ Here are some ways *you* can contribute:
45
43
  * by closing [issues](https://github.com/intridea/multi_json/issues)
46
44
  * by reviewing patches
47
45
 
48
- Submitting an Issue
49
- -------------------
50
- We use the [GitHub issue tracker](https://github.com/intridea/multi_json/issues) to track bugs and
51
- features. Before submitting a bug report or feature request, check to make sure it hasn't already
52
- been submitted. You can indicate support for an existing issuse by voting it up. When submitting a
53
- bug report, please include a [Gist](https://gist.github.com/) that includes a stack trace and any
54
- details that may be necessary to reproduce the bug, including your gem version, Ruby version, and
55
- operating system. Ideally, a bug report should include a pull request with failing specs.
56
-
57
- Submitting a Pull Request
58
- -------------------------
46
+ ## <a name="issues">Submitting an Issue</a>
47
+ We use the [GitHub issue
48
+ tracker](https://github.com/intridea/multi_json/issues) to track bugs and
49
+ features. Before submitting a bug report or feature request, check to make sure
50
+ it hasn't already been submitted. You can indicate support for an existing
51
+ issuse by voting it up. When submitting a bug report, please include a
52
+ [Gist](https://gist.github.com/) that includes a stack trace and any details
53
+ that may be necessary to reproduce the bug, including your gem version, Ruby
54
+ version, and operating system. Ideally, a bug report should include a pull
55
+ request with failing specs.
56
+
57
+ ## <a name="pulls">Submitting a Pull Request</a>
59
58
  1. Fork the project.
60
59
  2. Create a topic branch.
61
60
  3. Implement your feature or bug fix.
@@ -64,7 +63,32 @@ Submitting a Pull Request
64
63
  6. Commit and push your changes.
65
64
  7. Submit a pull request. Please do not include changes to the gemspec, version, or history file. (If you want to create your own version for some reason, please do so in a separate commit.)
66
65
 
67
- Copyright
68
- ---------
66
+ ## <a name="rubies">Supported Rubies</a>
67
+ This library aims to support and is [tested
68
+ against](http://travis-ci.org/intridea/multi_json) the following Ruby
69
+ implementations:
70
+
71
+ * Ruby 1.8.7
72
+ * Ruby 1.9.1
73
+ * Ruby 1.9.2
74
+ * [JRuby](http://www.jruby.org/)
75
+ * [Rubinius](http://rubini.us/)
76
+ * [Ruby Enterprise Edition](http://www.rubyenterpriseedition.com/)
77
+
78
+ If something doesn't work on one of these interpreters, it should be considered
79
+ a bug.
80
+
81
+ This library may inadvertently work (or seem to work) on other Ruby
82
+ implementations, however support will only be provided for the versions listed
83
+ above.
84
+
85
+ If you would like this library to support another Ruby version, you may
86
+ volunteer to be a maintainer. Being a maintainer entails making sure all tests
87
+ run and pass on that implementation. When something breaks on your
88
+ implementation, you will be personally responsible for providing patches in a
89
+ timely fashion. If critical issues for a particular implementation exist at the
90
+ time of a major release, support for that Ruby version may be dropped.
91
+
92
+ ## <a name="copyright">Copyright</a>
69
93
  Copyright (c) 2010 Michael Bleigh, Josh Kalderimis, Erik Michaels-Ober, and Intridea, Inc.
70
94
  See [LICENSE](https://github.com/intridea/multi_json/blob/master/LICENSE.md) for details.
data/Rakefile CHANGED
@@ -13,11 +13,13 @@ RSpec::Core::RakeTask.new(:spec)
13
13
  task :default => :spec
14
14
  task :test => :spec
15
15
 
16
- require 'rdoc/task'
17
- Rake::RDocTask.new do |rdoc|
18
- rdoc.rdoc_dir = 'rdoc'
19
- rdoc.title = "multi_json #{MultiJson::VERSION}"
20
- rdoc.rdoc_files.include('README.md')
21
- rdoc.rdoc_files.include('LICENSE.md')
22
- rdoc.rdoc_files.include('lib/**/*.rb')
16
+ namespace :doc do
17
+ require 'rdoc/task'
18
+ require File.expand_path('../lib/multi_json/version', __FILE__)
19
+ RDoc::Task.new do |rdoc|
20
+ rdoc.rdoc_dir = 'rdoc'
21
+ rdoc.title = "multi_json #{MultiJson::VERSION}"
22
+ rdoc.main = 'README.md'
23
+ rdoc.rdoc_files.include('README.md', 'LICENSE.md', 'lib/**/*.rb')
24
+ end
23
25
  end
@@ -1,5 +1,13 @@
1
1
  module MultiJson
2
- class DecodeError < StandardError; end
2
+ class DecodeError < StandardError
3
+ attr_reader :data
4
+ def initialize(message, backtrace, data)
5
+ super(message)
6
+ self.set_backtrace(backtrace)
7
+ @data = data
8
+ end
9
+ end
10
+
3
11
  module_function
4
12
 
5
13
  @engine = nil
@@ -17,6 +25,8 @@ module MultiJson
17
25
  ["json/pure", :json_pure]
18
26
  ]
19
27
 
28
+ DEFAULT_ENGINE_WARNING = 'Warning: multi_json is using default ok_json engine. Suggested action: require and load an appropriate JSON library.'
29
+
20
30
  # The default engine based on what you currently
21
31
  # have loaded and installed. First checks to see
22
32
  # if any engines are already loaded, then checks
@@ -34,6 +44,7 @@ module MultiJson
34
44
  end
35
45
  end
36
46
 
47
+ Kernel.warn DEFAULT_ENGINE_WARNING
37
48
  :ok_json
38
49
  end
39
50
 
@@ -64,11 +75,11 @@ module MultiJson
64
75
  def decode(string, options = {})
65
76
  engine.decode(string, options)
66
77
  rescue engine::ParseError => exception
67
- raise DecodeError, exception.message, exception.backtrace
78
+ raise DecodeError.new(exception.message, exception.backtrace, string)
68
79
  end
69
80
 
70
81
  # Encodes a Ruby object as JSON.
71
- def encode(object)
72
- engine.encode(object)
82
+ def encode(object, options = {})
83
+ engine.encode(object, options)
73
84
  end
74
85
  end
@@ -0,0 +1,27 @@
1
+ module MultiJson
2
+ module Engines
3
+ module JsonCommon
4
+
5
+ def decode(string, options = {})
6
+ opts = {}
7
+ opts[:symbolize_names] = options[:symbolize_keys]
8
+ string = string.read if string.respond_to?(:read)
9
+ ::JSON.parse(string, opts)
10
+ end
11
+
12
+ def encode(object, options = {})
13
+ object.to_json(process_options(options))
14
+ end
15
+
16
+ protected
17
+
18
+ def process_options(options={})
19
+ return options if options.empty?
20
+ opts = {}
21
+ opts.merge!(JSON::PRETTY_STATE_PROTOTYPE.to_h) if options.delete(:pretty)
22
+ opts.merge! options
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -1,21 +1,12 @@
1
1
  require 'json' unless defined?(::JSON)
2
+ require 'multi_json/engines/json_common'
2
3
 
3
4
  module MultiJson
4
5
  module Engines
5
6
  # Use the JSON gem to encode/decode.
6
7
  class JsonGem
7
8
  ParseError = ::JSON::ParserError
8
-
9
- def self.decode(string, options = {}) #:nodoc:
10
- opts = {}
11
- opts[:symbolize_names] = options[:symbolize_keys]
12
- string = string.read if string.respond_to?(:read)
13
- ::JSON.parse(string, opts)
14
- end
15
-
16
- def self.encode(object) #:nodoc:
17
- object.to_json
18
- end
9
+ extend JsonCommon
19
10
  end
20
11
  end
21
12
  end
@@ -1,21 +1,12 @@
1
1
  require 'json/pure' unless defined?(::JSON)
2
+ require 'multi_json/engines/json_common'
2
3
 
3
4
  module MultiJson
4
5
  module Engines
5
6
  # Use JSON pure to encode/decode.
6
7
  class JsonPure
7
8
  ParseError = ::JSON::ParserError
8
-
9
- def self.decode(string, options = {}) #:nodoc:
10
- opts = {}
11
- opts[:symbolize_names] = options[:symbolize_keys]
12
- string = string.read if string.respond_to?(:read)
13
- ::JSON.parse(string, opts)
14
- end
15
-
16
- def self.encode(object) #:nodoc:
17
- object.to_json
18
- end
9
+ extend JsonCommon
19
10
  end
20
11
  end
21
12
  end
@@ -1,18 +1,18 @@
1
- require "multi_json/vendor/ok_json" unless defined?(::OkJson)
1
+ require "multi_json/vendor/ok_json"
2
2
 
3
3
  module MultiJson
4
4
  module Engines
5
5
  class OkJson
6
- ParseError = ::OkJson::Error
6
+ ParseError = ::MultiJson::OkJson::Error
7
7
 
8
8
  def self.decode(string, options = {}) #:nodoc:
9
9
  string = string.read if string.respond_to?(:read)
10
- result = ::OkJson.decode(string)
10
+ result = ::MultiJson::OkJson.decode(string)
11
11
  options[:symbolize_keys] ? symbolize_keys(result) : result
12
12
  end
13
13
 
14
- def self.encode(object) #:nodoc:
15
- ::OkJson.valenc(stringify_keys(object))
14
+ def self.encode(object, options = {}) #:nodoc:
15
+ ::MultiJson::OkJson.valenc(stringify_keys(object))
16
16
  end
17
17
 
18
18
  def self.symbolize_keys(object) #:nodoc:
@@ -10,8 +10,8 @@ module MultiJson
10
10
  ::Yajl::Parser.new(:symbolize_keys => options[:symbolize_keys]).parse(string)
11
11
  end
12
12
 
13
- def self.encode(object) #:nodoc:
14
- ::Yajl::Encoder.new.encode(object)
13
+ def self.encode(object, options = {}) #:nodoc:
14
+ ::Yajl::Encoder.encode(object, options)
15
15
  end
16
16
  end
17
17
  end
@@ -25,84 +25,75 @@ require 'stringio'
25
25
  # Some parts adapted from
26
26
  # http://golang.org/src/pkg/json/decode.go and
27
27
  # http://golang.org/src/pkg/utf8/utf8.go
28
- module OkJson
29
- extend self
30
-
31
-
32
- # Decodes a json document in string s and
33
- # returns the corresponding ruby value.
34
- # String s must be valid UTF-8. If you have
35
- # a string in some other encoding, convert
36
- # it first.
37
- #
38
- # String values in the resulting structure
39
- # will be UTF-8.
40
- def decode(s)
41
- ts = lex(s)
42
- v, ts = textparse(ts)
43
- if ts.length > 0
44
- raise Error, 'trailing garbage'
45
- end
46
- v
47
- end
48
-
49
-
50
- # Parses a "json text" in the sense of RFC 4627.
51
- # Returns the parsed value and any trailing tokens.
52
- # Note: this is almost the same as valparse,
53
- # except that it does not accept atomic values.
54
- def textparse(ts)
55
- if ts.length < 0
56
- raise Error, 'empty'
57
- end
58
-
59
- typ, _, val = ts[0]
60
- case typ
61
- when '{' then objparse(ts)
62
- when '[' then arrparse(ts)
63
- else
64
- raise Error, "unexpected #{val.inspect}"
28
+ module MultiJson
29
+ module OkJson
30
+ extend self
31
+
32
+
33
+ # Decodes a json document in string s and
34
+ # returns the corresponding ruby value.
35
+ # String s must be valid UTF-8. If you have
36
+ # a string in some other encoding, convert
37
+ # it first.
38
+ #
39
+ # String values in the resulting structure
40
+ # will be UTF-8.
41
+ def decode(s)
42
+ ts = lex(s)
43
+ v, ts = textparse(ts)
44
+ if ts.length > 0
45
+ raise Error, 'trailing garbage'
46
+ end
47
+ v
65
48
  end
66
- end
67
49
 
68
50
 
69
- # Parses a "value" in the sense of RFC 4627.
70
- # Returns the parsed value and any trailing tokens.
71
- def valparse(ts)
72
- if ts.length < 0
73
- raise Error, 'empty'
74
- end
51
+ # Parses a "json text" in the sense of RFC 4627.
52
+ # Returns the parsed value and any trailing tokens.
53
+ # Note: this is almost the same as valparse,
54
+ # except that it does not accept atomic values.
55
+ def textparse(ts)
56
+ if ts.length < 0
57
+ raise Error, 'empty'
58
+ end
75
59
 
76
- typ, _, val = ts[0]
77
- case typ
78
- when '{' then objparse(ts)
79
- when '[' then arrparse(ts)
80
- when :val,:str then [val, ts[1..-1]]
81
- else
82
- raise Error, "unexpected #{val.inspect}"
60
+ typ, _, val = ts[0]
61
+ case typ
62
+ when '{' then objparse(ts)
63
+ when '[' then arrparse(ts)
64
+ else
65
+ raise Error, "unexpected #{val.inspect}"
66
+ end
83
67
  end
84
- end
85
68
 
86
69
 
87
- # Parses an "object" in the sense of RFC 4627.
88
- # Returns the parsed value and any trailing tokens.
89
- def objparse(ts)
90
- ts = eat('{', ts)
91
- obj = {}
70
+ # Parses a "value" in the sense of RFC 4627.
71
+ # Returns the parsed value and any trailing tokens.
72
+ def valparse(ts)
73
+ if ts.length < 0
74
+ raise Error, 'empty'
75
+ end
92
76
 
93
- if ts[0][0] == '}'
94
- return obj, ts[1..-1]
77
+ typ, _, val = ts[0]
78
+ case typ
79
+ when '{' then objparse(ts)
80
+ when '[' then arrparse(ts)
81
+ when :val,:str then [val, ts[1..-1]]
82
+ else
83
+ raise Error, "unexpected #{val.inspect}"
84
+ end
95
85
  end
96
86
 
97
- k, v, ts = pairparse(ts)
98
- obj[k] = v
99
87
 
100
- if ts[0][0] == '}'
101
- return obj, ts[1..-1]
102
- end
88
+ # Parses an "object" in the sense of RFC 4627.
89
+ # Returns the parsed value and any trailing tokens.
90
+ def objparse(ts)
91
+ ts = eat('{', ts)
92
+ obj = {}
103
93
 
104
- loop do
105
- ts = eat(',', ts)
94
+ if ts[0][0] == '}'
95
+ return obj, ts[1..-1]
96
+ end
106
97
 
107
98
  k, v, ts = pairparse(ts)
108
99
  obj[k] = v
@@ -110,42 +101,42 @@ module OkJson
110
101
  if ts[0][0] == '}'
111
102
  return obj, ts[1..-1]
112
103
  end
113
- end
114
- end
115
104
 
105
+ loop do
106
+ ts = eat(',', ts)
116
107
 
117
- # Parses a "member" in the sense of RFC 4627.
118
- # Returns the parsed values and any trailing tokens.
119
- def pairparse(ts)
120
- (typ, _, k), ts = ts[0], ts[1..-1]
121
- if typ != :str
122
- raise Error, "unexpected #{k.inspect}"
123
- end
124
- ts = eat(':', ts)
125
- v, ts = valparse(ts)
126
- [k, v, ts]
127
- end
108
+ k, v, ts = pairparse(ts)
109
+ obj[k] = v
128
110
 
111
+ if ts[0][0] == '}'
112
+ return obj, ts[1..-1]
113
+ end
114
+ end
115
+ end
129
116
 
130
- # Parses an "array" in the sense of RFC 4627.
131
- # Returns the parsed value and any trailing tokens.
132
- def arrparse(ts)
133
- ts = eat('[', ts)
134
- arr = []
135
117
 
136
- if ts[0][0] == ']'
137
- return arr, ts[1..-1]
118
+ # Parses a "member" in the sense of RFC 4627.
119
+ # Returns the parsed values and any trailing tokens.
120
+ def pairparse(ts)
121
+ (typ, _, k), ts = ts[0], ts[1..-1]
122
+ if typ != :str
123
+ raise Error, "unexpected #{k.inspect}"
124
+ end
125
+ ts = eat(':', ts)
126
+ v, ts = valparse(ts)
127
+ [k, v, ts]
138
128
  end
139
129
 
140
- v, ts = valparse(ts)
141
- arr << v
142
130
 
143
- if ts[0][0] == ']'
144
- return arr, ts[1..-1]
145
- end
131
+ # Parses an "array" in the sense of RFC 4627.
132
+ # Returns the parsed value and any trailing tokens.
133
+ def arrparse(ts)
134
+ ts = eat('[', ts)
135
+ arr = []
146
136
 
147
- loop do
148
- ts = eat(',', ts)
137
+ if ts[0][0] == ']'
138
+ return arr, ts[1..-1]
139
+ end
149
140
 
150
141
  v, ts = valparse(ts)
151
142
  arr << v
@@ -153,429 +144,444 @@ module OkJson
153
144
  if ts[0][0] == ']'
154
145
  return arr, ts[1..-1]
155
146
  end
156
- end
157
- end
158
147
 
148
+ loop do
149
+ ts = eat(',', ts)
150
+
151
+ v, ts = valparse(ts)
152
+ arr << v
159
153
 
160
- def eat(typ, ts)
161
- if ts[0][0] != typ
162
- raise Error, "expected #{typ} (got #{ts[0].inspect})"
154
+ if ts[0][0] == ']'
155
+ return arr, ts[1..-1]
156
+ end
157
+ end
163
158
  end
164
- ts[1..-1]
165
- end
166
159
 
167
160
 
168
- # Sans s and returns a list of json tokens,
169
- # excluding white space (as defined in RFC 4627).
170
- def lex(s)
171
- ts = []
172
- while s.length > 0
173
- typ, lexeme, val = tok(s)
174
- if typ == nil
175
- raise Error, "invalid character at #{s[0,10].inspect}"
161
+ def eat(typ, ts)
162
+ if ts[0][0] != typ
163
+ raise Error, "expected #{typ} (got #{ts[0].inspect})"
176
164
  end
177
- if typ != :space
178
- ts << [typ, lexeme, val]
165
+ ts[1..-1]
166
+ end
167
+
168
+
169
+ # Sans s and returns a list of json tokens,
170
+ # excluding white space (as defined in RFC 4627).
171
+ def lex(s)
172
+ ts = []
173
+ while s.length > 0
174
+ typ, lexeme, val = tok(s)
175
+ if typ == nil
176
+ raise Error, "invalid character at #{s[0,10].inspect}"
177
+ end
178
+ if typ != :space
179
+ ts << [typ, lexeme, val]
180
+ end
181
+ s = s[lexeme.length..-1]
179
182
  end
180
- s = s[lexeme.length..-1]
183
+ ts
181
184
  end
182
- ts
183
- end
184
185
 
185
186
 
186
- # Scans the first token in s and
187
- # returns a 3-element list, or nil
188
- # if no such token exists.
189
- #
190
- # The first list element is one of
191
- # '{', '}', ':', ',', '[', ']',
192
- # :val, :str, and :space.
193
- #
194
- # The second element is the lexeme.
195
- #
196
- # The third element is the value of the
197
- # token for :val and :str, otherwise
198
- # it is the lexeme.
199
- def tok(s)
200
- case s[0]
201
- when ?{ then ['{', s[0,1], s[0,1]]
202
- when ?} then ['}', s[0,1], s[0,1]]
203
- when ?: then [':', s[0,1], s[0,1]]
204
- when ?, then [',', s[0,1], s[0,1]]
205
- when ?[ then ['[', s[0,1], s[0,1]]
206
- when ?] then [']', s[0,1], s[0,1]]
207
- when ?n then nulltok(s)
208
- when ?t then truetok(s)
209
- when ?f then falsetok(s)
210
- when ?" then strtok(s)
211
- when Spc then [:space, s[0,1], s[0,1]]
212
- when ?\t then [:space, s[0,1], s[0,1]]
213
- when ?\n then [:space, s[0,1], s[0,1]]
214
- when ?\r then [:space, s[0,1], s[0,1]]
215
- else numtok(s)
187
+ # Scans the first token in s and
188
+ # returns a 3-element list, or nil
189
+ # if no such token exists.
190
+ #
191
+ # The first list element is one of
192
+ # '{', '}', ':', ',', '[', ']',
193
+ # :val, :str, and :space.
194
+ #
195
+ # The second element is the lexeme.
196
+ #
197
+ # The third element is the value of the
198
+ # token for :val and :str, otherwise
199
+ # it is the lexeme.
200
+ def tok(s)
201
+ case s[0]
202
+ when ?{ then ['{', s[0,1], s[0,1]]
203
+ when ?} then ['}', s[0,1], s[0,1]]
204
+ when ?: then [':', s[0,1], s[0,1]]
205
+ when ?, then [',', s[0,1], s[0,1]]
206
+ when ?[ then ['[', s[0,1], s[0,1]]
207
+ when ?] then [']', s[0,1], s[0,1]]
208
+ when ?n then nulltok(s)
209
+ when ?t then truetok(s)
210
+ when ?f then falsetok(s)
211
+ when ?" then strtok(s)
212
+ when Spc then [:space, s[0,1], s[0,1]]
213
+ when ?\t then [:space, s[0,1], s[0,1]]
214
+ when ?\n then [:space, s[0,1], s[0,1]]
215
+ when ?\r then [:space, s[0,1], s[0,1]]
216
+ else numtok(s)
217
+ end
216
218
  end
217
- end
218
219
 
219
220
 
220
- def nulltok(s); s[0,4] == 'null' && [:val, 'null', nil] end
221
- def truetok(s); s[0,4] == 'true' && [:val, 'true', true] end
222
- def falsetok(s); s[0,5] == 'false' && [:val, 'false', false] end
221
+ def nulltok(s); s[0,4] == 'null' && [:val, 'null', nil] end
222
+ def truetok(s); s[0,4] == 'true' && [:val, 'true', true] end
223
+ def falsetok(s); s[0,5] == 'false' && [:val, 'false', false] end
223
224
 
224
225
 
225
- def numtok(s)
226
- m = /-?([1-9][0-9]+|[0-9])([.][0-9]+)?([eE][+-]?[0-9]+)?/.match(s)
227
- if m && m.begin(0) == 0
228
- if m[3] && !m[2]
229
- [:val, m[0], Integer(m[1])*(10**Integer(m[3][1..-1]))]
230
- elsif m[2]
231
- [:val, m[0], Float(m[0])]
232
- else
233
- [:val, m[0], Integer(m[0])]
226
+ def numtok(s)
227
+ m = /-?([1-9][0-9]+|[0-9])([.][0-9]+)?([eE][+-]?[0-9]+)?/.match(s)
228
+ if m && m.begin(0) == 0
229
+ if m[3] && !m[2]
230
+ [:val, m[0], Integer(m[1])*(10**Integer(m[3][1..-1]))]
231
+ elsif m[2]
232
+ [:val, m[0], Float(m[0])]
233
+ else
234
+ [:val, m[0], Integer(m[0])]
235
+ end
234
236
  end
235
237
  end
236
- end
237
238
 
238
239
 
239
- def strtok(s)
240
- m = /"([^"\\]|\\["\/\\bfnrt]|\\u[0-9a-fA-F]{4})*"/.match(s)
241
- if ! m
242
- raise Error, "invalid string literal at #{abbrev(s)}"
240
+ def strtok(s)
241
+ m = /"([^"\\]|\\["\/\\bfnrt]|\\u[0-9a-fA-F]{4})*"/.match(s)
242
+ if ! m
243
+ raise Error, "invalid string literal at #{abbrev(s)}"
244
+ end
245
+ [:str, m[0], unquote(m[0])]
243
246
  end
244
- [:str, m[0], unquote(m[0])]
245
- end
246
-
247
247
 
248
- def abbrev(s)
249
- t = s[0,10]
250
- p = t['`']
251
- t = t[0,p] if p
252
- t = t + '...' if t.length < s.length
253
- '`' + t + '`'
254
- end
255
248
 
249
+ def abbrev(s)
250
+ t = s[0,10]
251
+ p = t['`']
252
+ t = t[0,p] if p
253
+ t = t + '...' if t.length < s.length
254
+ '`' + t + '`'
255
+ end
256
256
 
257
- # Converts a quoted json string literal q into a UTF-8-encoded string.
258
- # The rules are different than for Ruby, so we cannot use eval.
259
- # Unquote will raise an error if q contains control characters.
260
- def unquote(q)
261
- q = q[1...-1]
262
- a = q.dup # allocate a big enough string
263
- r, w = 0, 0
264
- while r < q.length
265
- c = q[r]
266
- case true
267
- when c == ?\\
268
- r += 1
269
- if r >= q.length
270
- raise Error, "string literal ends with a \"\\\": \"#{q}\""
271
- end
272
257
 
273
- case q[r]
274
- when ?",?\\,?/,?'
275
- a[w] = q[r]
276
- r += 1
277
- w += 1
278
- when ?b,?f,?n,?r,?t
279
- a[w] = Unesc[q[r]]
280
- r += 1
281
- w += 1
282
- when ?u
258
+ # Converts a quoted json string literal q into a UTF-8-encoded string.
259
+ # The rules are different than for Ruby, so we cannot use eval.
260
+ # Unquote will raise an error if q contains control characters.
261
+ def unquote(q)
262
+ q = q[1...-1]
263
+ a = q.dup # allocate a big enough string
264
+ r, w = 0, 0
265
+ while r < q.length
266
+ c = q[r]
267
+ case true
268
+ when c == ?\\
283
269
  r += 1
284
- uchar = begin
285
- hexdec4(q[r,4])
286
- rescue RuntimeError => e
287
- raise Error, "invalid escape sequence \\u#{q[r,4]}: #{e}"
270
+ if r >= q.length
271
+ raise Error, "string literal ends with a \"\\\": \"#{q}\""
288
272
  end
289
- r += 4
290
- if surrogate? uchar
291
- if q.length >= r+6
292
- uchar1 = hexdec4(q[r+2,4])
293
- uchar = subst(uchar, uchar1)
294
- if uchar != Ucharerr
295
- # A valid pair; consume.
296
- r += 6
273
+
274
+ case q[r]
275
+ when ?",?\\,?/,?'
276
+ a[w] = q[r]
277
+ r += 1
278
+ w += 1
279
+ when ?b,?f,?n,?r,?t
280
+ a[w] = Unesc[q[r]]
281
+ r += 1
282
+ w += 1
283
+ when ?u
284
+ r += 1
285
+ uchar = begin
286
+ hexdec4(q[r,4])
287
+ rescue RuntimeError => e
288
+ raise Error, "invalid escape sequence \\u#{q[r,4]}: #{e}"
289
+ end
290
+ r += 4
291
+ if surrogate? uchar
292
+ if q.length >= r+6
293
+ uchar1 = hexdec4(q[r+2,4])
294
+ uchar = subst(uchar, uchar1)
295
+ if uchar != Ucharerr
296
+ # A valid pair; consume.
297
+ r += 6
298
+ end
297
299
  end
298
300
  end
301
+ w += ucharenc(a, w, uchar)
302
+ else
303
+ raise Error, "invalid escape char #{q[r]} in \"#{q}\""
299
304
  end
300
- w += ucharenc(a, w, uchar)
305
+ when c == ?", c < Spc
306
+ raise Error, "invalid character in string literal \"#{q}\""
301
307
  else
302
- raise Error, "invalid escape char #{q[r]} in \"#{q}\""
308
+ # Copy anything else byte-for-byte.
309
+ # Valid UTF-8 will remain valid UTF-8.
310
+ # Invalid UTF-8 will remain invalid UTF-8.
311
+ a[w] = c
312
+ r += 1
313
+ w += 1
303
314
  end
304
- when c == ?", c < Spc
305
- raise Error, "invalid character in string literal \"#{q}\""
306
- else
307
- # Copy anything else byte-for-byte.
308
- # Valid UTF-8 will remain valid UTF-8.
309
- # Invalid UTF-8 will remain invalid UTF-8.
310
- a[w] = c
311
- r += 1
312
- w += 1
313
315
  end
316
+ a[0,w]
314
317
  end
315
- a[0,w]
316
- end
317
318
 
318
319
 
319
- # Encodes unicode character u as UTF-8
320
- # bytes in string a at position i.
321
- # Returns the number of bytes written.
322
- def ucharenc(a, i, u)
323
- case true
324
- when u <= Uchar1max
325
- a[i] = (u & 0xff).chr
326
- 1
327
- when u <= Uchar2max
328
- a[i+0] = (Utag2 | ((u>>6)&0xff)).chr
329
- a[i+1] = (Utagx | (u&Umaskx)).chr
330
- 2
331
- when u <= Uchar3max
332
- a[i+0] = (Utag3 | ((u>>12)&0xff)).chr
333
- a[i+1] = (Utagx | ((u>>6)&Umaskx)).chr
334
- a[i+2] = (Utagx | (u&Umaskx)).chr
335
- 3
336
- else
337
- a[i+0] = (Utag4 | ((u>>18)&0xff)).chr
338
- a[i+1] = (Utagx | ((u>>12)&Umaskx)).chr
339
- a[i+2] = (Utagx | ((u>>6)&Umaskx)).chr
340
- a[i+3] = (Utagx | (u&Umaskx)).chr
341
- 4
320
+ # Encodes unicode character u as UTF-8
321
+ # bytes in string a at position i.
322
+ # Returns the number of bytes written.
323
+ def ucharenc(a, i, u)
324
+ case true
325
+ when u <= Uchar1max
326
+ a[i] = (u & 0xff).chr
327
+ 1
328
+ when u <= Uchar2max
329
+ a[i+0] = (Utag2 | ((u>>6)&0xff)).chr
330
+ a[i+1] = (Utagx | (u&Umaskx)).chr
331
+ 2
332
+ when u <= Uchar3max
333
+ a[i+0] = (Utag3 | ((u>>12)&0xff)).chr
334
+ a[i+1] = (Utagx | ((u>>6)&Umaskx)).chr
335
+ a[i+2] = (Utagx | (u&Umaskx)).chr
336
+ 3
337
+ else
338
+ a[i+0] = (Utag4 | ((u>>18)&0xff)).chr
339
+ a[i+1] = (Utagx | ((u>>12)&Umaskx)).chr
340
+ a[i+2] = (Utagx | ((u>>6)&Umaskx)).chr
341
+ a[i+3] = (Utagx | (u&Umaskx)).chr
342
+ 4
343
+ end
342
344
  end
343
- end
344
345
 
345
346
 
346
- def hexdec4(s)
347
- if s.length != 4
348
- raise Error, 'short'
347
+ def hexdec4(s)
348
+ if s.length != 4
349
+ raise Error, 'short'
350
+ end
351
+ (nibble(s[0])<<12) | (nibble(s[1])<<8) | (nibble(s[2])<<4) | nibble(s[3])
349
352
  end
350
- (nibble(s[0])<<12) | (nibble(s[1])<<8) | (nibble(s[2])<<4) | nibble(s[3])
351
- end
352
353
 
353
354
 
354
- def subst(u1, u2)
355
- if Usurr1 <= u1 && u1 < Usurr2 && Usurr2 <= u2 && u2 < Usurr3
356
- return ((u1-Usurr1)<<10) | (u2-Usurr2) + Usurrself
355
+ def subst(u1, u2)
356
+ if Usurr1 <= u1 && u1 < Usurr2 && Usurr2 <= u2 && u2 < Usurr3
357
+ return ((u1-Usurr1)<<10) | (u2-Usurr2) + Usurrself
358
+ end
359
+ return Ucharerr
357
360
  end
358
- return Ucharerr
359
- end
360
361
 
361
362
 
362
- def unsubst(u)
363
- if u < Usurrself || u > Umax || surrogate?(u)
364
- return Ucharerr, Ucharerr
363
+ def unsubst(u)
364
+ if u < Usurrself || u > Umax || surrogate?(u)
365
+ return Ucharerr, Ucharerr
366
+ end
367
+ u -= Usurrself
368
+ [Usurr1 + ((u>>10)&0x3ff), Usurr2 + (u&0x3ff)]
365
369
  end
366
- u -= Usurrself
367
- [Usurr1 + ((u>>10)&0x3ff), Usurr2 + (u&0x3ff)]
368
- end
369
370
 
370
371
 
371
- def surrogate?(u)
372
- Usurr1 <= u && u < Usurr3
373
- end
372
+ def surrogate?(u)
373
+ Usurr1 <= u && u < Usurr3
374
+ end
374
375
 
375
376
 
376
- def nibble(c)
377
- case true
378
- when ?0 <= c && c <= ?9 then c.ord - ?0.ord
379
- when ?a <= c && c <= ?z then c.ord - ?a.ord + 10
380
- when ?A <= c && c <= ?Z then c.ord - ?A.ord + 10
381
- else
382
- raise Error, "invalid hex code #{c}"
377
+ def nibble(c)
378
+ case true
379
+ when ?0 <= c && c <= ?9 then c.ord - ?0.ord
380
+ when ?a <= c && c <= ?z then c.ord - ?a.ord + 10
381
+ when ?A <= c && c <= ?Z then c.ord - ?A.ord + 10
382
+ else
383
+ raise Error, "invalid hex code #{c}"
384
+ end
383
385
  end
384
- end
385
386
 
386
387
 
387
- # Encodes x into a json text. It may contain only
388
- # Array, Hash, String, Numeric, true, false, nil.
389
- # (Note, this list excludes Symbol.)
390
- # X itself must be an Array or a Hash.
391
- # No other value can be encoded, and an error will
392
- # be raised if x contains any other value, such as
393
- # Nan, Infinity, Symbol, and Proc, or if a Hash key
394
- # is not a String.
395
- # Strings contained in x must be valid UTF-8.
396
- def encode(x)
397
- case x
398
- when Hash then objenc(x)
399
- when Array then arrenc(x)
400
- else
401
- raise Error, 'root value must be an Array or a Hash'
388
+ # Encodes x into a json text. It may contain only
389
+ # Array, Hash, String, Numeric, true, false, nil.
390
+ # (Note, this list excludes Symbol.)
391
+ # X itself must be an Array or a Hash.
392
+ # No other value can be encoded, and an error will
393
+ # be raised if x contains any other value, such as
394
+ # Nan, Infinity, Symbol, and Proc, or if a Hash key
395
+ # is not a String.
396
+ # Strings contained in x must be valid UTF-8.
397
+ def encode(x)
398
+ case x
399
+ when Hash then objenc(x)
400
+ when Array then arrenc(x)
401
+ else
402
+ raise Error, 'root value must be an Array or a Hash'
403
+ end
402
404
  end
403
- end
404
405
 
405
406
 
406
- def valenc(x)
407
- case x
408
- when Hash then objenc(x)
409
- when Array then arrenc(x)
410
- when String then strenc(x)
411
- when Numeric then numenc(x)
412
- when true then "true"
413
- when false then "false"
414
- when nil then "null"
415
- else
416
- raise Error, "cannot encode #{x.class}: #{x.inspect}"
407
+ def valenc(x)
408
+ case x
409
+ when Hash then objenc(x)
410
+ when Array then arrenc(x)
411
+ when String then strenc(x)
412
+ when Numeric then numenc(x)
413
+ when true then "true"
414
+ when false then "false"
415
+ when nil then "null"
416
+ else
417
+ if x.respond_to?(:to_json)
418
+ x.to_json
419
+ else
420
+ raise Error, "cannot encode #{x.class}: #{x.inspect}"
421
+ end
422
+ end
417
423
  end
418
- end
419
424
 
420
425
 
421
- def objenc(x)
422
- '{' + x.map{|k,v| keyenc(k) + ':' + valenc(v)}.join(',') + '}'
423
- end
426
+ def objenc(x)
427
+ '{' + x.map{|k,v| keyenc(k) + ':' + valenc(v)}.join(',') + '}'
428
+ end
424
429
 
425
430
 
426
- def arrenc(a)
427
- '[' + a.map{|x| valenc(x)}.join(',') + ']'
428
- end
431
+ def arrenc(a)
432
+ '[' + a.map{|x| valenc(x)}.join(',') + ']'
433
+ end
429
434
 
430
435
 
431
- def keyenc(k)
432
- case k
433
- when String then strenc(k)
434
- else
435
- raise Error, "Hash key is not a string: #{k.inspect}"
436
+ def keyenc(k)
437
+ case k
438
+ when String then strenc(k)
439
+ else
440
+ raise Error, "Hash key is not a string: #{k.inspect}"
441
+ end
436
442
  end
437
- end
438
443
 
439
444
 
440
- def strenc(s)
441
- t = StringIO.new
442
- t.putc(?")
443
- r = 0
444
- while r < s.length
445
- case s[r]
446
- when ?" then t.print('\\"')
447
- when ?\\ then t.print('\\\\')
448
- when ?\b then t.print('\\b')
449
- when ?\f then t.print('\\f')
450
- when ?\n then t.print('\\n')
451
- when ?\r then t.print('\\r')
452
- when ?\t then t.print('\\t')
453
- else
454
- c = s[r]
455
- case true
456
- when Spc <= c && c <= ?~
457
- t.putc(c)
458
- when true
459
- u, size = uchardec(s, r)
460
- r += size - 1 # we add one more at the bottom of the loop
461
- if u < 0x10000
462
- t.print('\\u')
463
- hexenc4(t, u)
445
+ def strenc(s)
446
+ t = StringIO.new
447
+ t.putc(?")
448
+ r = 0
449
+ while r < s.length
450
+ case s[r]
451
+ when ?" then t.print('\\"')
452
+ when ?\\ then t.print('\\\\')
453
+ when ?\b then t.print('\\b')
454
+ when ?\f then t.print('\\f')
455
+ when ?\n then t.print('\\n')
456
+ when ?\r then t.print('\\r')
457
+ when ?\t then t.print('\\t')
458
+ else
459
+ c = s[r]
460
+ case true
461
+ when Spc <= c && c <= ?~
462
+ t.putc(c)
463
+ when true
464
+ u, size = uchardec(s, r)
465
+ r += size - 1 # we add one more at the bottom of the loop
466
+ if u < 0x10000
467
+ t.print('\\u')
468
+ hexenc4(t, u)
469
+ else
470
+ u1, u2 = unsubst(u)
471
+ t.print('\\u')
472
+ hexenc4(t, u1)
473
+ t.print('\\u')
474
+ hexenc4(t, u2)
475
+ end
464
476
  else
465
- u1, u2 = unsubst(u)
466
- t.print('\\u')
467
- hexenc4(t, u1)
468
- t.print('\\u')
469
- hexenc4(t, u2)
477
+ # invalid byte; skip it
470
478
  end
471
- else
472
- # invalid byte; skip it
473
479
  end
480
+ r += 1
474
481
  end
475
- r += 1
482
+ t.putc(?")
483
+ t.string
476
484
  end
477
- t.putc(?")
478
- t.string
479
- end
480
485
 
481
486
 
482
- def hexenc4(t, u)
483
- t.putc(Hex[(u>>12)&0xf])
484
- t.putc(Hex[(u>>8)&0xf])
485
- t.putc(Hex[(u>>4)&0xf])
486
- t.putc(Hex[u&0xf])
487
- end
487
+ def hexenc4(t, u)
488
+ t.putc(Hex[(u>>12)&0xf])
489
+ t.putc(Hex[(u>>8)&0xf])
490
+ t.putc(Hex[(u>>4)&0xf])
491
+ t.putc(Hex[u&0xf])
492
+ end
488
493
 
489
494
 
490
- def numenc(x)
491
- if x.nan? || x.infinite?
492
- return 'null'
493
- end rescue nil
494
- "#{x}"
495
- end
495
+ def numenc(x)
496
+ if x.nan? || x.infinite?
497
+ return 'null'
498
+ end rescue nil
499
+ "#{x}"
500
+ end
496
501
 
497
502
 
498
- # Decodes unicode character u from UTF-8
499
- # bytes in string s at position i.
500
- # Returns u and the number of bytes read.
501
- def uchardec(s, i)
502
- n = s.length - i
503
- return [Ucharerr, 1] if n < 1
503
+ # Decodes unicode character u from UTF-8
504
+ # bytes in string s at position i.
505
+ # Returns u and the number of bytes read.
506
+ def uchardec(s, i)
507
+ n = s.length - i
508
+ return [Ucharerr, 1] if n < 1
504
509
 
505
- c0 = s[i].ord
510
+ c0 = s[i].ord
506
511
 
507
- # 1-byte, 7-bit sequence?
508
- if c0 < Utagx
509
- return [c0, 1]
510
- end
512
+ # 1-byte, 7-bit sequence?
513
+ if c0 < Utagx
514
+ return [c0, 1]
515
+ end
511
516
 
512
- # unexpected continuation byte?
513
- return [Ucharerr, 1] if c0 < Utag2
517
+ # unexpected continuation byte?
518
+ return [Ucharerr, 1] if c0 < Utag2
514
519
 
515
- # need continuation byte
516
- return [Ucharerr, 1] if n < 2
517
- c1 = s[i+1].ord
518
- return [Ucharerr, 1] if c1 < Utagx || Utag2 <= c1
520
+ # need continuation byte
521
+ return [Ucharerr, 1] if n < 2
522
+ c1 = s[i+1].ord
523
+ return [Ucharerr, 1] if c1 < Utagx || Utag2 <= c1
519
524
 
520
- # 2-byte, 11-bit sequence?
521
- if c0 < Utag3
522
- u = (c0&Umask2)<<6 | (c1&Umaskx)
523
- return [Ucharerr, 1] if u <= Uchar1max
524
- return [u, 2]
525
- end
525
+ # 2-byte, 11-bit sequence?
526
+ if c0 < Utag3
527
+ u = (c0&Umask2)<<6 | (c1&Umaskx)
528
+ return [Ucharerr, 1] if u <= Uchar1max
529
+ return [u, 2]
530
+ end
526
531
 
527
- # need second continuation byte
528
- return [Ucharerr, 1] if n < 3
529
- c2 = s[i+2].ord
530
- return [Ucharerr, 1] if c2 < Utagx || Utag2 <= c2
532
+ # need second continuation byte
533
+ return [Ucharerr, 1] if n < 3
534
+ c2 = s[i+2].ord
535
+ return [Ucharerr, 1] if c2 < Utagx || Utag2 <= c2
531
536
 
532
- # 3-byte, 16-bit sequence?
533
- if c0 < Utag4
534
- u = (c0&Umask3)<<12 | (c1&Umaskx)<<6 | (c2&Umaskx)
535
- return [Ucharerr, 1] if u <= Uchar2max
536
- return [u, 3]
537
- end
537
+ # 3-byte, 16-bit sequence?
538
+ if c0 < Utag4
539
+ u = (c0&Umask3)<<12 | (c1&Umaskx)<<6 | (c2&Umaskx)
540
+ return [Ucharerr, 1] if u <= Uchar2max
541
+ return [u, 3]
542
+ end
538
543
 
539
- # need third continuation byte
540
- return [Ucharerr, 1] if n < 4
541
- c3 = s[i+3].ord
542
- return [Ucharerr, 1] if c3 < Utagx || Utag2 <= c3
544
+ # need third continuation byte
545
+ return [Ucharerr, 1] if n < 4
546
+ c3 = s[i+3].ord
547
+ return [Ucharerr, 1] if c3 < Utagx || Utag2 <= c3
543
548
 
544
- # 4-byte, 21-bit sequence?
545
- if c0 < Utag5
546
- u = (c0&Umask4)<<18 | (c1&Umaskx)<<12 | (c2&Umaskx)<<6 | (c3&Umaskx)
547
- return [Ucharerr, 1] if u <= Uchar3max
548
- return [u, 4]
549
- end
549
+ # 4-byte, 21-bit sequence?
550
+ if c0 < Utag5
551
+ u = (c0&Umask4)<<18 | (c1&Umaskx)<<12 | (c2&Umaskx)<<6 | (c3&Umaskx)
552
+ return [Ucharerr, 1] if u <= Uchar3max
553
+ return [u, 4]
554
+ end
550
555
 
551
- return [Ucharerr, 1]
552
- end
556
+ return [Ucharerr, 1]
557
+ end
553
558
 
554
559
 
555
- class Error < ::StandardError
556
- end
560
+ class Error < ::StandardError
561
+ end
557
562
 
558
563
 
559
- Utagx = 0x80 # 1000 0000
560
- Utag2 = 0xc0 # 1100 0000
561
- Utag3 = 0xe0 # 1110 0000
562
- Utag4 = 0xf0 # 1111 0000
563
- Utag5 = 0xF8 # 1111 1000
564
- Umaskx = 0x3f # 0011 1111
565
- Umask2 = 0x1f # 0001 1111
566
- Umask3 = 0x0f # 0000 1111
567
- Umask4 = 0x07 # 0000 0111
568
- Uchar1max = (1<<7) - 1
569
- Uchar2max = (1<<11) - 1
570
- Uchar3max = (1<<16) - 1
571
- Ucharerr = 0xFFFD # unicode "replacement char"
572
- Usurrself = 0x10000
573
- Usurr1 = 0xd800
574
- Usurr2 = 0xdc00
575
- Usurr3 = 0xe000
576
- Umax = 0x10ffff
577
-
578
- Spc = ' '[0]
579
- Unesc = {?b=>?\b, ?f=>?\f, ?n=>?\n, ?r=>?\r, ?t=>?\t}
580
- Hex = '0123456789abcdef'
581
- end
564
+ Utagx = 0x80 # 1000 0000
565
+ Utag2 = 0xc0 # 1100 0000
566
+ Utag3 = 0xe0 # 1110 0000
567
+ Utag4 = 0xf0 # 1111 0000
568
+ Utag5 = 0xF8 # 1111 1000
569
+ Umaskx = 0x3f # 0011 1111
570
+ Umask2 = 0x1f # 0001 1111
571
+ Umask3 = 0x0f # 0000 1111
572
+ Umask4 = 0x07 # 0000 0111
573
+ Uchar1max = (1<<7) - 1
574
+ Uchar2max = (1<<11) - 1
575
+ Uchar3max = (1<<16) - 1
576
+ Ucharerr = 0xFFFD # unicode "replacement char"
577
+ Usurrself = 0x10000
578
+ Usurr1 = 0xd800
579
+ Usurr2 = 0xdc00
580
+ Usurr3 = 0xe000
581
+ Umax = 0x10ffff
582
+
583
+ Spc = ' '[0]
584
+ Unesc = {?b=>?\b, ?f=>?\f, ?n=>?\n, ?r=>?\r, ?t=>?\t}
585
+ Hex = '0123456789abcdef'
586
+ end
587
+ end