mrpin-amf 2.1.8
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 +7 -0
- data/.gitignore +9 -0
- data/README.rdoc +52 -0
- data/Rakefile +59 -0
- data/benchmark.rb +86 -0
- data/doc/amf3-speification.pdf +0 -0
- data/ext/rocketamf_ext/class_mapping.c +483 -0
- data/ext/rocketamf_ext/constants.h +52 -0
- data/ext/rocketamf_ext/deserializer.c +776 -0
- data/ext/rocketamf_ext/deserializer.h +28 -0
- data/ext/rocketamf_ext/extconf.rb +18 -0
- data/ext/rocketamf_ext/remoting.c +184 -0
- data/ext/rocketamf_ext/rocketamf_ext.c +38 -0
- data/ext/rocketamf_ext/serializer.c +834 -0
- data/ext/rocketamf_ext/serializer.h +29 -0
- data/ext/rocketamf_ext/utility.h +4 -0
- data/lib/amf/common/hash_with_type.rb +20 -0
- data/lib/amf/ext.rb +22 -0
- data/lib/amf/pure/amf_constants.rb +42 -0
- data/lib/amf/pure/deserializer.rb +354 -0
- data/lib/amf/pure/errors/all_files.rb +2 -0
- data/lib/amf/pure/errors/amf_error.rb +5 -0
- data/lib/amf/pure/errors/amf_error_incomplete.rb +5 -0
- data/lib/amf/pure/helpers/all_files.rb +8 -0
- data/lib/amf/pure/helpers/cache_objects.rb +18 -0
- data/lib/amf/pure/helpers/cache_strings.rb +14 -0
- data/lib/amf/pure/helpers/io_helper_base.rb +19 -0
- data/lib/amf/pure/helpers/io_helper_read.rb +67 -0
- data/lib/amf/pure/helpers/io_helper_write.rb +49 -0
- data/lib/amf/pure/mapping/class_mapper.rb +159 -0
- data/lib/amf/pure/mapping/mapping_set.rb +49 -0
- data/lib/amf/pure/serializer.rb +318 -0
- data/lib/amf/pure.rb +16 -0
- data/lib/amf.rb +140 -0
- data/mrpin-amf.gemspec +24 -0
- data/spec/fixtures/objects/complex/amf3-associative-array.bin +1 -0
- data/spec/fixtures/objects/complex/amf3-byte-array.bin +0 -0
- data/spec/fixtures/objects/complex/amf3-date.bin +0 -0
- data/spec/fixtures/objects/complex/amf3-dictionary.bin +0 -0
- data/spec/fixtures/objects/complex/amf3-dynamic-object.bin +2 -0
- data/spec/fixtures/objects/complex/amf3-empty-array.bin +1 -0
- data/spec/fixtures/objects/complex/amf3-empty-dictionary.bin +0 -0
- data/spec/fixtures/objects/complex/amf3-hash.bin +2 -0
- data/spec/fixtures/objects/complex/amf3-mixed-array.bin +10 -0
- data/spec/fixtures/objects/complex/amf3-primitive-array.bin +1 -0
- data/spec/fixtures/objects/complex/amf3-typed-object.bin +2 -0
- data/spec/fixtures/objects/encoding/amf3-complex-encoded-string-array.bin +1 -0
- data/spec/fixtures/objects/encoding/amf3-encoded-string-ref.bin +0 -0
- data/spec/fixtures/objects/references/amf3-array-ref.bin +1 -0
- data/spec/fixtures/objects/references/amf3-byte-array-ref.bin +1 -0
- data/spec/fixtures/objects/references/amf3-date-ref.bin +0 -0
- data/spec/fixtures/objects/references/amf3-empty-array-ref.bin +1 -0
- data/spec/fixtures/objects/references/amf3-empty-string-ref.bin +1 -0
- data/spec/fixtures/objects/references/amf3-graph-member.bin +0 -0
- data/spec/fixtures/objects/references/amf3-object-ref.bin +0 -0
- data/spec/fixtures/objects/references/amf3-string-ref.bin +0 -0
- data/spec/fixtures/objects/references/amf3-trait-ref.bin +3 -0
- data/spec/fixtures/objects/simple/amf3-0.bin +0 -0
- data/spec/fixtures/objects/simple/amf3-bigNum.bin +0 -0
- data/spec/fixtures/objects/simple/amf3-false.bin +1 -0
- data/spec/fixtures/objects/simple/amf3-float.bin +0 -0
- data/spec/fixtures/objects/simple/amf3-large-max.bin +0 -0
- data/spec/fixtures/objects/simple/amf3-large-min.bin +0 -0
- data/spec/fixtures/objects/simple/amf3-max.bin +1 -0
- data/spec/fixtures/objects/simple/amf3-min.bin +0 -0
- data/spec/fixtures/objects/simple/amf3-null.bin +1 -0
- data/spec/fixtures/objects/simple/amf3-string.bin +1 -0
- data/spec/fixtures/objects/simple/amf3-symbol.bin +1 -0
- data/spec/fixtures/objects/simple/amf3-true.bin +1 -0
- data/spec/helpers/class_mapping_test.rb +4 -0
- data/spec/helpers/class_mapping_test2.rb +3 -0
- data/spec/helpers/fixtures.rb +34 -0
- data/spec/helpers/other_class.rb +4 -0
- data/spec/helpers/ruby_class.rb +4 -0
- data/spec/helpers/test_ruby_class.rb +4 -0
- data/spec/spec-class_mapping.rb +98 -0
- data/spec/spec_deserializer.rb +239 -0
- data/spec/spec_fast_class_mapping.rb +147 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/spec_serializer.rb +267 -0
- metadata +146 -0
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'amf/pure/mapping/mapping_set'
|
2
|
+
|
3
|
+
module AMF
|
4
|
+
|
5
|
+
# Handles class name mapping between other language and ruby and assists in
|
6
|
+
# serializing and deserializing data between them. Simply map an other-language class to a
|
7
|
+
# ruby class and when the object is (de)serialized it will end up as the
|
8
|
+
# appropriate class.
|
9
|
+
#
|
10
|
+
# Example:
|
11
|
+
#
|
12
|
+
# AMF.class_mapper.register_class_alias('SomeClass', 'RubyClass')
|
13
|
+
#
|
14
|
+
# class_map =
|
15
|
+
# {
|
16
|
+
# 'ClassNameLocal0' => 'ClassNameRemote0',
|
17
|
+
# 'ClassNameLocal1' => 'ClassNameRemote1'
|
18
|
+
# }
|
19
|
+
# AMF::Root.register_classes(class_map)
|
20
|
+
|
21
|
+
#
|
22
|
+
class ClassMapper
|
23
|
+
|
24
|
+
class << self
|
25
|
+
|
26
|
+
attr_reader :map
|
27
|
+
|
28
|
+
#
|
29
|
+
# Methods
|
30
|
+
#
|
31
|
+
|
32
|
+
public
|
33
|
+
def initialize
|
34
|
+
@map = MappingSet.new
|
35
|
+
end
|
36
|
+
|
37
|
+
public
|
38
|
+
def register_class_alias(class_local, class_remote)
|
39
|
+
@map.register_class_alias(class_local, class_remote)
|
40
|
+
end
|
41
|
+
|
42
|
+
public
|
43
|
+
def register_classes(map)
|
44
|
+
map.each do |class_local, class_remote|
|
45
|
+
self.register_class_alias(class_local, class_remote)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Reset all class mappings except the defaults
|
50
|
+
public
|
51
|
+
def reset
|
52
|
+
@map = MappingSet.new
|
53
|
+
nil
|
54
|
+
end
|
55
|
+
end #end of static
|
56
|
+
|
57
|
+
# init static
|
58
|
+
self.initialize
|
59
|
+
|
60
|
+
#
|
61
|
+
# Methods
|
62
|
+
#
|
63
|
+
|
64
|
+
public
|
65
|
+
def initialize
|
66
|
+
#cache static variable
|
67
|
+
@map = ClassMapper.map
|
68
|
+
@ignored_props = Object.new.public_methods
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns the other-language class name for the given ruby object.
|
72
|
+
public
|
73
|
+
def get_class_name_remote(object)
|
74
|
+
# Get class name
|
75
|
+
return object.class_type if object.is_a?(HashWithType)
|
76
|
+
return nil if object.is_a?(Hash)
|
77
|
+
|
78
|
+
class_name_local = object.class.name
|
79
|
+
|
80
|
+
# Get mapped remote class name
|
81
|
+
@map.get_class_name_remote(class_name_local)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Instantiates a ruby object using the mapping configuration based on the
|
85
|
+
# source ActionScript class name. If there is no mapping defined, it returns
|
86
|
+
# a <tt>AMF::HashWithType</tt> with the serialized class name.
|
87
|
+
public
|
88
|
+
def create_object(class_name_remote)
|
89
|
+
result = nil
|
90
|
+
|
91
|
+
if class_name_remote.nil? || class_name_remote.empty?
|
92
|
+
return {}
|
93
|
+
end
|
94
|
+
|
95
|
+
class_name_local = @map.get_class_name_local(class_name_remote)
|
96
|
+
|
97
|
+
if class_name_local.nil?
|
98
|
+
# Populate a simple hash, since no mapping
|
99
|
+
result = HashWithType.new(class_name_remote)
|
100
|
+
else
|
101
|
+
ruby_class = class_name_local.split('::').inject(Kernel) { |scope, const_name| scope.const_get(const_name) }
|
102
|
+
result = ruby_class.new
|
103
|
+
end
|
104
|
+
|
105
|
+
result
|
106
|
+
end
|
107
|
+
|
108
|
+
# Populates the ruby object using the given properties. props will be hashes with symbols for keys.
|
109
|
+
public
|
110
|
+
def object_deserialize(target, props)
|
111
|
+
# Don't even bother checking if it responds to setter methods if it's a TypedHash
|
112
|
+
if target.is_a?(HashWithType)
|
113
|
+
target.merge! props
|
114
|
+
return target
|
115
|
+
end
|
116
|
+
|
117
|
+
# Some type of object
|
118
|
+
hash_like = target.respond_to?('[]=')
|
119
|
+
|
120
|
+
props.each do |key, value|
|
121
|
+
if target.respond_to?("#{key}=")
|
122
|
+
target.send("#{key}=", value)
|
123
|
+
elsif hash_like
|
124
|
+
target[key] = value
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
target
|
129
|
+
end
|
130
|
+
|
131
|
+
# Extracts all exportable properties from the given ruby object and returns
|
132
|
+
# them in a hash. If overriding, make sure to return a hash wth string keys
|
133
|
+
# unless you are only going to be using the native C extensions, as the pure
|
134
|
+
# ruby serializer performs a sort on the keys to acheive consistent, testable
|
135
|
+
# results.
|
136
|
+
public
|
137
|
+
def object_serialize(ruby_obj)
|
138
|
+
result = {}
|
139
|
+
|
140
|
+
# Handle hashes
|
141
|
+
if ruby_obj.is_a?(Hash)
|
142
|
+
|
143
|
+
# Stringify keys to make it easier later on and allow sorting
|
144
|
+
ruby_obj.each { |k, v| result[k.to_s] = v }
|
145
|
+
|
146
|
+
else
|
147
|
+
|
148
|
+
# Generic object serializer
|
149
|
+
(ruby_obj.public_methods - @ignored_props).each do |method_name|
|
150
|
+
# Add them to the prop hash if they take no arguments
|
151
|
+
method_def = ruby_obj.method(method_name)
|
152
|
+
result[method_name.to_s] = ruby_obj.send(method_name) if method_def.arity == 0
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
result
|
157
|
+
end # props_for_serialization
|
158
|
+
end #ClassMapping
|
159
|
+
end #AMF
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module AMF
|
2
|
+
|
3
|
+
# Container for all mapped classes
|
4
|
+
class MappingSet
|
5
|
+
|
6
|
+
#
|
7
|
+
# Methods
|
8
|
+
#
|
9
|
+
|
10
|
+
# Creates a mapping set object and populates the default mappings
|
11
|
+
public
|
12
|
+
def initialize
|
13
|
+
@mappings_remote = {}
|
14
|
+
@mappings_local = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
# Map a given other-language class to a ruby class.
|
18
|
+
#
|
19
|
+
# Use fully qualified names for both.
|
20
|
+
#
|
21
|
+
# Example:
|
22
|
+
#
|
23
|
+
# register_class_alias('Example::Date', 'com.example.Date')
|
24
|
+
public
|
25
|
+
def register_class_alias(class_local, class_remote)
|
26
|
+
# Convert params to strings
|
27
|
+
class_remote = class_remote.to_s
|
28
|
+
class_local = class_local.to_s
|
29
|
+
|
30
|
+
@mappings_remote[class_remote] = class_local
|
31
|
+
@mappings_local[class_local] = class_remote
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the ruby class name for the given other-language class name
|
35
|
+
# returning nil if not found
|
36
|
+
public
|
37
|
+
def get_class_name_local(class_name_remote) #:nodoc:
|
38
|
+
@mappings_remote[class_name_remote.to_s]
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns the other-language class name for the given ruby class name,
|
42
|
+
# returning nil if not found
|
43
|
+
public
|
44
|
+
def get_class_name_remote(class_name_local) #:nodoc:
|
45
|
+
@mappings_local[class_name_local.to_s]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,318 @@
|
|
1
|
+
module AMF
|
2
|
+
module Pure
|
3
|
+
# Pure ruby serializer for AMF3
|
4
|
+
class Serializer
|
5
|
+
|
6
|
+
#
|
7
|
+
# Modules
|
8
|
+
#
|
9
|
+
|
10
|
+
private
|
11
|
+
include AMF::Pure::IOHelperWrite
|
12
|
+
include AMF::AMFConstants
|
13
|
+
|
14
|
+
#
|
15
|
+
# Properties
|
16
|
+
#
|
17
|
+
|
18
|
+
#
|
19
|
+
# Methods
|
20
|
+
#
|
21
|
+
|
22
|
+
# Pass in the class mapper instance to use when serializing. This enables
|
23
|
+
# better caching behavior in the class mapper and allows one to change
|
24
|
+
# mappings between serialization attempts.
|
25
|
+
public
|
26
|
+
def initialize(class_mapper)
|
27
|
+
@class_mapper = class_mapper
|
28
|
+
@stream = ''
|
29
|
+
@depth = 0
|
30
|
+
end
|
31
|
+
|
32
|
+
# Serialize the given object using AMF3. Can be called from inside
|
33
|
+
# encode_amf.
|
34
|
+
def serialize(obj)
|
35
|
+
# Initialize caches
|
36
|
+
if @depth == 0
|
37
|
+
@cache_strings = CacheStrings.new
|
38
|
+
@cache_objects = CacheObjects.new
|
39
|
+
@cache_traits = CacheStrings.new
|
40
|
+
end
|
41
|
+
@depth += 1
|
42
|
+
|
43
|
+
# Perform serialization
|
44
|
+
amf3_serialize(obj)
|
45
|
+
|
46
|
+
# Cleanup
|
47
|
+
@depth -= 1
|
48
|
+
|
49
|
+
if @depth == 0
|
50
|
+
@cache_strings = nil
|
51
|
+
@cache_objects = nil
|
52
|
+
@cache_traits = nil
|
53
|
+
end
|
54
|
+
|
55
|
+
@stream
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
def amf3_serialize(object)
|
60
|
+
case true
|
61
|
+
when object.is_a?(NilClass)
|
62
|
+
amf3_write_null
|
63
|
+
when object.respond_to?(:encode_amf)
|
64
|
+
object.encode_amf(self)
|
65
|
+
when object.is_a?(TrueClass)
|
66
|
+
amf3_write_true
|
67
|
+
when object.is_a?(FalseClass)
|
68
|
+
amf3_write_false
|
69
|
+
when object.is_a?(Numeric)
|
70
|
+
amf3_write_numeric(object)
|
71
|
+
when object.is_a?(Symbol), object.is_a?(String)
|
72
|
+
amf3_write_string(object.to_s)
|
73
|
+
when object.is_a?(Time)
|
74
|
+
amf3_write_time object
|
75
|
+
when object.is_a?(Date)
|
76
|
+
amf3_write_date(object)
|
77
|
+
when object.is_a?(StringIO)
|
78
|
+
amf3_write_byte_array(object)
|
79
|
+
when object.is_a?(Array)
|
80
|
+
amf3_write_array(object)
|
81
|
+
when object.is_a?(Hash), object.is_a?(Object)
|
82
|
+
amf3_write_object(object)
|
83
|
+
else
|
84
|
+
raise AMFError, 'unknown type for serialize'
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Helper for writing arrays inside encode_amf.
|
89
|
+
public
|
90
|
+
def write_array(value)
|
91
|
+
amf3_write_array(value)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Helper for writing objects inside encode_amf. If you pass in a property hash, it will use
|
95
|
+
# it rather than having the class mapper determine properties.
|
96
|
+
# You can also specify a traits hash, which can be used to reduce serialized
|
97
|
+
# data size or serialize things as externalizable.
|
98
|
+
public
|
99
|
+
def write_object(obj, props = nil, traits = nil)
|
100
|
+
amf3_write_object(obj, props, traits)
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
def amf3_write_reference(index)
|
105
|
+
header = index << 1 # shift value left to leave a low bit of 0
|
106
|
+
@stream << pack_integer(header)
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
def amf3_write_null
|
111
|
+
# no data is serialized except their type marker
|
112
|
+
@stream << AMF3_MARKER_NULL
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
def amf3_write_true
|
117
|
+
# no data is serialized except their type marker
|
118
|
+
@stream << AMF3_MARKER_TRUE
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
def amf3_write_false
|
123
|
+
# no data is serialized except their type marker
|
124
|
+
@stream << AMF3_MARKER_FALSE
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
def amf3_write_numeric(value)
|
129
|
+
if !value.integer? || value < INTEGER_MIN || value > INTEGER_MAX # Check valid range for 29 bits
|
130
|
+
@stream << AMF3_MARKER_DOUBLE
|
131
|
+
@stream << pack_double(value)
|
132
|
+
else
|
133
|
+
@stream << AMF3_MARKER_INTEGER
|
134
|
+
@stream << pack_integer(value)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
def amf3_write_time(value)
|
140
|
+
@stream << AMF3_MARKER_DATE
|
141
|
+
|
142
|
+
if @cache_objects[value] != nil
|
143
|
+
amf3_write_reference(@cache_objects[value])
|
144
|
+
else
|
145
|
+
# Cache time
|
146
|
+
@cache_objects.add_object(value)
|
147
|
+
|
148
|
+
# Build AMF string
|
149
|
+
value = value.getutc # Dup and convert to UTC
|
150
|
+
milli = (value.to_f * 1000).to_i
|
151
|
+
@stream << AMF3_MARKER_NULL
|
152
|
+
@stream << pack_double(milli)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
private
|
157
|
+
def amf3_write_date(value)
|
158
|
+
@stream << AMF3_MARKER_DATE
|
159
|
+
|
160
|
+
if @cache_objects[value] != nil
|
161
|
+
amf3_write_reference(@cache_objects[value])
|
162
|
+
else
|
163
|
+
# Cache date
|
164
|
+
@cache_objects.add_object(value)
|
165
|
+
|
166
|
+
# Build AMF string
|
167
|
+
@stream << AMF3_MARKER_NULL
|
168
|
+
@stream << pack_double(value.strftime('%Q').to_i)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
private
|
173
|
+
def amf3_write_byte_array(value)
|
174
|
+
@stream << AMF3_MARKER_BYTE_ARRAY
|
175
|
+
|
176
|
+
if @cache_objects[value] != nil
|
177
|
+
amf3_write_reference(@cache_objects[value])
|
178
|
+
else
|
179
|
+
@cache_objects.add_object(value)
|
180
|
+
str = value.string
|
181
|
+
@stream << pack_integer(str.bytesize << 1 | 1)
|
182
|
+
@stream << str
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
private
|
187
|
+
def amf3_write_array(value)
|
188
|
+
# Write type marker
|
189
|
+
@stream << AMF3_MARKER_ARRAY
|
190
|
+
|
191
|
+
# Write reference or cache array
|
192
|
+
if @cache_objects[value] != nil
|
193
|
+
amf3_write_reference(@cache_objects[value])
|
194
|
+
return
|
195
|
+
end
|
196
|
+
|
197
|
+
@cache_objects.add_object(value)
|
198
|
+
|
199
|
+
# Build AMF string for array
|
200
|
+
header = value.length << 1 # make room for a low bit of 1
|
201
|
+
header = header | 1 # set the low bit to 1
|
202
|
+
@stream << pack_integer(header)
|
203
|
+
|
204
|
+
@stream << AMF3_CLOSE_DYNAMIC_ARRAY
|
205
|
+
|
206
|
+
value.each do |elem|
|
207
|
+
amf3_serialize elem
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
private
|
212
|
+
def amf3_write_object(value, properties = nil, traits = nil)
|
213
|
+
@stream << AMF3_MARKER_OBJECT
|
214
|
+
|
215
|
+
# Caching...
|
216
|
+
if @cache_objects[value] != nil
|
217
|
+
amf3_write_reference(@cache_objects[value])
|
218
|
+
return
|
219
|
+
end
|
220
|
+
|
221
|
+
@cache_objects.add_object(value)
|
222
|
+
|
223
|
+
# Calculate traits if not given
|
224
|
+
use_default_class_name = false
|
225
|
+
|
226
|
+
if traits.nil?
|
227
|
+
traits =
|
228
|
+
{
|
229
|
+
class_name: @class_mapper.get_class_name_remote(value),
|
230
|
+
members: [],
|
231
|
+
dynamic: true
|
232
|
+
}
|
233
|
+
use_default_class_name = true unless traits[:class_name]
|
234
|
+
end
|
235
|
+
|
236
|
+
class_name = use_default_class_name ? '__default__' : traits[:class_name]
|
237
|
+
|
238
|
+
# Write out traits
|
239
|
+
if !class_name.nil? && @cache_traits[class_name] != nil
|
240
|
+
@stream << pack_integer(@cache_traits[class_name] << 2 | 0x01)
|
241
|
+
else
|
242
|
+
@cache_traits.add_string(class_name) unless class_name.nil?
|
243
|
+
|
244
|
+
# Write out trait header
|
245
|
+
header = 0x03 # Not object ref and not trait ref
|
246
|
+
header |= 0x02 << 2 if traits[:dynamic]
|
247
|
+
header |= traits[:members].length << 4
|
248
|
+
@stream << pack_integer(header)
|
249
|
+
|
250
|
+
# Write out class name
|
251
|
+
if use_default_class_name
|
252
|
+
amf3_write_string_internal('')
|
253
|
+
else
|
254
|
+
amf3_write_string_internal(class_name.to_s)
|
255
|
+
end
|
256
|
+
|
257
|
+
# Write out members
|
258
|
+
traits[:members].each { |m| amf3_write_string_internal(m) }
|
259
|
+
end
|
260
|
+
|
261
|
+
# Extract properties if not given
|
262
|
+
properties = @class_mapper.object_serialize(value) if properties.nil?
|
263
|
+
|
264
|
+
# Write out sealed properties
|
265
|
+
traits[:members].each do |m|
|
266
|
+
amf3_serialize(properties[m])
|
267
|
+
properties.delete(m)
|
268
|
+
end
|
269
|
+
|
270
|
+
# Write out dynamic properties
|
271
|
+
if traits[:dynamic]
|
272
|
+
# Write out dynamic properties
|
273
|
+
properties.each do |key, val|
|
274
|
+
amf3_write_string_internal(key.to_s)
|
275
|
+
amf3_serialize(val)
|
276
|
+
end
|
277
|
+
|
278
|
+
# Write close
|
279
|
+
@stream << AMF3_CLOSE_DYNAMIC_OBJECT
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
private
|
284
|
+
def amf3_write_string(value)
|
285
|
+
@stream << AMF3_MARKER_STRING
|
286
|
+
amf3_write_string_internal value
|
287
|
+
end
|
288
|
+
|
289
|
+
|
290
|
+
private
|
291
|
+
def amf3_write_string_internal(value)
|
292
|
+
if value.respond_to?(:encode)
|
293
|
+
value = value.dup if value.frozen?
|
294
|
+
|
295
|
+
value = value.encode('UTF-8')
|
296
|
+
|
297
|
+
value.force_encoding('ASCII-8BIT')
|
298
|
+
end
|
299
|
+
|
300
|
+
if value == ''
|
301
|
+
@stream << AMF3_EMPTY_STRING
|
302
|
+
elsif @cache_strings[value] != nil
|
303
|
+
amf3_write_reference(@cache_strings[value])
|
304
|
+
else
|
305
|
+
|
306
|
+
# Cache string
|
307
|
+
@cache_strings.add_string(value)
|
308
|
+
|
309
|
+
# Build AMF string
|
310
|
+
@stream << pack_integer(value.bytesize << 1 | 1)
|
311
|
+
@stream << value
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
end # Serializer
|
316
|
+
|
317
|
+
end #Pure
|
318
|
+
end #AMF
|
data/lib/amf/pure.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'amf/pure/deserializer'
|
2
|
+
require 'amf/pure/serializer'
|
3
|
+
|
4
|
+
module AMF
|
5
|
+
# This module holds all the modules/classes that implement AMF's functionality
|
6
|
+
# in pure ruby
|
7
|
+
module Pure
|
8
|
+
$DEBUG and warn 'Using pure library for AMF.'
|
9
|
+
end
|
10
|
+
|
11
|
+
#:stopdoc:
|
12
|
+
# Import serializer/deserializer
|
13
|
+
Deserializer = AMF::Pure::Deserializer
|
14
|
+
Serializer = AMF::Pure::Serializer
|
15
|
+
#:startdoc:
|
16
|
+
end
|
data/lib/amf.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
2
|
+
$:.unshift "#{File.expand_path(File.dirname(__FILE__))}/tamf/"
|
3
|
+
$:.unshift "#{File.expand_path(File.dirname(__FILE__))}/amf/types/"
|
4
|
+
|
5
|
+
require 'date'
|
6
|
+
require 'stringio'
|
7
|
+
require 'amf/common/hash_with_type'
|
8
|
+
require 'amf/pure/errors/all_files'
|
9
|
+
require 'amf/pure/helpers/all_files'
|
10
|
+
require 'amf/pure/mapping/class_mapper'
|
11
|
+
|
12
|
+
|
13
|
+
#todo: implement C version
|
14
|
+
# begin
|
15
|
+
# require 'rocketamf/ext'
|
16
|
+
# rescue LoadError
|
17
|
+
|
18
|
+
require 'amf/pure'
|
19
|
+
# end
|
20
|
+
|
21
|
+
# AMF is a full featured AMF3 serializer/deserializer with support for
|
22
|
+
# bi-directional other language to ruby class mapping, custom serialization and mapping,
|
23
|
+
# remoting gateway helpers that follow AMF3 messaging specs, and a suite of specs
|
24
|
+
# to ensure adherence to the specification documents put out by Adobe. If the C
|
25
|
+
# components compile, then RocketAMF automatically takes advantage of them to
|
26
|
+
# provide a substantial performance benefit. In addition, RocketAMF is fully
|
27
|
+
# compatible with Ruby 2.0, 2.1.
|
28
|
+
#
|
29
|
+
# == Performance
|
30
|
+
# # 100.000 objects
|
31
|
+
# # Ruby 2.0
|
32
|
+
# Testing pure AMF3:
|
33
|
+
# minimum serialize time: 49.294496s
|
34
|
+
# minimum deserialize time: 6.600238s
|
35
|
+
#
|
36
|
+
# == Example
|
37
|
+
#
|
38
|
+
#
|
39
|
+
# test_object =
|
40
|
+
# {
|
41
|
+
# first_name: "Greg",
|
42
|
+
# last_name: "House"
|
43
|
+
# }
|
44
|
+
#
|
45
|
+
# data = AMF::Root.serialize(test_object)
|
46
|
+
#
|
47
|
+
# restored_object = AMF::Root.serialize(data)
|
48
|
+
#
|
49
|
+
# == Mapping Classes Between Other language and Ruby
|
50
|
+
#
|
51
|
+
# RocketAMF provides a simple class mapping tool to facilitate serialization and
|
52
|
+
# deserialization of typed objects. Refer to the documentation of
|
53
|
+
# <tt>RocketAMF::ClassMapping</tt> for more details. If the provided class
|
54
|
+
# mapping tool is not sufficient for your needs, you also have the option to
|
55
|
+
# replace it with a class mapper of your own devising that matches the documented
|
56
|
+
# API.
|
57
|
+
#
|
58
|
+
# == Advanced Serialization (encode_amf and IExternalizable)
|
59
|
+
#
|
60
|
+
# RocketAMF provides some additional functionality to support advanced
|
61
|
+
# serialization techniques. If you define an <tt>encode_amf</tt> method on your
|
62
|
+
# object, it will get called during serialization. It is passed a single argument,
|
63
|
+
# the serializer, and it can use the serializer stream, the <tt>serialize</tt>
|
64
|
+
# method, the <tt>write_array</tt> method, the <tt>write_object</tt> method, and
|
65
|
+
# the serializer version. Below is a simple example that uses <tt>write_object</tt>
|
66
|
+
# to customize the property hash that is used for serialization.
|
67
|
+
#
|
68
|
+
# Example:
|
69
|
+
#
|
70
|
+
# class TestObject
|
71
|
+
# def encode_amf serializer
|
72
|
+
# serializer.write_object self, @attributes
|
73
|
+
# end
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
# If you plan on using the <tt>serialize</tt> method, make sure to pass in the
|
77
|
+
# current serializer version, or you could create a message that cannot be deserialized.
|
78
|
+
#
|
79
|
+
# Example:
|
80
|
+
#
|
81
|
+
# class VariableObject
|
82
|
+
# def encode_amf serializer
|
83
|
+
# serializer.serialize(false)
|
84
|
+
# end
|
85
|
+
# end
|
86
|
+
#
|
87
|
+
module AMF
|
88
|
+
|
89
|
+
class Root
|
90
|
+
# Deserialize the AMF string _source_ of the given AMF version into a Ruby
|
91
|
+
# data structure and return it. Creates an instance of <tt>AMF::Deserializer</tt>
|
92
|
+
# with a new instance of <tt>AMF::Root.class_mapper</tt> and calls deserialize
|
93
|
+
# on it with the given source, returning the result.
|
94
|
+
public
|
95
|
+
def self.deserialize(source)
|
96
|
+
deserializer = AMF::Deserializer.new(@class_mapper.new)
|
97
|
+
deserializer.deserialize(source)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Serialize the given Ruby data structure _obj_ into an AMF3 stream.
|
101
|
+
# Creates an instance of <tt>AMF::Serializer</tt>
|
102
|
+
# with a new instance of <tt>AMF::Root.class_mapper</tt> and calls serialize
|
103
|
+
# on it with the given object, returning the result.
|
104
|
+
public
|
105
|
+
def self.serialize(obj)
|
106
|
+
serializer = AMF::Serializer.new(@class_mapper.new)
|
107
|
+
serializer.serialize(obj)
|
108
|
+
end
|
109
|
+
|
110
|
+
public
|
111
|
+
def self.register_class_alias(class_name_local, class_name_remote)
|
112
|
+
@class_mapper.register_class_alias(class_name_local, class_name_remote)
|
113
|
+
end
|
114
|
+
|
115
|
+
public
|
116
|
+
def self.register_classes(map)
|
117
|
+
@class_mapper.register_classes(map)
|
118
|
+
end
|
119
|
+
|
120
|
+
public
|
121
|
+
def self.clear_class_aliases
|
122
|
+
@class_mapper.reset
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
def self.init_class_mapper
|
127
|
+
#todo: use c version
|
128
|
+
# Activating the C Class Mapper:
|
129
|
+
# require 'rubygems'
|
130
|
+
# require 'amf'
|
131
|
+
# AMF::ClassMapper = AMF::Ext::FastClassMapping
|
132
|
+
|
133
|
+
@class_mapper = AMF::ClassMapper
|
134
|
+
end
|
135
|
+
|
136
|
+
init_class_mapper
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
data/mrpin-amf.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = 'mrpin-amf'
|
5
|
+
spec.version = '2.1.8'
|
6
|
+
spec.platform = Gem::Platform::RUBY
|
7
|
+
spec.authors = ['Jacob Henry', 'Stephen Augenstein', "Joc O'Connor", 'Gregory Tkach']
|
8
|
+
spec.email = %w(gregory.tkach@gmail.com)
|
9
|
+
spec.homepage = 'https://github.com/mrpin/mrpin-amf-ruby'
|
10
|
+
spec.license = 'MIT'
|
11
|
+
spec.summary = 'Fast AMF3 serializer/deserializer'
|
12
|
+
spec.description = 'Fast AMF3 serializer/deserializer with remoting request/response wrappers to simplify integration'
|
13
|
+
|
14
|
+
spec.files = `git ls-files`.split($/)
|
15
|
+
spec.test_files = Dir[*['spec/**/*_spec.rb']]
|
16
|
+
spec.extensions = Dir[*['ext/**/extconf.rb']]
|
17
|
+
spec.require_paths = ['lib']
|
18
|
+
|
19
|
+
spec.add_development_dependency 'rake-compiler', '~> 0'
|
20
|
+
|
21
|
+
spec.has_rdoc = true
|
22
|
+
spec.extra_rdoc_files = %w( README.rdoc )
|
23
|
+
spec.rdoc_options = %w(--line-numbers --main README.rdoc)
|
24
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
asdf fdsafoobar42 bar1 bar2 bar3
|
Binary file
|