revent 0.1 → 0.2

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.
@@ -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