revent 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,184 @@
1
+ module RubyAMF
2
+ module IO
3
+ class AMFSerializer
4
+
5
+ require "#{File.dirname(__FILE__)}/read_write"
6
+
7
+ include RubyAMF::Configuration
8
+ include RubyAMF::IO::BinaryWriter
9
+ include RubyAMF::IO::Constants
10
+ include RubyAMF::VoHelper
11
+
12
+ attr_accessor :stream
13
+
14
+ def initialize
15
+ reset
16
+ end
17
+
18
+ def reset
19
+ @stream = ''
20
+ reset_referencables
21
+ end
22
+
23
+ def reset_referencables
24
+ @stored_strings = {} # hash is way faster than array
25
+ @stored_strings[""] = true # add this in automatically
26
+ @write_amf3_integer_results = {} # cache the integers
27
+ @current_strings_index = 0
28
+ end
29
+
30
+ def write_amf3(value)
31
+ if !value
32
+ @stream << "\001" # represents an amf3 null
33
+
34
+ elsif (value.is_a?(TrueClass) || value.is_a?(FalseClass))
35
+ value ? (@stream << "\003") : (@stream << "\002") # represents an amf3 true and false
36
+
37
+ elsif value.is_a?(Numeric)
38
+ if value.is_a?(Integer) # Aryk: This was also incorrect before because you has Bignum check AFTER the Integer check, which means the Bignum's were getting picked up by Integers
39
+ if value.is_a?(Bignum)
40
+ @stream << "\005" # represents an amf3 complex number
41
+ write_double(value)
42
+ else
43
+ write_amf3_number(value)
44
+ end
45
+ elsif(value.is_a?(Float))
46
+ @stream << "\005" # represents an amf3 complex number
47
+ write_double(value)
48
+ elsif value.is_a?(BigDecimal) # Aryk: BigDecimal does not relate to Float, so keep it as a seperate check.
49
+ # TODO: Aryk: Not quite sure why you do value.to_s.to_f? can't you just do value.to_f?
50
+ value = value.to_s('F').to_f #this is turning a string into a Ruby Float, but because there are no further operations on it it is safe
51
+ @stream << "\005" # represents an amf3 complex number
52
+ write_double(value)
53
+ end
54
+
55
+ elsif(value.is_a?(String))
56
+ @stream << "\006" # represents an amf3 string
57
+ write_amf3_string(value)
58
+
59
+ elsif(value.is_a?(Array))
60
+ write_amf3_array(value)
61
+
62
+ elsif(value.is_a?(Hash))
63
+ write_amf3_object(value)
64
+
65
+ elsif (value.is_a?(Time)||value.is_a?(Date))
66
+ @stream << "\b" # represents an amf3 date
67
+ write_amf3_date(value)
68
+
69
+ # I know we can combine this with the last condition, but don't ; the Rexml and Beautiful Soup test is expensive, and for large record sets with many AR its better to be able to skip the next step
70
+ elsif value.is_a?(ActiveRecord::Base) # Aryk: this way, we can bypass the "['REXML::Document', 'BeautifulSoup'].include?(value.class.to_s) " operation
71
+ write_amf3_object(VoUtil.get_vo_hash_for_outgoing(value))
72
+
73
+ elsif ['REXML::Document', 'BeautifulSoup'].include?(value.class.to_s)
74
+ write_byte(AMF3_XML)
75
+ write_amf3_xml(value)
76
+
77
+ elsif value.is_a?(Object)
78
+ write_amf3_object(VoUtil.get_vo_hash_for_outgoing(value) )
79
+ end
80
+ end
81
+
82
+ def write_amf3_integer(int)
83
+ @stream << (@write_amf3_integer_results[int] ||= (
84
+ int = int & 0x1fffffff
85
+ if(int < 0x80)
86
+ [int].pack('c')
87
+ elsif(int < 0x4000)
88
+ [int >> 7 & 0x7f | 0x80].pack('c')+
89
+ [int & 0x7f].pack('c')
90
+ elsif(int < 0x200000)
91
+ [int >> 14 & 0x7f | 0x80].pack('c')+
92
+ [int >> 7 & 0x7f | 0x80].pack('c')+
93
+ [int & 0x7f].pack('c')
94
+ else
95
+ [int >> 22 & 0x7f | 0x80].pack('c')+
96
+ [int >> 15 & 0x7f | 0x80].pack('c')+
97
+ [int >> 8 & 0x7f | 0x80].pack('c')+
98
+ [int & 0xff].pack('c')
99
+ end
100
+ ))
101
+ end
102
+
103
+ def write_amf3_number(number)
104
+ if(number >= AMF3_INTEGER_MIN && number <= AMF3_INTEGER_MAX) #check valid range for 29bits
105
+ @stream << "\004" # represents an amf3 integer
106
+ write_amf3_integer(number)
107
+ else #overflow condition otherwise
108
+ @stream << "\005" # represents an amf3 complex number
109
+ write_double(number)
110
+ end
111
+ end
112
+
113
+ def write_amf3_string(string)
114
+ if index = @stored_strings[string]
115
+ if string == "" # store this initially so it gets caught by the stored_strings check
116
+ @stream << "\001" # represents an amf3 empty string
117
+ else
118
+ reference = index << 1
119
+ write_amf3_integer(reference)
120
+ end
121
+ else
122
+ @stored_strings[string] = @current_strings_index
123
+ @current_strings_index += 1 # increment the index
124
+ reference = string.length
125
+ reference = reference << 1
126
+ reference = reference | 1
127
+ write_amf3_integer(reference)
128
+ writen(string)
129
+ end
130
+ end
131
+
132
+ def write_amf3_object(hash)
133
+ not_vo_hash = !hash.is_a?(VoHash) # is this not a vohash - then doesnt have an _explicitType parameter
134
+ @stream << "\n\v" # represents an amf3 object and dynamic object
135
+ not_vo_hash || !hash._explicitType ? (@stream << "\001") : write_amf3_string(hash._explicitType)
136
+ hash.each do |attr, value| # Aryk: no need to remove any "_explicitType" or "rmember" key since they werent added as keys
137
+ if not_vo_hash # then that means that the attr might not be symbols and it hasn't gone through camelizing if thats needed
138
+ attr = attr.to_s.dup # need this just in case its frozen
139
+ attr.to_camel! if ClassMappings.translate_case
140
+ end
141
+ write_amf3_string(attr)
142
+ value ? write_amf3(value) : (@stream << "\001") # represents an amf3 null
143
+ end
144
+ @stream << "\001" # represents an amf3 empty string #close open object
145
+ end
146
+
147
+ def write_amf3_array(array)
148
+ num_objects = array.length * 2 + 1
149
+ if ClassMappings.use_array_collection
150
+ @stream << "\n\a" # AMF3_OBJECT and AMF3_XML
151
+ write_amf3_string("flex.messaging.io.ArrayCollection")
152
+ end
153
+ @stream << "\t" # represents an amf3 array
154
+ write_amf3_integer(num_objects)
155
+ @stream << "\001" # represents an amf3 empty string #write empty for string keyed elements here, as it's never allowed from ruby
156
+ array.each{|v| write_amf3(v) }
157
+ end
158
+
159
+ def write_amf3_date(datetime) # Aryk: Dates will almost never be the same, so turn off the storing_objects
160
+ write_amf3_integer(1)
161
+ seconds = if datetime.is_a?(Time)
162
+ datetime.utc unless datetime.utc?
163
+ datetime.to_f
164
+ elsif datetime.is_a?(Date) # this also handles the case for DateTime
165
+ datetime.strftime("%s").to_i
166
+ # datetime = Time.gm( datetime.year, datetime.month, datetime.day )
167
+ # datetime = Time.gm( datetime.year, datetime.month, datetime.day, datetime.hour, datetime.min, datetime.sec )
168
+ end
169
+ write_double( (seconds*1000).to_i ) # used to be total_milliseconds = datetime.to_i * 1000 + ( datetime.usec/1000 )
170
+ end
171
+
172
+ def write_amf3_xml(value)
173
+ xml = value.to_s
174
+ a = xml.strip
175
+ if(a != nil)
176
+ b = a.gsub!(/\>(\n|\r|\r\n| |\t)*\</,'><') #clean whitespace
177
+ else
178
+ b = xml.gsub!(/\>(\n|\r|\r\n| |\t)*\</,'><') #clean whitespace
179
+ end
180
+ write_amf3_string(b)
181
+ end
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,308 @@
1
+ begin
2
+ module RubyAMF
3
+ module IO
4
+ module Constants
5
+ AMF3_TYPE = 0x11
6
+ AMF3_UNDEFINED = 0x00
7
+ AMF3_NULL = 0x01
8
+ AMF3_FALSE = 0x02
9
+ AMF3_TRUE = 0x03
10
+ AMF3_INTEGER = 0x04
11
+ AMF3_NUMBER = 0x05
12
+ AMF3_STRING = 0x06
13
+ AMF3_XML = 0x07
14
+ AMF3_DATE = 0x08
15
+ AMF3_ARRAY = 0x09
16
+ AMF3_OBJECT = 0x0A
17
+ AMF3_XML_STRING = 0x0B
18
+ AMF3_BYTE_ARRAY = 0x0C
19
+ AMF3_INTEGER_MAX = 268435455
20
+ AMF3_INTEGER_MIN = -268435456
21
+ end
22
+ module BinaryReader
23
+
24
+ Native = :Native
25
+ Big = BigEndian = Network = :BigEndian
26
+ Little = LittleEndian = :LittleEndian
27
+
28
+ #examines the locale byte order on the running machine
29
+ def byte_order
30
+ if [0x12345678].pack("L") == "\x12\x34\x56\x78"
31
+ :BigEndian
32
+ else
33
+ :LittleEndian
34
+ end
35
+ end
36
+
37
+ def byte_order_little?
38
+ (byte_order == :LittleEndian) ? true : false;
39
+ end
40
+
41
+ def byte_order_big?
42
+ (byte_order == :BigEndian) ? true : false;
43
+ end
44
+ alias :byte_order_network? :byte_order_big?
45
+
46
+ #read N length from stream starting at position
47
+ def readn(length)
48
+ self.stream_position ||= 0
49
+ str = self.stream[self.stream_position, length]
50
+ self.stream_position += length
51
+ str
52
+ end
53
+
54
+ #reada a boolean
55
+ def read_boolean
56
+ d = self.stream[self.stream_position,1].unpack('c').first
57
+ self.stream_position += 1
58
+ (d == 1) ? true : false;
59
+ end
60
+
61
+ #8bits no byte order
62
+ def read_int8
63
+ d = self.stream[self.stream_position,1].unpack('c').first
64
+ self.stream_position += 1
65
+ d
66
+ end
67
+ alias :read_byte :read_int8
68
+
69
+ # Aryk: TODO: This needs to be written more cleanly. Using rescue and then regex checks on top of that slows things down
70
+ def read_word8
71
+ begin
72
+ d = self.stream[self.stream_position,1].unpack('C').first
73
+ self.stream_position += 1
74
+ d
75
+ rescue Exception => e
76
+ #this handles an exception condition when Rails'
77
+ #ActionPack strips off the last "\000" of the AMF stream
78
+ self.stream_position += 1
79
+ return 0
80
+ end
81
+ end
82
+
83
+ #16 bits Unsigned
84
+ def read_word16_native
85
+ d = self.stream[self.stream_position,2].unpack('S').first
86
+ self.stream_position += 2
87
+ d
88
+ end
89
+
90
+ def read_word16_little
91
+ d = self.stream[self.stream_position,2].unpack('v').first
92
+ self.stream_position += 2
93
+ d
94
+ end
95
+
96
+ def read_word16_network
97
+ d = self.stream[self.stream_position,2].unpack('n').first
98
+ self.stream_position += 2
99
+ d
100
+ end
101
+
102
+ #16 bits Signed
103
+ def read_int16_native
104
+ str = self.readn(2).unpack('s').first
105
+ end
106
+
107
+ def read_int16_little
108
+ str = self.readn(2)
109
+ str.reverse! if byte_order_network? # swap bytes as native=network (and we want little)
110
+ str.unpack('s').first
111
+ end
112
+
113
+ def read_int16_network
114
+ str = self.readn(2)
115
+ str.reverse! if byte_order_little? # swap bytes as native=little (and we want network)
116
+ str.unpack('s').first
117
+ end
118
+
119
+ #32 bits unsigned
120
+ def read_word32_native
121
+ d = self.stream[self.stream_position,4].unpack('L').first
122
+ self.stream_position += 4
123
+ d
124
+ end
125
+
126
+ def read_word32_little
127
+ d = self.stream[self.stream_position,4].unpack('V').first
128
+ self.stream_position += 4
129
+ d
130
+ end
131
+
132
+ def read_word32_network
133
+ d = self.stream[self.stream_position,4].unpack('N').first
134
+ self.stream_position += 4
135
+ d
136
+ end
137
+
138
+ #32 bits signed
139
+ def read_int32_native
140
+ d = self.stream[self.stream_position,4].unpack('l').first
141
+ self.stream_position += 4
142
+ d
143
+ end
144
+
145
+ def read_int32_little
146
+ str = readn(4)
147
+ str.reverse! if byte_order_network? # swap bytes as native=network (and we want little)
148
+ str.unpack('l').first
149
+ end
150
+
151
+ def read_int32_network
152
+ str = readn(4)
153
+ str.reverse! if byte_order_little? # swap bytes as native=little (and we want network)
154
+ str.unpack('l').first
155
+ end
156
+
157
+
158
+ #UTF string
159
+ def read_utf
160
+ length = self.read_word16_network
161
+ readn(length)
162
+ end
163
+
164
+ def read_int32_network
165
+ str = self.readn(4)
166
+ str.reverse! if byte_order_little? # swap bytes as native=little (and we want network)
167
+ str.unpack('l').first
168
+ end
169
+
170
+ def read_double
171
+ d = self.stream[self.stream_position,8].unpack('G').first
172
+ self.stream_position += 8
173
+ d
174
+ end
175
+
176
+ def read_long_utf(length)
177
+ length = read_word32_network #get the length of the string (1st 4 bytes)
178
+ self.readn(length) #read length number of bytes
179
+ end
180
+ end
181
+
182
+
183
+ module BinaryWriter
184
+
185
+ #examines the locale byte order on the running machine
186
+ def byte_order
187
+ if [0x12345678].pack("L") == "\x12\x34\x56\x78"
188
+ :BigEndian
189
+ else
190
+ :LittleEndian
191
+ end
192
+ end
193
+
194
+ def byte_order_little?
195
+ (byte_order == :LittleEndian) ? true : false;
196
+ end
197
+
198
+ def byte_order_big?
199
+ (byte_order == :BigEndian) ? true : false;
200
+ end
201
+ alias :byte_order_network? :byte_order_big?
202
+
203
+ def writen(val)
204
+ @stream << val
205
+ end
206
+
207
+ #8 bit no byteorder
208
+ def write_word8(val)
209
+ self.stream << [val].pack('C')
210
+ end
211
+
212
+ def write_int8(val)
213
+ self.stream << [val].pack('c')
214
+ end
215
+
216
+ #16 bit unsigned
217
+ def write_word16_native(val)
218
+ self.stream << [val].pack('S')
219
+ end
220
+
221
+ def write_word16_little(val)
222
+ str = [val].pack('S')
223
+ str.reverse! if byte_order_network? # swap bytes as native=network (and we want little)
224
+ self.stream << str
225
+ end
226
+
227
+ def write_word16_network(val)
228
+ str = [val].pack('S')
229
+ str.reverse! if byte_order_little? # swap bytes as native=little (and we want network)
230
+ self.stream << str
231
+ end
232
+
233
+ #16 bits signed
234
+ def write_int16_native(val)
235
+ self.stream << [val].pack('s')
236
+ end
237
+
238
+ def write_int16_little(val)
239
+ self.stream << [val].pack('v')
240
+ end
241
+
242
+ def write_int16_network(val)
243
+ self.stream << [val].pack('n')
244
+ end
245
+
246
+ #32 bit unsigned
247
+ def write_word32_native(val)
248
+ self.stream << [val].pack('L')
249
+ end
250
+
251
+ def write_word32_little(val)
252
+ str = [val].pack('L')
253
+ str.reverse! if byte_order_network? # swap bytes as native=network (and we want little)
254
+ self.stream << str
255
+ end
256
+
257
+ def write_word32_network(val)
258
+ str = [val].pack('L')
259
+ str.reverse! if byte_order_little? # swap bytes as native=little (and we want network)
260
+ self.stream << str
261
+ end
262
+
263
+ #32 signed
264
+ def write_int32_native(val)
265
+ self.stream << [val].pack('l')
266
+ end
267
+
268
+ def write_int32_little(val)
269
+ self.stream << [val].pack('V')
270
+ end
271
+
272
+ def write_int32_network(val)
273
+ self.stream << [val].pack('N')
274
+ end
275
+
276
+ # write utility methods
277
+ def write_byte(val)
278
+ #self.write_int8(val)
279
+ @stream << [val].pack('c')
280
+ end
281
+
282
+ def write_boolean(val)
283
+ if val then self.write_byte(1) else self.write_byte(0) end
284
+ end
285
+
286
+ def write_utf(str)
287
+ self.write_int16_network(str.length)
288
+ self.stream << str
289
+ end
290
+
291
+ def write_long_utf(str)
292
+ self.write_int32_network(str.length)
293
+ self.stream << str
294
+ end
295
+
296
+ def write_double(val)
297
+ self.stream << ( @floats_cache[val] ||=
298
+ [val].pack('G')
299
+ )
300
+ #puts "WRITE DOUBLE"
301
+ #puts @floats_cache
302
+ end
303
+ end
304
+ end
305
+ end
306
+ rescue Exception => e
307
+ raise RUBYAMFException.new(RUBYAMFException.AMF_ERROR, "The AMF data is incorrect or incomplete.")
308
+ end