qpid_proton 0.18.1 → 0.19.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.
- checksums.yaml +4 -4
- data/ext/cproton/cproton.c +863 -75
- data/lib/codec/data.rb +589 -815
- data/lib/codec/mapping.rb +142 -126
- data/lib/core/condition.rb +89 -0
- data/lib/core/connection.rb +188 -228
- data/lib/core/connection_driver.rb +202 -0
- data/lib/core/container.rb +366 -0
- data/lib/core/delivery.rb +76 -251
- data/lib/core/disposition.rb +21 -35
- data/lib/core/endpoint.rb +21 -53
- data/lib/core/event.rb +156 -0
- data/lib/core/exceptions.rb +109 -106
- data/lib/core/link.rb +24 -49
- data/lib/core/listener.rb +82 -0
- data/lib/core/message.rb +59 -155
- data/lib/core/messaging_handler.rb +190 -0
- data/lib/core/receiver.rb +38 -7
- data/lib/core/sasl.rb +43 -46
- data/lib/core/sender.rb +55 -32
- data/lib/core/session.rb +58 -58
- data/lib/core/ssl.rb +5 -13
- data/lib/core/ssl_details.rb +1 -2
- data/lib/core/ssl_domain.rb +5 -8
- data/lib/core/terminus.rb +62 -30
- data/lib/core/tracker.rb +45 -0
- data/lib/core/transfer.rb +121 -0
- data/lib/core/transport.rb +62 -97
- data/lib/core/uri.rb +73 -0
- data/lib/core/url.rb +11 -7
- data/lib/handler/adapter.rb +78 -0
- data/lib/handler/messaging_adapter.rb +127 -0
- data/lib/handler/messaging_handler.rb +128 -178
- data/lib/handler/reactor_messaging_adapter.rb +158 -0
- data/lib/messenger/messenger.rb +9 -8
- data/lib/messenger/subscription.rb +1 -2
- data/lib/messenger/tracker.rb +1 -2
- data/lib/messenger/tracker_status.rb +1 -2
- data/lib/qpid_proton.rb +36 -66
- data/lib/reactor/container.rb +40 -234
- data/lib/types/array.rb +73 -130
- data/lib/types/described.rb +2 -44
- data/lib/types/hash.rb +19 -56
- data/lib/types/strings.rb +1 -2
- data/lib/types/type.rb +68 -0
- data/lib/util/{handler.rb → deprecation.rb} +22 -15
- data/lib/util/error_handler.rb +4 -25
- data/lib/util/timeout.rb +1 -2
- data/lib/util/version.rb +1 -2
- data/lib/util/wrapper.rb +58 -38
- metadata +16 -33
- data/lib/core/base_handler.rb +0 -31
- data/lib/core/selectable.rb +0 -130
- data/lib/event/collector.rb +0 -148
- data/lib/event/event.rb +0 -318
- data/lib/event/event_base.rb +0 -91
- data/lib/event/event_type.rb +0 -71
- data/lib/handler/acking.rb +0 -70
- data/lib/handler/c_adaptor.rb +0 -47
- data/lib/handler/c_flow_controller.rb +0 -33
- data/lib/handler/endpoint_state_handler.rb +0 -217
- data/lib/handler/incoming_message_handler.rb +0 -74
- data/lib/handler/outgoing_message_handler.rb +0 -100
- data/lib/handler/wrapped_handler.rb +0 -76
- data/lib/reactor/acceptor.rb +0 -41
- data/lib/reactor/backoff.rb +0 -41
- data/lib/reactor/connector.rb +0 -115
- data/lib/reactor/global_overrides.rb +0 -44
- data/lib/reactor/link_option.rb +0 -90
- data/lib/reactor/reactor.rb +0 -196
- data/lib/reactor/session_per_connection.rb +0 -45
- data/lib/reactor/ssl_config.rb +0 -41
- data/lib/reactor/task.rb +0 -39
- data/lib/reactor/urls.rb +0 -45
- data/lib/util/class_wrapper.rb +0 -54
- data/lib/util/condition.rb +0 -47
- data/lib/util/constants.rb +0 -85
- data/lib/util/engine.rb +0 -82
- data/lib/util/reactor.rb +0 -32
- data/lib/util/swig_helper.rb +0 -114
- data/lib/util/uuid.rb +0 -32
data/lib/types/array.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
#--
|
2
1
|
# Licensed to the Apache Software Foundation (ASF) under one
|
3
2
|
# or more contributor license agreements. See the NOTICE file
|
4
3
|
# distributed with this work for additional information
|
@@ -15,158 +14,102 @@
|
|
15
14
|
# KIND, either express or implied. See the License for the
|
16
15
|
# specific language governing permissions and limitations
|
17
16
|
# under the License.
|
18
|
-
|
17
|
+
|
19
18
|
|
20
19
|
#--
|
21
20
|
# Patch the Array class to provide methods for adding its contents
|
22
|
-
# to a Qpid::Proton::Data instance.
|
21
|
+
# to a Qpid::Proton::Codec::Data instance.
|
23
22
|
#++
|
24
23
|
|
25
|
-
module Qpid::Proton
|
26
|
-
|
27
|
-
# Holds the information for an AMQP Array compound type.
|
28
|
-
#
|
29
|
-
# It holds the type for the array and the descriptor if the
|
30
|
-
# array is described.
|
31
|
-
#
|
32
|
-
# @private
|
33
|
-
#
|
34
|
-
class ArrayHeader
|
35
|
-
attr_reader :type
|
36
|
-
attr_reader :descriptor
|
37
|
-
|
38
|
-
def initialize(type, descriptor = nil)
|
39
|
-
@type = type
|
40
|
-
@descriptor = descriptor
|
41
|
-
end
|
42
|
-
|
43
|
-
# Returns true if the array is described.
|
44
|
-
def described?
|
45
|
-
!@descriptor.nil?
|
46
|
-
end
|
24
|
+
module Qpid::Proton
|
25
|
+
module Types
|
47
26
|
|
48
|
-
|
49
|
-
|
27
|
+
# @deprecated use {UniformArray}
|
28
|
+
class ArrayHeader
|
29
|
+
def initialize(type, descriptor = nil)
|
30
|
+
Util::Deprecation.deprecated ArrayHeader, "UniformArray"
|
31
|
+
@type, @descriptor = Codec::Mapping[type], descriptor
|
32
|
+
end
|
33
|
+
attr_reader :type, :descriptor
|
34
|
+
def described?() !@descriptor.nil?; end
|
35
|
+
def <=>(x) [@type, @descriptor] <=> [x.type, x.descriptor]; end
|
36
|
+
include Comparable
|
50
37
|
end
|
51
|
-
end
|
52
38
|
|
53
|
-
|
39
|
+
# *Unsettled API* - An array that is converted to/from an AMQP array of uniform element type.
|
40
|
+
# A plain ruby +::Array+ is converted to/from an AMQP list, which can contain mixed type elements.
|
41
|
+
# If invalid elements are included, then {TypeError} will be raised when encoding to AMQP.
|
42
|
+
class UniformArray < ::Array
|
43
|
+
|
44
|
+
# Construct a uniform array, which will be converted to an AMQP array.
|
45
|
+
# A plain ruby +::Array+ is converted to/from an AMQP list, containing mixed type elements.
|
46
|
+
#
|
47
|
+
# @param type [Type] Elements must be convertible to this AMQP type.
|
48
|
+
# @param elements [Enumerator] Initial elements for the array
|
49
|
+
# @param descriptor [Object] Optional array descriptor
|
50
|
+
def initialize(type, elements=nil, descriptor=nil)
|
51
|
+
@type, @descriptor = type, descriptor
|
52
|
+
raise ArgumentError, "no type specified for array" if @type.nil?
|
53
|
+
super elements if elements
|
54
|
+
end
|
54
55
|
|
55
|
-
# @
|
56
|
-
|
56
|
+
# @deprecated backwards compatibility {UniformArray}
|
57
|
+
def proton_array_header
|
58
|
+
@proton_array_header ||= ArrayHeader.new(@type, @descriptor) # Deprecated
|
59
|
+
end
|
57
60
|
|
58
|
-
|
59
|
-
|
60
|
-
# The value, if defined, is an instance of Qpid::Proton::Types::ArrayHeader
|
61
|
-
attr_accessor :proton_array_header
|
61
|
+
# @return [Type] Array elements must be convertible to this AMQP type
|
62
|
+
attr_reader :type
|
62
63
|
|
63
|
-
|
64
|
-
|
65
|
-
!@proton_array_header.nil? && @proton_array_header.described?
|
66
|
-
end
|
64
|
+
# @return [Object] Optional descriptor.
|
65
|
+
attr_reader :descriptor
|
67
66
|
|
68
|
-
|
69
|
-
def proton_put(data)
|
70
|
-
raise TypeError, "data object cannot be nil" if data.nil?
|
67
|
+
def inspect() "#{self.class.name}<#{type}>#{super}"; end
|
71
68
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
69
|
+
def <=>(x)
|
70
|
+
ret = [@type, @descriptor] <=> [x.type, x.descriptor]
|
71
|
+
ret == 0 ? super : ret
|
72
|
+
end
|
76
73
|
end
|
77
74
|
end
|
75
|
+
end
|
78
76
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
# get the proton type for the element
|
87
|
-
mapping = Qpid::Proton::Codec::Mapping.for_class(element.class)
|
88
|
-
# add the element
|
89
|
-
mapping.put(data, element)
|
90
|
-
end
|
91
|
-
# exit the list
|
92
|
-
data.exit
|
77
|
+
# {Array} is converted to/from an AMQP list, which is allowed to hold mixed-type elements.
|
78
|
+
# Use {UniformArray} to convert/from an AMQP array with uniform element type.
|
79
|
+
class ::Array
|
80
|
+
# @deprecated use {UniformArray}
|
81
|
+
def proton_array_header
|
82
|
+
Qpid::Proton::Util::Deprecation.deprecated __method__, UniformArray
|
83
|
+
@proton_array_header
|
93
84
|
end
|
94
85
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
data.symbol = @proton_array_header.descriptor
|
100
|
-
end
|
101
|
-
|
102
|
-
each do |element|
|
103
|
-
@proton_array_header.type.put(data, element)
|
104
|
-
end
|
105
|
-
|
106
|
-
data.exit
|
86
|
+
# @deprecated use {UniformArray}
|
87
|
+
def proton_array_header=(h)
|
88
|
+
Qpid::Proton::Util::Deprecation.deprecated __method__, UniformArray
|
89
|
+
@proton_array_header= h
|
107
90
|
end
|
108
91
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
raise TypeError, "can't convert nil into Qpid::Proton::Data" if data.nil?
|
115
|
-
|
116
|
-
type = data.type
|
117
|
-
|
118
|
-
if type == Qpid::Proton::Codec::LIST
|
119
|
-
result = proton_get_list(data)
|
120
|
-
elsif type == Qpid::Proton::Codec::ARRAY
|
121
|
-
result = proton_get_array(data)
|
122
|
-
else
|
123
|
-
raise TypeError, "element is not a list and not an array"
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
private
|
128
|
-
|
129
|
-
def proton_get_list(data)
|
130
|
-
size = data.list
|
131
|
-
raise TypeError, "not a list" unless data.enter
|
132
|
-
elements = []
|
133
|
-
(0...size).each do
|
134
|
-
data.next
|
135
|
-
type = data.type
|
136
|
-
raise TypeError, "missing next element in list" unless type
|
137
|
-
elements << type.get(data)
|
138
|
-
end
|
139
|
-
data.exit
|
140
|
-
return elements
|
141
|
-
end
|
142
|
-
|
143
|
-
def proton_get_array(data)
|
144
|
-
count, described, type = data.array
|
145
|
-
|
146
|
-
raise TypeError, "not an array" unless data.enter
|
147
|
-
elements = []
|
148
|
-
|
149
|
-
descriptor = nil
|
150
|
-
|
151
|
-
if described
|
152
|
-
data.next
|
153
|
-
descriptor = data.symbol
|
154
|
-
end
|
92
|
+
# @deprecated use {UniformArray}
|
93
|
+
def proton_described?()
|
94
|
+
Qpid::Proton::Util::Deprecation.deprecated __method__, UniformArray
|
95
|
+
@proton_array_header && @proton_array_header.described?
|
96
|
+
end
|
155
97
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
end
|
165
|
-
data.exit
|
166
|
-
return elements
|
98
|
+
# @deprecated
|
99
|
+
def proton_put(data)
|
100
|
+
Qpid::Proton::Util::Deprecation.deprecated __method__, "Codec::Data#array=, Codec::Data#list="
|
101
|
+
raise TypeError, "nil data" unless data
|
102
|
+
if @proton_array_header && @proton_array_header.type
|
103
|
+
data.array = self
|
104
|
+
else
|
105
|
+
data.list = self
|
167
106
|
end
|
168
|
-
|
169
107
|
end
|
170
108
|
|
109
|
+
# @deprecated
|
110
|
+
def self.proton_get(data)
|
111
|
+
Qpid::Proton::Util::Deprecation.deprecated __method__, "Codec::Data#list"
|
112
|
+
data.list
|
113
|
+
end
|
171
114
|
end
|
172
115
|
|
data/lib/types/described.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
#--
|
2
1
|
# Licensed to the Apache Software Foundation (ASF) under one
|
3
2
|
# or more contributor license agreements. See the NOTICE file
|
4
3
|
# distributed with this work for additional information
|
@@ -15,49 +14,8 @@
|
|
15
14
|
# KIND, either express or implied. See the License for the
|
16
15
|
# specific language governing permissions and limitations
|
17
16
|
# under the License.
|
18
|
-
#++
|
19
17
|
|
20
|
-
module Qpid::Proton::Types
|
21
|
-
|
22
|
-
# @private
|
23
|
-
class Described
|
24
|
-
|
25
|
-
attr_reader :descriptor
|
26
|
-
attr_reader :value
|
27
|
-
|
28
|
-
def initialize(descriptor, value)
|
29
|
-
@descriptor = descriptor
|
30
|
-
@value = value
|
31
|
-
end
|
32
|
-
|
33
|
-
# Puts the description into the Data object.
|
34
|
-
#
|
35
|
-
# ==== Arguments
|
36
|
-
#
|
37
|
-
# * data - the Qpid::Proton::Data instance
|
38
|
-
#
|
39
|
-
# ==== Examples
|
40
|
-
#
|
41
|
-
# described = Qpid::Proton::Described.new("my-descriptor", "the value")
|
42
|
-
# data = Qpid::Proton::Data.new
|
43
|
-
# ...
|
44
|
-
# described.put(data)
|
45
|
-
#
|
46
|
-
def put(data)
|
47
|
-
data.symbol = @descriptor
|
48
|
-
data.string = @value
|
49
|
-
end
|
50
|
-
|
51
|
-
def ==(that) # :nodoc:
|
52
|
-
(that.is_a?(Qpid::Proton::Types::Described) &&
|
53
|
-
(self.descriptor == that.descriptor) &&
|
54
|
-
(self.value == that.value))
|
55
|
-
end
|
56
|
-
|
57
|
-
def to_s # :nodoc:
|
58
|
-
"descriptor=#{descriptor} value=#{value}"
|
59
|
-
end
|
60
|
-
|
61
|
-
end
|
62
18
|
|
19
|
+
module Qpid::Proton::Types
|
20
|
+
Described = Struct.new(:descriptor, :value)
|
63
21
|
end
|
data/lib/types/hash.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
#--
|
2
1
|
# Licensed to the Apache Software Foundation (ASF) under one
|
3
2
|
# or more contributor license agreements. See the NOTICE file
|
4
3
|
# distributed with this work for additional information
|
@@ -15,73 +14,37 @@
|
|
15
14
|
# KIND, either express or implied. See the License for the
|
16
15
|
# specific language governing permissions and limitations
|
17
16
|
# under the License.
|
18
|
-
|
17
|
+
|
19
18
|
|
20
19
|
#--
|
21
20
|
# Patch the Hash class to provide methods for adding its contents
|
22
|
-
# to a Qpid::Proton::Data instance.
|
21
|
+
# to a Qpid::Proton::Codec::Data instance.
|
23
22
|
#++
|
24
23
|
|
25
24
|
# @private
|
26
25
|
class Hash # :nodoc:
|
27
26
|
|
28
|
-
#
|
29
|
-
#
|
30
|
-
# ==== Arguments
|
31
|
-
#
|
32
|
-
# * data - the Qpid::Proton::Data instance
|
33
|
-
#
|
34
|
-
# ==== Examples
|
35
|
-
#
|
36
|
-
# data = Qpid::Proton::Data.new
|
37
|
-
# values = {:foo => :bar}
|
38
|
-
# values.proton_data_put(data)
|
39
|
-
#
|
27
|
+
# @deprecated
|
40
28
|
def proton_data_put(data)
|
41
|
-
|
42
|
-
|
43
|
-
data.put_map
|
44
|
-
data.enter
|
45
|
-
|
46
|
-
each_pair do |key, value|
|
47
|
-
type = Qpid::Proton::Codec::Mapping.for_class(key.class)
|
48
|
-
type.put(data, key)
|
49
|
-
type = Qpid::Proton::Codec::Mapping.for_class(value.class)
|
50
|
-
type.put(data, value)
|
51
|
-
end
|
52
|
-
|
53
|
-
data.exit
|
29
|
+
Qpid::Proton::Util::Deprecation.deprecated(__method__, "Codec::Data#map=")
|
30
|
+
data.map = self
|
54
31
|
end
|
55
32
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
raise TypeError, "element is not a map" unless type == Qpid::Proton::Codec::MAP
|
64
|
-
|
65
|
-
count = data.map
|
66
|
-
result = {}
|
67
|
-
|
68
|
-
data.enter
|
69
|
-
|
70
|
-
(0...(count/2)).each do
|
71
|
-
data.next
|
72
|
-
type = data.type
|
73
|
-
key = type.get(data)
|
74
|
-
data.next
|
75
|
-
type = data.type
|
76
|
-
value = type.get(data)
|
77
|
-
result[key] = value
|
78
|
-
end
|
79
|
-
|
80
|
-
data.exit
|
81
|
-
|
82
|
-
return result
|
83
|
-
end
|
33
|
+
# @deprecated
|
34
|
+
def self.proton_data_get(data)
|
35
|
+
Qpid::Proton::Util::Deprecation.deprecated(__method__, "Codec::Data#map")
|
36
|
+
data.map
|
37
|
+
end
|
38
|
+
end
|
84
39
|
|
40
|
+
module Qpid::Proton::Types
|
41
|
+
# @private
|
42
|
+
def self.symbol_keys(h)
|
43
|
+
h && h.reduce({}) { |x, (k, v)| x[k.to_sym] = v; x }
|
85
44
|
end
|
86
45
|
|
46
|
+
# @private
|
47
|
+
def self.symbol_array(a)
|
48
|
+
a && UniformArray.new(SYMBOL, a.collect { |v| v.to_sym })
|
49
|
+
end
|
87
50
|
end
|
data/lib/types/strings.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
#--
|
2
1
|
# Licensed to the Apache Software Foundation (ASF) under one
|
3
2
|
# or more contributor license agreements. See the NOTICE file
|
4
3
|
# distributed with this work for additional information
|
@@ -15,7 +14,7 @@
|
|
15
14
|
# KIND, either express or implied. See the License for the
|
16
15
|
# specific language governing permissions and limitations
|
17
16
|
# under the License.
|
18
|
-
|
17
|
+
|
19
18
|
|
20
19
|
module Qpid::Proton::Types
|
21
20
|
|
data/lib/types/type.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
#--
|
2
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
3
|
+
# or more contributor license agreements. See the NOTICE file
|
4
|
+
# distributed with this work for additional information
|
5
|
+
# regarding copyright ownership. The ASF licenses this file
|
6
|
+
# to you under the Apache License, Version 2.0 (the
|
7
|
+
# "License"); you may not use this file except in compliance
|
8
|
+
# with the License. You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing,
|
13
|
+
# software distributed under the License is distributed on an
|
14
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
15
|
+
# KIND, either express or implied. See the License for the
|
16
|
+
# specific language governing permissions and limitations
|
17
|
+
# under the License.
|
18
|
+
#++
|
19
|
+
|
20
|
+
module Qpid::Proton
|
21
|
+
module Types
|
22
|
+
|
23
|
+
# Represents an AMQP Type
|
24
|
+
class Type
|
25
|
+
private
|
26
|
+
@@builtin = {}
|
27
|
+
def initialize(code) @code = code; @@builtin[code] = self; end
|
28
|
+
|
29
|
+
public
|
30
|
+
def self.try_convert(code) code.is_a?(Type) ? code : @@builtin[code]; end
|
31
|
+
def self.[](code) try_convert(code) or raise IndexError, "unknown type code #{code}"; end
|
32
|
+
|
33
|
+
attr_reader :code
|
34
|
+
def name() Cproton.pn_type_name(@code); end
|
35
|
+
alias to_s name
|
36
|
+
def <=>(x) @code <=> x; end
|
37
|
+
def hash() @code.hash; end
|
38
|
+
end
|
39
|
+
|
40
|
+
# @!group
|
41
|
+
NULL = Type.new(Cproton::PN_NULL)
|
42
|
+
BOOL = Type.new(Cproton::PN_BOOL)
|
43
|
+
UBYTE = Type.new(Cproton::PN_UBYTE)
|
44
|
+
BYTE = Type.new(Cproton::PN_BYTE)
|
45
|
+
USHORT = Type.new(Cproton::PN_USHORT)
|
46
|
+
SHORT = Type.new(Cproton::PN_SHORT)
|
47
|
+
UINT = Type.new(Cproton::PN_UINT)
|
48
|
+
INT = Type.new(Cproton::PN_INT)
|
49
|
+
CHAR = Type.new(Cproton::PN_CHAR)
|
50
|
+
ULONG = Type.new(Cproton::PN_ULONG)
|
51
|
+
LONG = Type.new(Cproton::PN_LONG)
|
52
|
+
TIMESTAMP = Type.new(Cproton::PN_TIMESTAMP)
|
53
|
+
FLOAT = Type.new(Cproton::PN_FLOAT)
|
54
|
+
DOUBLE = Type.new(Cproton::PN_DOUBLE)
|
55
|
+
DECIMAL32 = Type.new(Cproton::PN_DECIMAL32)
|
56
|
+
DECIMAL64 = Type.new(Cproton::PN_DECIMAL64)
|
57
|
+
DECIMAL128 = Type.new(Cproton::PN_DECIMAL128)
|
58
|
+
UUID = Type.new(Cproton::PN_UUID)
|
59
|
+
BINARY = Type.new(Cproton::PN_BINARY)
|
60
|
+
STRING = Type.new(Cproton::PN_STRING)
|
61
|
+
SYMBOL = Type.new(Cproton::PN_SYMBOL)
|
62
|
+
DESCRIBED = Type.new(Cproton::PN_DESCRIBED)
|
63
|
+
ARRAY = Type.new(Cproton::PN_ARRAY)
|
64
|
+
LIST = Type.new(Cproton::PN_LIST)
|
65
|
+
MAP = Type.new(Cproton::PN_MAP)
|
66
|
+
#@!endgroup
|
67
|
+
end
|
68
|
+
end
|