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.
- data/.travis.yml +1 -3
- data/Gemfile +1 -2
- data/README.md +47 -23
- data/Rakefile +9 -7
- data/lib/multi_json.rb +15 -4
- data/lib/multi_json/engines/json_common.rb +27 -0
- data/lib/multi_json/engines/json_gem.rb +2 -11
- data/lib/multi_json/engines/json_pure.rb +2 -11
- data/lib/multi_json/engines/ok_json.rb +5 -5
- data/lib/multi_json/engines/yajl.rb +2 -2
- data/lib/multi_json/vendor/ok_json.rb +435 -429
- data/lib/multi_json/version.rb +1 -1
- data/multi_json.gemspec +4 -5
- data/spec/helper.rb +30 -6
- data/spec/multi_json_spec.rb +62 -18
- metadata +63 -67
- data/.gemtest +0 -0
- data/.gitignore +0 -34
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
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
|
-
[](http://travis-ci.org/intridea/multi_json)
|
28
|
+
## <a name="ci">Continuous Integration</a>
|
29
|
+
[](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
|
-
|
51
|
-
features. Before submitting a bug report or feature request, check to make sure
|
52
|
-
been submitted. You can indicate support for an existing
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
data/lib/multi_json.rb
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
module MultiJson
|
2
|
-
class DecodeError < StandardError
|
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
|
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"
|
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.
|
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
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
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
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
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
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
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
|
-
|
94
|
-
|
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
|
-
|
101
|
-
|
102
|
-
|
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
|
-
|
105
|
-
|
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
|
-
|
118
|
-
|
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
|
-
|
137
|
-
|
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
|
-
|
144
|
-
|
145
|
-
|
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
|
-
|
148
|
-
|
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
|
-
|
161
|
-
|
162
|
-
|
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
|
-
|
169
|
-
|
170
|
-
|
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
|
-
|
178
|
-
|
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
|
-
|
183
|
+
ts
|
181
184
|
end
|
182
|
-
ts
|
183
|
-
end
|
184
185
|
|
185
186
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
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
|
-
|
221
|
-
|
222
|
-
|
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
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
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
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
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
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
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
|
-
|
285
|
-
|
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
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
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
|
-
|
305
|
+
when c == ?", c < Spc
|
306
|
+
raise Error, "invalid character in string literal \"#{q}\""
|
301
307
|
else
|
302
|
-
|
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
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
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
|
-
|
347
|
-
|
348
|
-
|
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
|
-
|
355
|
-
|
356
|
-
|
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
|
-
|
363
|
-
|
364
|
-
|
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
|
-
|
372
|
-
|
373
|
-
|
372
|
+
def surrogate?(u)
|
373
|
+
Usurr1 <= u && u < Usurr3
|
374
|
+
end
|
374
375
|
|
375
376
|
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
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
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
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
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
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
|
-
|
422
|
-
|
423
|
-
|
426
|
+
def objenc(x)
|
427
|
+
'{' + x.map{|k,v| keyenc(k) + ':' + valenc(v)}.join(',') + '}'
|
428
|
+
end
|
424
429
|
|
425
430
|
|
426
|
-
|
427
|
-
|
428
|
-
|
431
|
+
def arrenc(a)
|
432
|
+
'[' + a.map{|x| valenc(x)}.join(',') + ']'
|
433
|
+
end
|
429
434
|
|
430
435
|
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
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
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
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
|
-
|
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
|
-
|
482
|
+
t.putc(?")
|
483
|
+
t.string
|
476
484
|
end
|
477
|
-
t.putc(?")
|
478
|
-
t.string
|
479
|
-
end
|
480
485
|
|
481
486
|
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
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
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
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
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
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
|
-
|
510
|
+
c0 = s[i].ord
|
506
511
|
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
512
|
+
# 1-byte, 7-bit sequence?
|
513
|
+
if c0 < Utagx
|
514
|
+
return [c0, 1]
|
515
|
+
end
|
511
516
|
|
512
|
-
|
513
|
-
|
517
|
+
# unexpected continuation byte?
|
518
|
+
return [Ucharerr, 1] if c0 < Utag2
|
514
519
|
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
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
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
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
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
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
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
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
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
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
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
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
|
-
|
552
|
-
|
556
|
+
return [Ucharerr, 1]
|
557
|
+
end
|
553
558
|
|
554
559
|
|
555
|
-
|
556
|
-
|
560
|
+
class Error < ::StandardError
|
561
|
+
end
|
557
562
|
|
558
563
|
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
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
|