forked from OpenNeo/impress
Moderize RocketAMF C types to fix build error
I'm not sure if this is a Mac-only problem or what, but we were getting incompatible-function-pointer errors when trying to build the RocketAMF C extensions. This fixes that! (Maybe it's like, Mac-only but as of Ruby 3.4 in specific? We're running RocketAMF in production on Ruby 3.4 right now without this. Shrug.)
This commit is contained in:
parent
d90e0549ca
commit
b1f06029f8
108 changed files with 6422 additions and 1 deletions
3
Gemfile
3
Gemfile
|
|
@ -45,7 +45,8 @@ gem 'sanitize', '~> 6.0', '>= 6.0.2'
|
||||||
|
|
||||||
# For working with Neopets APIs.
|
# For working with Neopets APIs.
|
||||||
# unstable version of RocketAMF interprets info registry as a hash instead of an array
|
# unstable version of RocketAMF interprets info registry as a hash instead of an array
|
||||||
gem 'RocketAMF', :git => 'https://github.com/rubyamf/rocketamf.git'
|
# Vendored version with Ruby 3.4 ARM compatibility fixes (see vendor/gems/README-RocketAMF.md)
|
||||||
|
gem 'RocketAMF', path: 'vendor/gems/RocketAMF-1.0.0'
|
||||||
|
|
||||||
# For preventing too many modeling attempts.
|
# For preventing too many modeling attempts.
|
||||||
gem 'rack-attack', '~> 6.7'
|
gem 'rack-attack', '~> 6.7'
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
PATH
|
||||||
|
remote: vendor/gems/RocketAMF-1.0.0
|
||||||
|
specs:
|
||||||
|
RocketAMF (1.0.0.dti1)
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
|
|
@ -527,6 +532,7 @@ PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
|
RocketAMF!
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
async (~> 2.17)
|
async (~> 2.17)
|
||||||
async-http (~> 0.89.0)
|
async-http (~> 0.89.0)
|
||||||
|
|
|
||||||
75
vendor/gems/README-RocketAMF.md
vendored
Normal file
75
vendor/gems/README-RocketAMF.md
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
# RocketAMF Vendored Gem
|
||||||
|
|
||||||
|
_Fix and docs authored by Claude Code, with Matchu's supervision. I'm not super familiar with C extensions in Ruby, but the edit seems small and safe, and pet loading still works as expected!_
|
||||||
|
|
||||||
|
This directory contains a vendored, patched version of RocketAMF 1.0.0.
|
||||||
|
|
||||||
|
## Why Vendored?
|
||||||
|
|
||||||
|
RocketAMF is a critical dependency for DTI's "modeling" system - it enables communication with Neopets.com's legacy Flash/AMF (Action Message Format) API to fetch pet appearance data. However, the upstream gem has not been maintained for modern Ruby versions.
|
||||||
|
|
||||||
|
## What Was Changed?
|
||||||
|
|
||||||
|
**File modified**: `ext/rocketamf_ext/class_mapping.c`
|
||||||
|
|
||||||
|
**Problem**: Ruby 3.4 introduced stricter type checking for `st_foreach` callback functions. The original code used incorrect function pointer types that were accepted in older Ruby versions but rejected in Ruby 3.4+.
|
||||||
|
|
||||||
|
**Fix**: Updated the `mapping_populate_iter` callback function signature (line 340) to match Ruby 3.4's requirements:
|
||||||
|
|
||||||
|
```c
|
||||||
|
// BEFORE (Ruby < 3.4):
|
||||||
|
static int mapping_populate_iter(VALUE key, VALUE val, const VALUE args[2])
|
||||||
|
|
||||||
|
// AFTER (Ruby 3.4+):
|
||||||
|
static int mapping_populate_iter(st_data_t key_data, st_data_t val_data, st_data_t args_data) {
|
||||||
|
VALUE key = (VALUE)key_data;
|
||||||
|
VALUE val = (VALUE)val_data;
|
||||||
|
const VALUE *args = (const VALUE *)args_data;
|
||||||
|
// ... rest of function unchanged
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The function body remains identical - we just cast the `st_data_t` parameters to the expected `VALUE` types at the start of the function.
|
||||||
|
|
||||||
|
## Upstream Status
|
||||||
|
|
||||||
|
**Repository**: https://github.com/rubyamf/rocketamf
|
||||||
|
**Last commit**: 2018
|
||||||
|
**Issue**: No active maintenance; Ruby 3.4 compatibility not addressed upstream
|
||||||
|
|
||||||
|
We chose to vendor this fix rather than maintain a full fork because:
|
||||||
|
1. RocketAMF functionality is stable - we don't expect to need updates
|
||||||
|
2. The community demand is low (Neopets' Flash API is legacy)
|
||||||
|
3. A vendored gem is simpler to maintain than a hosted fork
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
After applying the fix, verified:
|
||||||
|
- ✅ Gem compiles successfully on ARM (aarch64-linux) with Ruby 3.4.5
|
||||||
|
- ✅ Gem loads without errors: `require 'rocketamf'`
|
||||||
|
- ✅ C extension works: `RocketAMF::ClassMapping.new`
|
||||||
|
- ✅ End-to-end Neopets API integration functional
|
||||||
|
|
||||||
|
## Updating This Gem
|
||||||
|
|
||||||
|
If you need to update RocketAMF in the future:
|
||||||
|
|
||||||
|
1. Clone the upstream repo: `git clone https://github.com/rubyamf/rocketamf.git`
|
||||||
|
2. Apply the fix to `ext/rocketamf_ext/class_mapping.c` (see above)
|
||||||
|
3. Build the gem: `gem build RocketAMF.gemspec`
|
||||||
|
4. Unpack to vendor: `gem unpack RocketAMF-X.X.X.gem`
|
||||||
|
5. Update the Gemfile path if version changed
|
||||||
|
6. Test thoroughly with `bundle install` and Neopets modeling functionality
|
||||||
|
|
||||||
|
## Alternative Solutions Considered
|
||||||
|
|
||||||
|
- **Fork to GitHub**: Too much maintenance overhead for a single file change
|
||||||
|
- **Downgrade Ruby**: Would miss out on Ruby 3.4+ features and security updates
|
||||||
|
- **Pure-Ruby AMF library**: None exist with active maintenance
|
||||||
|
- **Patch at runtime**: C extension issues can't be patched from Ruby
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Fix applied**: 2025-10-30
|
||||||
|
**Ruby version**: 3.4.5
|
||||||
|
**Architecture**: ARM64 (aarch64-linux)
|
||||||
47
vendor/gems/RocketAMF-1.0.0/README.rdoc
vendored
Normal file
47
vendor/gems/RocketAMF-1.0.0/README.rdoc
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
== DESCRIPTION:
|
||||||
|
|
||||||
|
RocketAMF is a full featured AMF0/3 serializer and deserializer with support for
|
||||||
|
bi-directional Flash to Ruby class mapping, custom serialization and mapping,
|
||||||
|
remoting gateway helpers that follow AMF0/3 messaging specs, and a suite of specs
|
||||||
|
to ensure adherence to the specification documents put out by Adobe. If the C
|
||||||
|
components compile, then RocketAMF automatically takes advantage of them to
|
||||||
|
provide a substantial performance benefit. In addition, RocketAMF is fully
|
||||||
|
compatible with Ruby 1.9.
|
||||||
|
|
||||||
|
== INSTALL:
|
||||||
|
|
||||||
|
gem install RocketAMF
|
||||||
|
|
||||||
|
== SIMPLE EXAMPLE:
|
||||||
|
|
||||||
|
require 'rocketamf'
|
||||||
|
|
||||||
|
hash = {:apple => "Apfel", :red => "Rot", :eyes => "Augen"}
|
||||||
|
File.open("amf.dat", 'w') do |f|
|
||||||
|
f.write RocketAMF.serialize(hash, 3) # Use AMF3 encoding to serialize
|
||||||
|
end
|
||||||
|
|
||||||
|
== LICENSE:
|
||||||
|
|
||||||
|
(The MIT License)
|
||||||
|
|
||||||
|
Copyright (c) 2011 Stephen Augenstein and Jacob Henry
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
'Software'), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
59
vendor/gems/RocketAMF-1.0.0/Rakefile
vendored
Normal file
59
vendor/gems/RocketAMF-1.0.0/Rakefile
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
require 'rubygems'
|
||||||
|
require 'rake'
|
||||||
|
require 'rake/rdoctask'
|
||||||
|
require 'rake/gempackagetask'
|
||||||
|
require 'rspec/core/rake_task'
|
||||||
|
require 'rake/extensiontask'
|
||||||
|
|
||||||
|
desc 'Default: run the specs.'
|
||||||
|
task :default => :spec
|
||||||
|
|
||||||
|
# I don't want to depend on bundler, so we do it the bundler way without it
|
||||||
|
gemspec_path = 'RocketAMF.gemspec'
|
||||||
|
spec = begin
|
||||||
|
eval(File.read(File.join(File.dirname(__FILE__), gemspec_path)), TOPLEVEL_BINDING, gemspec_path)
|
||||||
|
rescue LoadError => e
|
||||||
|
original_line = e.backtrace.find { |line| line.include?(gemspec_path) }
|
||||||
|
msg = "There was a LoadError while evaluating #{gemspec_path}:\n #{e.message}"
|
||||||
|
msg << " from\n #{original_line}" if original_line
|
||||||
|
msg << "\n"
|
||||||
|
puts msg
|
||||||
|
exit
|
||||||
|
end
|
||||||
|
|
||||||
|
RSpec::Core::RakeTask.new do |t|
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Generate documentation'
|
||||||
|
Rake::RDocTask.new(:rdoc) do |rdoc|
|
||||||
|
rdoc.rdoc_dir = 'rdoc'
|
||||||
|
rdoc.title = spec.name
|
||||||
|
rdoc.options += spec.rdoc_options
|
||||||
|
rdoc.rdoc_files.include(*spec.extra_rdoc_files)
|
||||||
|
rdoc.rdoc_files.include("lib") # Don't include ext folder because no one cares
|
||||||
|
end
|
||||||
|
|
||||||
|
Rake::GemPackageTask.new(spec) do |pkg|
|
||||||
|
pkg.need_zip = false
|
||||||
|
pkg.need_tar = false
|
||||||
|
end
|
||||||
|
|
||||||
|
Rake::ExtensionTask.new('rocketamf_ext', spec) do |ext|
|
||||||
|
if RUBY_PLATFORM =~ /mswin|mingw/ then
|
||||||
|
# No cross-compile on win, so compile extension to lib/1.[89]
|
||||||
|
RUBY_VERSION =~ /(\d+\.\d+)/
|
||||||
|
ext.lib_dir = "lib/#{$1}"
|
||||||
|
else
|
||||||
|
ext.cross_compile = true
|
||||||
|
ext.cross_platform = 'x86-mingw32'
|
||||||
|
ext.cross_compiling do |gem_spec|
|
||||||
|
gem_spec.post_install_message = "You installed the binary version of this gem!"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
#ext.config_options << '--enable-sort-props'
|
||||||
|
end
|
||||||
|
|
||||||
|
desc "Build gem packages"
|
||||||
|
task :gems do
|
||||||
|
sh "rake cross native gem RUBY_CC_VERSION=1.8.7:1.9.2"
|
||||||
|
end
|
||||||
20
vendor/gems/RocketAMF-1.0.0/RocketAMF.gemspec
vendored
Normal file
20
vendor/gems/RocketAMF-1.0.0/RocketAMF.gemspec
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
Gem::Specification.new do |s|
|
||||||
|
s.name = 'RocketAMF'
|
||||||
|
s.version = '1.0.0.dti1'
|
||||||
|
s.platform = Gem::Platform::RUBY
|
||||||
|
s.authors = ['Jacob Henry', 'Stephen Augenstein', "Joc O'Connor"]
|
||||||
|
s.email = ['perl.programmer@gmail.com']
|
||||||
|
s.homepage = 'http://github.com/rubyamf/rocketamf'
|
||||||
|
s.summary = 'Fast AMF serializer/deserializer with remoting request/response wrappers to simplify integration'
|
||||||
|
|
||||||
|
s.files = Dir[*['README.rdoc', 'benchmark.rb', 'RocketAMF.gemspec', 'Rakefile', 'lib/**/*.rb', 'spec/**/*.{rb,bin,opts}', 'ext/**/*.{c,h,rb}']]
|
||||||
|
s.test_files = Dir[*['spec/**/*_spec.rb']]
|
||||||
|
s.extensions = Dir[*["ext/**/extconf.rb"]]
|
||||||
|
s.require_paths = ["lib"]
|
||||||
|
|
||||||
|
s.has_rdoc = true
|
||||||
|
s.extra_rdoc_files = ['README.rdoc']
|
||||||
|
s.rdoc_options = ['--line-numbers', '--main', 'README.rdoc']
|
||||||
|
end
|
||||||
74
vendor/gems/RocketAMF-1.0.0/benchmark.rb
vendored
Normal file
74
vendor/gems/RocketAMF-1.0.0/benchmark.rb
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
$:.unshift(File.dirname(__FILE__) + '/ext')
|
||||||
|
$:.unshift(File.dirname(__FILE__) + '/lib')
|
||||||
|
require 'rubygems'
|
||||||
|
require 'rocketamf'
|
||||||
|
require 'rocketamf/pure/deserializer' # Only ext gets included by default if available
|
||||||
|
require 'rocketamf/pure/serializer'
|
||||||
|
|
||||||
|
OBJECT_COUNT = 100000
|
||||||
|
TESTS = 5
|
||||||
|
|
||||||
|
class TestClass
|
||||||
|
attr_accessor :prop_a, :prop_b, :prop_c, :prop_d, :prop_e
|
||||||
|
|
||||||
|
def populate some_arg=nil # Make sure class mapper doesn't think populate is a property
|
||||||
|
@@count ||= 1
|
||||||
|
@prop_a = "asdfasdf #{@@count}"
|
||||||
|
@prop_b = "simple string"
|
||||||
|
@prop_c = 3120094.03
|
||||||
|
@prop_d = Time.now
|
||||||
|
@prop_e = 3120094
|
||||||
|
@@count += 1
|
||||||
|
self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
objs = []
|
||||||
|
OBJECT_COUNT.times do
|
||||||
|
objs << TestClass.new.populate
|
||||||
|
end
|
||||||
|
|
||||||
|
["native", "pure"].each do |type|
|
||||||
|
# Set up class mapper
|
||||||
|
cm = if type == "pure"
|
||||||
|
RocketAMF::ClassMapping
|
||||||
|
else
|
||||||
|
RocketAMF::Ext::FastClassMapping
|
||||||
|
end
|
||||||
|
cm.define do |m|
|
||||||
|
m.map :as => 'TestClass', :ruby => 'TestClass'
|
||||||
|
end
|
||||||
|
|
||||||
|
[0, 3].each do |version|
|
||||||
|
# 2**24 is larger than anyone is ever going to run this for
|
||||||
|
min_serialize = 2**24
|
||||||
|
min_deserialize = 2**24
|
||||||
|
|
||||||
|
puts "Testing #{type} AMF#{version}:"
|
||||||
|
TESTS.times do
|
||||||
|
ser = if type == "pure"
|
||||||
|
RocketAMF::Pure::Serializer.new(cm.new)
|
||||||
|
else
|
||||||
|
RocketAMF::Ext::Serializer.new(cm.new)
|
||||||
|
end
|
||||||
|
start_time = Time.now
|
||||||
|
out = ser.serialize(version, objs)
|
||||||
|
end_time = Time.now
|
||||||
|
puts "\tserialize run: #{end_time-start_time}s"
|
||||||
|
min_serialize = [end_time-start_time, min_serialize].min
|
||||||
|
|
||||||
|
des = if type == "pure"
|
||||||
|
RocketAMF::Pure::Deserializer.new(cm.new)
|
||||||
|
else
|
||||||
|
RocketAMF::Ext::Deserializer.new(cm.new)
|
||||||
|
end
|
||||||
|
start_time = Time.now
|
||||||
|
temp = des.deserialize(version, out)
|
||||||
|
end_time = Time.now
|
||||||
|
puts "\tdeserialize run: #{end_time-start_time}s"
|
||||||
|
min_deserialize = [end_time-start_time, min_deserialize].min
|
||||||
|
end
|
||||||
|
puts "\tminimum serialize time: #{min_serialize}s"
|
||||||
|
puts "\tminimum deserialize time: #{min_deserialize}s"
|
||||||
|
end
|
||||||
|
end
|
||||||
487
vendor/gems/RocketAMF-1.0.0/ext/rocketamf_ext/class_mapping.c
vendored
Normal file
487
vendor/gems/RocketAMF-1.0.0/ext/rocketamf_ext/class_mapping.c
vendored
Normal file
|
|
@ -0,0 +1,487 @@
|
||||||
|
#include <ruby.h>
|
||||||
|
#ifdef HAVE_RB_STR_ENCODE
|
||||||
|
#include <ruby/st.h>
|
||||||
|
#else
|
||||||
|
#include <st.h>
|
||||||
|
#endif
|
||||||
|
#include "utility.h"
|
||||||
|
|
||||||
|
extern VALUE mRocketAMF;
|
||||||
|
extern VALUE mRocketAMFExt;
|
||||||
|
VALUE cFastMappingSet;
|
||||||
|
VALUE cTypedHash;
|
||||||
|
ID id_use_ac;
|
||||||
|
ID id_use_ac_ivar;
|
||||||
|
ID id_mappings;
|
||||||
|
ID id_mappings_ivar;
|
||||||
|
ID id_hashset;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
VALUE mapset;
|
||||||
|
st_table* setter_cache;
|
||||||
|
st_table* prop_cache;
|
||||||
|
} CLASS_MAPPING;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
st_table* as_mappings;
|
||||||
|
st_table* rb_mappings;
|
||||||
|
} MAPSET;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mark the as_mappings and rb_mappings hashes
|
||||||
|
*/
|
||||||
|
static void mapset_mark(MAPSET *set) {
|
||||||
|
if(!set) return;
|
||||||
|
rb_mark_tbl(set->as_mappings);
|
||||||
|
rb_mark_tbl(set->rb_mappings);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free the mapping tables and struct
|
||||||
|
*/
|
||||||
|
int mapset_free_strtable_key(st_data_t key, st_data_t value, st_data_t ignored) {
|
||||||
|
xfree((void *)key);
|
||||||
|
return ST_DELETE;
|
||||||
|
}
|
||||||
|
static void mapset_free(MAPSET *set) {
|
||||||
|
st_foreach(set->as_mappings, mapset_free_strtable_key, 0);
|
||||||
|
st_free_table(set->as_mappings);
|
||||||
|
set->as_mappings = NULL;
|
||||||
|
st_foreach(set->rb_mappings, mapset_free_strtable_key, 0);
|
||||||
|
st_free_table(set->rb_mappings);
|
||||||
|
set->rb_mappings = NULL;
|
||||||
|
xfree(set);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate mapset and populate mappings with built-in mappings
|
||||||
|
*/
|
||||||
|
static VALUE mapset_alloc(VALUE klass) {
|
||||||
|
MAPSET *set = ALLOC(MAPSET);
|
||||||
|
memset(set, 0, sizeof(MAPSET));
|
||||||
|
VALUE self = Data_Wrap_Struct(klass, mapset_mark, mapset_free, set);
|
||||||
|
|
||||||
|
// Initialize internal data
|
||||||
|
set->as_mappings = st_init_strtable();
|
||||||
|
set->rb_mappings = st_init_strtable();
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* RocketAMF::Ext::MappingSet.new
|
||||||
|
*
|
||||||
|
* Creates a mapping set object and populates the default mappings
|
||||||
|
*/
|
||||||
|
static VALUE mapset_init(VALUE self) {
|
||||||
|
rb_funcall(self, rb_intern("map_defaults"), 0);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* m.map_defaults
|
||||||
|
*
|
||||||
|
* Adds required mapping configs, calling map for the required base mappings
|
||||||
|
*/
|
||||||
|
static VALUE mapset_map_defaults(VALUE self) {
|
||||||
|
const int NUM_MAPPINGS = 9;
|
||||||
|
const char* ruby_classes[] = {
|
||||||
|
"RocketAMF::Values::AbstractMessage",
|
||||||
|
"RocketAMF::Values::RemotingMessage",
|
||||||
|
"RocketAMF::Values::AsyncMessage",
|
||||||
|
"RocketAMF::Values::AsyncMessageExt",
|
||||||
|
"RocketAMF::Values::CommandMessage",
|
||||||
|
"RocketAMF::Values::CommandMessageExt",
|
||||||
|
"RocketAMF::Values::AcknowledgeMessage",
|
||||||
|
"RocketAMF::Values::AcknowledgeMessageExt",
|
||||||
|
"RocketAMF::Values::ErrorMessage"
|
||||||
|
};
|
||||||
|
const char* as_classes[] = {
|
||||||
|
"flex.messaging.messages.AbstractMessage",
|
||||||
|
"flex.messaging.messages.RemotingMessage",
|
||||||
|
"flex.messaging.messages.AsyncMessage",
|
||||||
|
"DSA",
|
||||||
|
"flex.messaging.messages.CommandMessage",
|
||||||
|
"DSC",
|
||||||
|
"flex.messaging.messages.AcknowledgeMessage",
|
||||||
|
"DSK",
|
||||||
|
"flex.messaging.messages.ErrorMessage"
|
||||||
|
};
|
||||||
|
|
||||||
|
int i;
|
||||||
|
ID map_id = rb_intern("map");
|
||||||
|
VALUE params = rb_hash_new();
|
||||||
|
VALUE as_sym = ID2SYM(rb_intern("as"));
|
||||||
|
VALUE ruby_sym = ID2SYM(rb_intern("ruby"));
|
||||||
|
for(i = 0; i < NUM_MAPPINGS; i++) {
|
||||||
|
rb_hash_aset(params, as_sym, rb_str_new2(as_classes[i]));
|
||||||
|
rb_hash_aset(params, ruby_sym, rb_str_new2(ruby_classes[i]));
|
||||||
|
rb_funcall(self, map_id, 1, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* m.map :as => 'com.example.Date', :ruby => "Example::Date'
|
||||||
|
*
|
||||||
|
* Map a given AS class to a ruby class. Use fully qualified names for both.
|
||||||
|
*/
|
||||||
|
static VALUE mapset_map(VALUE self, VALUE mapping) {
|
||||||
|
MAPSET *set;
|
||||||
|
Data_Get_Struct(self, MAPSET, set);
|
||||||
|
|
||||||
|
VALUE as_class = rb_hash_aref(mapping, ID2SYM(rb_intern("as")));
|
||||||
|
VALUE rb_class = rb_hash_aref(mapping, ID2SYM(rb_intern("ruby")));
|
||||||
|
st_insert(set->as_mappings, (st_data_t)strdup(RSTRING_PTR(as_class)), rb_class);
|
||||||
|
st_insert(set->rb_mappings, (st_data_t)strdup(RSTRING_PTR(rb_class)), as_class);
|
||||||
|
|
||||||
|
return Qnil;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Internal method for looking up a given ruby class's AS class name or Qnil if
|
||||||
|
* not found
|
||||||
|
*/
|
||||||
|
static VALUE mapset_as_lookup(VALUE self, const char* class_name) {
|
||||||
|
MAPSET *set;
|
||||||
|
Data_Get_Struct(self, MAPSET, set);
|
||||||
|
|
||||||
|
VALUE as_name;
|
||||||
|
if(st_lookup(set->rb_mappings, (st_data_t)class_name, &as_name)) {
|
||||||
|
return as_name;
|
||||||
|
} else {
|
||||||
|
return Qnil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Internal method for looking up a given AS class names ruby class name mapping
|
||||||
|
* or Qnil if not found
|
||||||
|
*/
|
||||||
|
static VALUE mapset_rb_lookup(VALUE self, const char* class_name) {
|
||||||
|
MAPSET *set;
|
||||||
|
Data_Get_Struct(self, MAPSET, set);
|
||||||
|
|
||||||
|
VALUE rb_name;
|
||||||
|
if(st_lookup(set->as_mappings, (st_data_t)class_name, &rb_name)) {
|
||||||
|
return rb_name;
|
||||||
|
} else {
|
||||||
|
return Qnil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mark the mapset object and property lookup cache
|
||||||
|
*/
|
||||||
|
static void mapping_mark(CLASS_MAPPING *map) {
|
||||||
|
if(!map) return;
|
||||||
|
rb_gc_mark(map->mapset);
|
||||||
|
rb_mark_tbl(map->prop_cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free prop cache table and struct
|
||||||
|
*/
|
||||||
|
static void mapping_free(CLASS_MAPPING *map) {
|
||||||
|
st_free_table(map->setter_cache);
|
||||||
|
st_free_table(map->prop_cache);
|
||||||
|
xfree(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate class mapping struct
|
||||||
|
*/
|
||||||
|
static VALUE mapping_alloc(VALUE klass) {
|
||||||
|
CLASS_MAPPING *map = ALLOC(CLASS_MAPPING);
|
||||||
|
memset(map, 0, sizeof(CLASS_MAPPING));
|
||||||
|
VALUE self = Data_Wrap_Struct(klass, mapping_mark, mapping_free, map);
|
||||||
|
map->setter_cache = st_init_numtable();
|
||||||
|
map->prop_cache = st_init_numtable();
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class-level getter for use_array_collection
|
||||||
|
*/
|
||||||
|
static VALUE mapping_s_array_collection_get(VALUE klass) {
|
||||||
|
VALUE use_ac = rb_ivar_get(klass, id_use_ac_ivar);
|
||||||
|
if(use_ac == Qnil) {
|
||||||
|
use_ac = Qfalse;
|
||||||
|
rb_ivar_set(klass, id_use_ac_ivar, use_ac);
|
||||||
|
}
|
||||||
|
return use_ac;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class-level setter for use_array_collection
|
||||||
|
*/
|
||||||
|
static VALUE mapping_s_array_collection_set(VALUE klass, VALUE use_ac) {
|
||||||
|
return rb_ivar_set(klass, id_use_ac_ivar, use_ac);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return MappingSet for class mapper, creating if uninitialized
|
||||||
|
*/
|
||||||
|
static VALUE mapping_s_mappings(VALUE klass) {
|
||||||
|
VALUE mappings = rb_ivar_get(klass, id_mappings_ivar);
|
||||||
|
if(mappings == Qnil) {
|
||||||
|
mappings = rb_class_new_instance(0, NULL, cFastMappingSet);
|
||||||
|
rb_ivar_set(klass, id_mappings_ivar, mappings);
|
||||||
|
}
|
||||||
|
return mappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* mapper.define {|m| block } => nil
|
||||||
|
*
|
||||||
|
* Define class mappings in the block. Block is passed a MappingSet object as
|
||||||
|
* the first parameter. See RocketAMF::ClassMapping for details.
|
||||||
|
*/
|
||||||
|
static VALUE mapping_s_define(VALUE klass) {
|
||||||
|
if (rb_block_given_p()) {
|
||||||
|
VALUE mappings = rb_funcall(klass, id_mappings, 0);
|
||||||
|
rb_yield(mappings);
|
||||||
|
}
|
||||||
|
return Qnil;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset class mappings
|
||||||
|
*/
|
||||||
|
static VALUE mapping_s_reset(VALUE klass) {
|
||||||
|
rb_ivar_set(klass, id_use_ac_ivar, Qfalse);
|
||||||
|
rb_ivar_set(klass, id_mappings_ivar, Qnil);
|
||||||
|
return Qnil;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize class mapping object, setting use_class_mapping to false
|
||||||
|
*/
|
||||||
|
static VALUE mapping_init(VALUE self) {
|
||||||
|
CLASS_MAPPING *map;
|
||||||
|
Data_Get_Struct(self, CLASS_MAPPING, map);
|
||||||
|
map->mapset = rb_funcall(CLASS_OF(self), id_mappings, 0);
|
||||||
|
VALUE use_ac = rb_funcall(CLASS_OF(self), id_use_ac, 0);
|
||||||
|
rb_ivar_set(self, id_use_ac_ivar, use_ac);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* mapper.get_as_class_name => str
|
||||||
|
*
|
||||||
|
* Returns the AS class name for the given ruby object. Will also take a string
|
||||||
|
* containing the ruby class name.
|
||||||
|
*/
|
||||||
|
static VALUE mapping_as_class_name(VALUE self, VALUE obj) {
|
||||||
|
CLASS_MAPPING *map;
|
||||||
|
Data_Get_Struct(self, CLASS_MAPPING, map);
|
||||||
|
|
||||||
|
int type = TYPE(obj);
|
||||||
|
const char* class_name;
|
||||||
|
if(type == T_STRING) {
|
||||||
|
// Use strings as the class name
|
||||||
|
class_name = RSTRING_PTR(obj);
|
||||||
|
} else {
|
||||||
|
// Look up the class name and use that
|
||||||
|
VALUE klass = CLASS_OF(obj);
|
||||||
|
class_name = rb_class2name(klass);
|
||||||
|
if(klass == cTypedHash) {
|
||||||
|
VALUE orig_name = rb_funcall(obj, rb_intern("type"), 0);
|
||||||
|
class_name = RSTRING_PTR(orig_name);
|
||||||
|
} else if(type == T_HASH) {
|
||||||
|
// Don't bother looking up hash mapping, but need to check class name first in case it's a typed hash
|
||||||
|
return Qnil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapset_as_lookup(map->mapset, class_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call_seq:
|
||||||
|
* mapper.get_ruby_obj => obj
|
||||||
|
*
|
||||||
|
* Instantiates a ruby object using the mapping configuration based on the
|
||||||
|
* source AS class name. If there is no mapping defined, it returns a
|
||||||
|
* <tt>RocketAMF::Values::TypedHash</tt> with the serialized class name.
|
||||||
|
*/
|
||||||
|
static VALUE mapping_get_ruby_obj(VALUE self, VALUE name) {
|
||||||
|
CLASS_MAPPING *map;
|
||||||
|
Data_Get_Struct(self, CLASS_MAPPING, map);
|
||||||
|
|
||||||
|
VALUE argv[1];
|
||||||
|
VALUE ruby_class_name = mapset_rb_lookup(map->mapset, RSTRING_PTR(name));
|
||||||
|
if(ruby_class_name == Qnil) {
|
||||||
|
argv[0] = name;
|
||||||
|
return rb_class_new_instance(1, argv, cTypedHash);
|
||||||
|
} else {
|
||||||
|
VALUE base_const = rb_mKernel;
|
||||||
|
char* endptr;
|
||||||
|
char* ptr = RSTRING_PTR(ruby_class_name);
|
||||||
|
while((endptr = strstr(ptr,"::"))) {
|
||||||
|
endptr[0] = '\0'; // NULL terminate to make string ops work
|
||||||
|
base_const = rb_const_get(base_const, rb_intern(ptr));
|
||||||
|
endptr[0] = ':'; // Restore correct char
|
||||||
|
ptr = endptr + 2;
|
||||||
|
}
|
||||||
|
return rb_class_new_instance(0, NULL, rb_const_get(base_const, rb_intern(ptr)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* st_table iterator for populating a given object from a property hash
|
||||||
|
*/
|
||||||
|
static int mapping_populate_iter(st_data_t key_data, st_data_t val_data, st_data_t args_data) {
|
||||||
|
VALUE key = (VALUE)key_data;
|
||||||
|
VALUE val = (VALUE)val_data;
|
||||||
|
const VALUE *args = (const VALUE *)args_data;
|
||||||
|
CLASS_MAPPING *map;
|
||||||
|
Data_Get_Struct(args[0], CLASS_MAPPING, map);
|
||||||
|
VALUE obj = args[1];
|
||||||
|
|
||||||
|
if(TYPE(obj) == T_HASH) {
|
||||||
|
rb_hash_aset(obj, key, val);
|
||||||
|
return ST_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(TYPE(key) != T_SYMBOL) rb_raise(rb_eArgError, "Invalid type for property key: %d", TYPE(key));
|
||||||
|
|
||||||
|
// Calculate symbol for setter function
|
||||||
|
ID key_id = SYM2ID(key);
|
||||||
|
ID setter_id;
|
||||||
|
if(!st_lookup(map->setter_cache, key_id, &setter_id)) {
|
||||||
|
// Calculate symbol
|
||||||
|
const char* key_str = rb_id2name(key_id);
|
||||||
|
long len = strlen(key_str);
|
||||||
|
char* setter = ALLOC_N(char, len+2);
|
||||||
|
memcpy(setter, key_str, len);
|
||||||
|
setter[len] = '=';
|
||||||
|
setter[len+1] = '\0';
|
||||||
|
setter_id = rb_intern(setter);
|
||||||
|
xfree(setter);
|
||||||
|
|
||||||
|
// Store it
|
||||||
|
st_add_direct(map->setter_cache, key_id, setter_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rb_respond_to(obj, setter_id)) {
|
||||||
|
rb_funcall(obj, setter_id, 1, val);
|
||||||
|
} else if(rb_respond_to(obj, id_hashset)) {
|
||||||
|
rb_funcall(obj, id_hashset, 2, key, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ST_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* mapper.populate_ruby_obj(obj, props, dynamic_props=nil) => obj
|
||||||
|
*
|
||||||
|
* Populates the ruby object using the given properties. Property hashes MUST
|
||||||
|
* have symbol keys, or it will raise an exception.
|
||||||
|
*/
|
||||||
|
static VALUE mapping_populate(int argc, VALUE *argv, VALUE self) {
|
||||||
|
// Check args
|
||||||
|
VALUE obj, props, dynamic_props;
|
||||||
|
rb_scan_args(argc, argv, "21", &obj, &props, &dynamic_props);
|
||||||
|
|
||||||
|
VALUE args[2] = {self, obj};
|
||||||
|
st_foreach(RHASH_TBL(props), mapping_populate_iter, (st_data_t)args);
|
||||||
|
if(dynamic_props != Qnil) {
|
||||||
|
st_foreach(RHASH_TBL(dynamic_props), mapping_populate_iter, (st_data_t)args);
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* mapper.props_for_serialization(obj) => hash
|
||||||
|
*
|
||||||
|
* Extracts all exportable properties from the given ruby object and returns
|
||||||
|
* them in a hash. For performance purposes, property detection is only performed
|
||||||
|
* once for a given class instance, and then cached for all instances of that
|
||||||
|
* class. IF YOU'RE ADDING AND REMOVING PROPERTIES FROM CLASS INSTANCES YOU
|
||||||
|
* CANNOT USE THE FAST CLASS MAPPER.
|
||||||
|
*/
|
||||||
|
static VALUE mapping_props(VALUE self, VALUE obj) {
|
||||||
|
CLASS_MAPPING *map;
|
||||||
|
Data_Get_Struct(self, CLASS_MAPPING, map);
|
||||||
|
|
||||||
|
if(TYPE(obj) == T_HASH) {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get "properties"
|
||||||
|
VALUE props_ary;
|
||||||
|
VALUE klass = CLASS_OF(obj);
|
||||||
|
long i, len;
|
||||||
|
if(!st_lookup(map->prop_cache, klass, &props_ary)) {
|
||||||
|
props_ary = rb_ary_new();
|
||||||
|
|
||||||
|
// Build props array
|
||||||
|
VALUE all_methods = rb_class_public_instance_methods(0, NULL, klass);
|
||||||
|
VALUE object_methods = rb_class_public_instance_methods(0, NULL, rb_cObject);
|
||||||
|
VALUE possible_methods = rb_funcall(all_methods, rb_intern("-"), 1, object_methods);
|
||||||
|
len = RARRAY_LEN(possible_methods);
|
||||||
|
for(i = 0; i < len; i++) {
|
||||||
|
VALUE meth = rb_obj_method(obj, RARRAY_PTR(possible_methods)[i]);
|
||||||
|
VALUE arity = rb_funcall(meth, rb_intern("arity"), 0);
|
||||||
|
if(FIX2INT(arity) == 0) {
|
||||||
|
rb_ary_push(props_ary, RARRAY_PTR(possible_methods)[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store it
|
||||||
|
st_add_direct(map->prop_cache, klass, props_ary);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build properties hash using list of properties
|
||||||
|
VALUE props = rb_hash_new();
|
||||||
|
len = RARRAY_LEN(props_ary);
|
||||||
|
for(i = 0; i < len; i++) {
|
||||||
|
VALUE key = RARRAY_PTR(props_ary)[i];
|
||||||
|
ID getter = (TYPE(key) == T_STRING) ? rb_intern(RSTRING_PTR(key)) : SYM2ID(key);
|
||||||
|
rb_hash_aset(props, key, rb_funcall(obj, getter, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Init_rocket_amf_fast_class_mapping() {
|
||||||
|
// Define map set
|
||||||
|
cFastMappingSet = rb_define_class_under(mRocketAMFExt, "FastMappingSet", rb_cObject);
|
||||||
|
rb_define_alloc_func(cFastMappingSet, mapset_alloc);
|
||||||
|
rb_define_method(cFastMappingSet, "initialize", mapset_init, 0);
|
||||||
|
rb_define_method(cFastMappingSet, "map_defaults", mapset_map_defaults, 0);
|
||||||
|
rb_define_method(cFastMappingSet, "map", mapset_map, 1);
|
||||||
|
|
||||||
|
// Define FastClassMapping
|
||||||
|
VALUE cFastClassMapping = rb_define_class_under(mRocketAMFExt, "FastClassMapping", rb_cObject);
|
||||||
|
rb_define_alloc_func(cFastClassMapping, mapping_alloc);
|
||||||
|
rb_define_singleton_method(cFastClassMapping, "use_array_collection", mapping_s_array_collection_get, 0);
|
||||||
|
rb_define_singleton_method(cFastClassMapping, "use_array_collection=", mapping_s_array_collection_set, 1);
|
||||||
|
rb_define_singleton_method(cFastClassMapping, "mappings", mapping_s_mappings, 0);
|
||||||
|
rb_define_singleton_method(cFastClassMapping, "reset", mapping_s_reset, 0);
|
||||||
|
rb_define_singleton_method(cFastClassMapping, "define", mapping_s_define, 0);
|
||||||
|
rb_define_attr(cFastClassMapping, "use_array_collection", 1, 0);
|
||||||
|
rb_define_method(cFastClassMapping, "initialize", mapping_init, 0);
|
||||||
|
rb_define_method(cFastClassMapping, "get_as_class_name", mapping_as_class_name, 1);
|
||||||
|
rb_define_method(cFastClassMapping, "get_ruby_obj", mapping_get_ruby_obj, 1);
|
||||||
|
rb_define_method(cFastClassMapping, "populate_ruby_obj", mapping_populate, -1);
|
||||||
|
rb_define_method(cFastClassMapping, "props_for_serialization", mapping_props, 1);
|
||||||
|
|
||||||
|
// Cache values
|
||||||
|
cTypedHash = rb_const_get(rb_const_get(mRocketAMF, rb_intern("Values")), rb_intern("TypedHash"));
|
||||||
|
id_use_ac = rb_intern("use_array_collection");
|
||||||
|
id_use_ac_ivar = rb_intern("@use_array_collection");
|
||||||
|
id_mappings = rb_intern("mappings");
|
||||||
|
id_mappings_ivar = rb_intern("@mappings");
|
||||||
|
id_hashset = rb_intern("[]=");
|
||||||
|
}
|
||||||
52
vendor/gems/RocketAMF-1.0.0/ext/rocketamf_ext/constants.h
vendored
Normal file
52
vendor/gems/RocketAMF-1.0.0/ext/rocketamf_ext/constants.h
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
// AMF0 Type Markers
|
||||||
|
#define AMF0_NUMBER_MARKER 0x00
|
||||||
|
#define AMF0_BOOLEAN_MARKER 0x01
|
||||||
|
#define AMF0_STRING_MARKER 0x02
|
||||||
|
#define AMF0_OBJECT_MARKER 0x03
|
||||||
|
#define AMF0_MOVIE_CLIP_MARKER 0x04
|
||||||
|
#define AMF0_NULL_MARKER 0x05
|
||||||
|
#define AMF0_UNDEFINED_MARKER 0x06
|
||||||
|
#define AMF0_REFERENCE_MARKER 0x07
|
||||||
|
#define AMF0_HASH_MARKER 0x08
|
||||||
|
#define AMF0_OBJECT_END_MARKER 0x09
|
||||||
|
#define AMF0_STRICT_ARRAY_MARKER 0x0A
|
||||||
|
#define AMF0_DATE_MARKER 0x0B
|
||||||
|
#define AMF0_LONG_STRING_MARKER 0x0C
|
||||||
|
#define AMF0_UNSUPPORTED_MARKER 0x0D
|
||||||
|
#define AMF0_RECORDSET_MARKER 0x0E
|
||||||
|
#define AMF0_XML_MARKER 0x0F
|
||||||
|
#define AMF0_TYPED_OBJECT_MARKER 0x10
|
||||||
|
#define AMF0_AMF3_MARKER 0x11
|
||||||
|
|
||||||
|
// AMF3 Type Markers
|
||||||
|
#define AMF3_UNDEFINED_MARKER 0x00
|
||||||
|
#define AMF3_NULL_MARKER 0x01
|
||||||
|
#define AMF3_FALSE_MARKER 0x02
|
||||||
|
#define AMF3_TRUE_MARKER 0x03
|
||||||
|
#define AMF3_INTEGER_MARKER 0x04
|
||||||
|
#define AMF3_DOUBLE_MARKER 0x05
|
||||||
|
#define AMF3_STRING_MARKER 0x06
|
||||||
|
#define AMF3_XML_DOC_MARKER 0x07
|
||||||
|
#define AMF3_DATE_MARKER 0x08
|
||||||
|
#define AMF3_ARRAY_MARKER 0x09
|
||||||
|
#define AMF3_OBJECT_MARKER 0x0A
|
||||||
|
#define AMF3_XML_MARKER 0x0B
|
||||||
|
#define AMF3_BYTE_ARRAY_MARKER 0x0C
|
||||||
|
#define AMF3_VECTOR_INT_MARKER 0x0D
|
||||||
|
#define AMF3_VECTOR_UINT_MARKER 0x0E
|
||||||
|
#define AMF3_VECTOR_DOUBLE_MARKER 0x0F
|
||||||
|
#define AMF3_VECTOR_OBJECT_MARKER 0x10
|
||||||
|
#define AMF3_DICT_MARKER 0x11
|
||||||
|
|
||||||
|
// Other AMF3 Markers
|
||||||
|
#define AMF3_EMPTY_STRING 0x01
|
||||||
|
#define AMF3_DYNAMIC_OBJECT 0x0B
|
||||||
|
#define AMF3_CLOSE_DYNAMIC_OBJECT 0x01
|
||||||
|
#define AMF3_CLOSE_DYNAMIC_ARRAY 0x01
|
||||||
|
|
||||||
|
// Other Constants
|
||||||
|
#define MAX_INTEGER 268435455
|
||||||
|
#define MIN_INTEGER -268435456
|
||||||
|
#define INITIAL_STREAM_LENGTH 128 // Initial buffer length for serializer output
|
||||||
|
#define MAX_STREAM_LENGTH 10*1024*1024 // Let's cap it at 10MB for now
|
||||||
|
#define MAX_ARRAY_PREALLOC 100000
|
||||||
776
vendor/gems/RocketAMF-1.0.0/ext/rocketamf_ext/deserializer.c
vendored
Normal file
776
vendor/gems/RocketAMF-1.0.0/ext/rocketamf_ext/deserializer.c
vendored
Normal file
|
|
@ -0,0 +1,776 @@
|
||||||
|
#include "deserializer.h"
|
||||||
|
#include "constants.h"
|
||||||
|
|
||||||
|
#define DES_BOUNDS_CHECK(des, i) if(des->pos + (i) > des->size || des->pos + (i) < des->pos) rb_raise(rb_eRangeError, "reading %lu bytes is beyond end of source: %ld (pos), %ld (size)", (unsigned long)(i), des->pos, des->size);
|
||||||
|
|
||||||
|
extern VALUE mRocketAMF;
|
||||||
|
extern VALUE mRocketAMFExt;
|
||||||
|
extern VALUE cDeserializer;
|
||||||
|
extern VALUE cStringIO;
|
||||||
|
extern VALUE sym_class_name;
|
||||||
|
extern VALUE sym_members;
|
||||||
|
extern VALUE sym_externalizable;
|
||||||
|
extern VALUE sym_dynamic;
|
||||||
|
ID id_get_ruby_obj;
|
||||||
|
ID id_populate_ruby_obj;
|
||||||
|
|
||||||
|
static VALUE des0_deserialize(VALUE self, char type);
|
||||||
|
static VALUE des3_deserialize(VALUE self);
|
||||||
|
|
||||||
|
char des_read_byte(AMF_DESERIALIZER *des) {
|
||||||
|
DES_BOUNDS_CHECK(des, 1);
|
||||||
|
des->pos++;
|
||||||
|
return des->stream[des->pos-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
char des_read_ahead_byte(AMF_DESERIALIZER *des) {
|
||||||
|
DES_BOUNDS_CHECK(des, 1);
|
||||||
|
return des->stream[des->pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
int des_read_uint16(AMF_DESERIALIZER *des) {
|
||||||
|
DES_BOUNDS_CHECK(des, 2);
|
||||||
|
const unsigned char *str = (unsigned char*)(des->stream) + des->pos;
|
||||||
|
des->pos += 2;
|
||||||
|
return ((str[0] << 8) | str[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int des_read_uint32(AMF_DESERIALIZER *des) {
|
||||||
|
DES_BOUNDS_CHECK(des, 4);
|
||||||
|
const unsigned char *str = (unsigned char*)(des->stream) + des->pos;
|
||||||
|
des->pos += 4;
|
||||||
|
return ((str[0] << 24) | (str[1] << 16) | (str[2] << 8) | str[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read a network double
|
||||||
|
*/
|
||||||
|
double des_read_double(AMF_DESERIALIZER *des) {
|
||||||
|
DES_BOUNDS_CHECK(des, 8);
|
||||||
|
union aligned {
|
||||||
|
double dval;
|
||||||
|
char cval[8];
|
||||||
|
} d;
|
||||||
|
const char *str = des->stream + des->pos;
|
||||||
|
des->pos +=8;
|
||||||
|
|
||||||
|
#ifdef WORDS_BIGENDIAN
|
||||||
|
memcpy(d.cval, str, 8);
|
||||||
|
#else
|
||||||
|
d.cval[0] = str[7];
|
||||||
|
d.cval[1] = str[6];
|
||||||
|
d.cval[2] = str[5];
|
||||||
|
d.cval[3] = str[4];
|
||||||
|
d.cval[4] = str[3];
|
||||||
|
d.cval[5] = str[2];
|
||||||
|
d.cval[6] = str[1];
|
||||||
|
d.cval[7] = str[0];
|
||||||
|
#endif
|
||||||
|
return d.dval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read an AMF3 style integer
|
||||||
|
*/
|
||||||
|
int des_read_int(AMF_DESERIALIZER *des) {
|
||||||
|
int result = 0, byte_cnt = 0;
|
||||||
|
DES_BOUNDS_CHECK(des, 1);
|
||||||
|
unsigned char byte = des->stream[des->pos++];
|
||||||
|
|
||||||
|
while(byte & 0x80 && byte_cnt < 3) {
|
||||||
|
result <<= 7;
|
||||||
|
result |= byte & 0x7f;
|
||||||
|
DES_BOUNDS_CHECK(des, 1);
|
||||||
|
byte = des->stream[des->pos++];
|
||||||
|
byte_cnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byte_cnt < 3) {
|
||||||
|
result <<= 7;
|
||||||
|
result |= byte & 0x7F;
|
||||||
|
} else {
|
||||||
|
result <<= 8;
|
||||||
|
result |= byte & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result & 0x10000000) {
|
||||||
|
result -= 0x20000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read a string and then force the encoding to UTF 8 if running ruby 1.9
|
||||||
|
*/
|
||||||
|
VALUE des_read_string(AMF_DESERIALIZER *des, unsigned int len) {
|
||||||
|
DES_BOUNDS_CHECK(des, len);
|
||||||
|
VALUE str = rb_str_new(des->stream + des->pos, len);
|
||||||
|
#ifdef HAVE_RB_STR_ENCODE
|
||||||
|
rb_encoding *utf8 = rb_utf8_encoding();
|
||||||
|
rb_enc_associate(str, utf8);
|
||||||
|
ENC_CODERANGE_CLEAR(str);
|
||||||
|
#endif
|
||||||
|
des->pos += len;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the source of the amf reader to a StringIO object, creating a new one to
|
||||||
|
* wrap the source if it's only a string
|
||||||
|
*/
|
||||||
|
void des_set_src(AMF_DESERIALIZER *des, VALUE src) {
|
||||||
|
VALUE klass = CLASS_OF(src);
|
||||||
|
if(klass == cStringIO) {
|
||||||
|
VALUE str = rb_funcall(src, rb_intern("string"), 0);
|
||||||
|
des->src = src;
|
||||||
|
des->stream = RSTRING_PTR(str);
|
||||||
|
des->pos = NUM2LONG(rb_funcall(src, rb_intern("pos"), 0));
|
||||||
|
des->size = RSTRING_LEN(str);
|
||||||
|
} else if(klass == rb_cString) {
|
||||||
|
VALUE args[1] = {src};
|
||||||
|
des->src = rb_class_new_instance(1, args, cStringIO);
|
||||||
|
des->stream = RSTRING_PTR(src);
|
||||||
|
des->pos = 0;
|
||||||
|
des->size = RSTRING_LEN(src);
|
||||||
|
} else {
|
||||||
|
rb_raise(rb_eArgError, "Invalid source type to deserialize from");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(des->pos >= des->size) rb_raise(rb_eRangeError, "already at the end of the source");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create AMF3 deserializer and copy source data over to it, before calling
|
||||||
|
* AMF3 internal deserialize function
|
||||||
|
*/
|
||||||
|
static VALUE des0_read_amf3(VALUE self) {
|
||||||
|
AMF_DESERIALIZER *des;
|
||||||
|
Data_Get_Struct(self, AMF_DESERIALIZER, des);
|
||||||
|
des->version = 3;
|
||||||
|
des->str_cache = rb_ary_new();
|
||||||
|
des->trait_cache = rb_ary_new();
|
||||||
|
return des3_deserialize(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reads an AMF0 hash
|
||||||
|
*/
|
||||||
|
static void des0_read_props(VALUE self, VALUE hash) {
|
||||||
|
AMF_DESERIALIZER *des;
|
||||||
|
Data_Get_Struct(self, AMF_DESERIALIZER, des);
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
int len = des_read_uint16(des);
|
||||||
|
if(len == 0 && des_read_ahead_byte(des) == AMF0_OBJECT_END_MARKER) {
|
||||||
|
// Don't create a ruby string if this is really the object end
|
||||||
|
des_read_byte(des); // Read type byte
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
VALUE key = des_read_string(des, len);
|
||||||
|
char type = des_read_byte(des);
|
||||||
|
rb_hash_aset(hash, key, des0_deserialize(self, type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE des0_read_object(VALUE self) {
|
||||||
|
AMF_DESERIALIZER *des;
|
||||||
|
Data_Get_Struct(self, AMF_DESERIALIZER, des);
|
||||||
|
|
||||||
|
// Create object and add to cache
|
||||||
|
VALUE obj = rb_funcall(des->class_mapper, id_get_ruby_obj, 1, rb_str_new(NULL, 0));
|
||||||
|
rb_ary_push(des->obj_cache, obj);
|
||||||
|
|
||||||
|
// Populate object
|
||||||
|
VALUE props = rb_hash_new();
|
||||||
|
des0_read_props(self, props);
|
||||||
|
rb_funcall(des->class_mapper, id_populate_ruby_obj, 2, obj, props);
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE des0_read_typed_object(VALUE self) {
|
||||||
|
AMF_DESERIALIZER *des;
|
||||||
|
Data_Get_Struct(self, AMF_DESERIALIZER, des);
|
||||||
|
|
||||||
|
// Create object and add to cache
|
||||||
|
VALUE class_name = des_read_string(des, des_read_uint16(des));
|
||||||
|
VALUE obj = rb_funcall(des->class_mapper, id_get_ruby_obj, 1, class_name);
|
||||||
|
rb_ary_push(des->obj_cache, obj);
|
||||||
|
|
||||||
|
// Populate object
|
||||||
|
VALUE props = rb_hash_new();
|
||||||
|
des0_read_props(self, props);
|
||||||
|
rb_funcall(des->class_mapper, id_populate_ruby_obj, 2, obj, props);
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE des0_read_hash(VALUE self) {
|
||||||
|
AMF_DESERIALIZER *des;
|
||||||
|
Data_Get_Struct(self, AMF_DESERIALIZER, des);
|
||||||
|
des_read_uint32(des); // Hash size, but there's no optimization I can perform with this
|
||||||
|
VALUE obj = rb_hash_new();
|
||||||
|
rb_ary_push(des->obj_cache, obj);
|
||||||
|
des0_read_props(self, obj);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE des0_read_array(VALUE self) {
|
||||||
|
AMF_DESERIALIZER *des;
|
||||||
|
Data_Get_Struct(self, AMF_DESERIALIZER, des);
|
||||||
|
|
||||||
|
// Limit size of pre-allocation to force remote user to actually send data,
|
||||||
|
// rather than just sending a size of 2**32-1 and nothing afterwards to
|
||||||
|
// crash the server
|
||||||
|
unsigned int len = des_read_uint32(des);
|
||||||
|
VALUE ary = rb_ary_new2(len < MAX_ARRAY_PREALLOC ? len : MAX_ARRAY_PREALLOC);
|
||||||
|
rb_ary_push(des->obj_cache, ary);
|
||||||
|
|
||||||
|
unsigned int i;
|
||||||
|
for(i = 0; i < len; i++) {
|
||||||
|
rb_ary_push(ary, des0_deserialize(self, des_read_byte(des)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ary;
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE des0_read_time(VALUE self) {
|
||||||
|
AMF_DESERIALIZER *des;
|
||||||
|
Data_Get_Struct(self, AMF_DESERIALIZER, des);
|
||||||
|
double milli = des_read_double(des);
|
||||||
|
des_read_uint16(des); // Timezone - unused
|
||||||
|
time_t sec = milli/1000.0;
|
||||||
|
time_t micro = (milli-sec*1000)*1000;
|
||||||
|
return rb_time_new(sec, micro);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Internal C deserialize call. Takes deserializer and a char for the type
|
||||||
|
* marker.
|
||||||
|
*/
|
||||||
|
static VALUE des0_deserialize(VALUE self, char type) {
|
||||||
|
AMF_DESERIALIZER *des;
|
||||||
|
Data_Get_Struct(self, AMF_DESERIALIZER, des);
|
||||||
|
|
||||||
|
long tmp;
|
||||||
|
VALUE ret = Qnil;
|
||||||
|
switch(type) {
|
||||||
|
case AMF0_STRING_MARKER:
|
||||||
|
ret = des_read_string(des, des_read_uint16(des));
|
||||||
|
break;
|
||||||
|
case AMF0_AMF3_MARKER:
|
||||||
|
ret = des0_read_amf3(self);
|
||||||
|
break;
|
||||||
|
case AMF0_NUMBER_MARKER:
|
||||||
|
ret = rb_float_new(des_read_double(des));
|
||||||
|
break;
|
||||||
|
case AMF0_BOOLEAN_MARKER:
|
||||||
|
ret = des_read_byte(des) == 0 ? Qfalse : Qtrue;
|
||||||
|
break;
|
||||||
|
case AMF0_NULL_MARKER:
|
||||||
|
case AMF0_UNDEFINED_MARKER:
|
||||||
|
case AMF0_UNSUPPORTED_MARKER:
|
||||||
|
ret = Qnil;
|
||||||
|
break;
|
||||||
|
case AMF0_OBJECT_MARKER:
|
||||||
|
ret = des0_read_object(self);
|
||||||
|
break;
|
||||||
|
case AMF0_TYPED_OBJECT_MARKER:
|
||||||
|
ret = des0_read_typed_object(self);
|
||||||
|
break;
|
||||||
|
case AMF0_HASH_MARKER:
|
||||||
|
ret = des0_read_hash(self);
|
||||||
|
break;
|
||||||
|
case AMF0_STRICT_ARRAY_MARKER:
|
||||||
|
ret = des0_read_array(self);
|
||||||
|
break;
|
||||||
|
case AMF0_REFERENCE_MARKER:
|
||||||
|
tmp = des_read_uint16(des);
|
||||||
|
if(tmp >= RARRAY_LEN(des->obj_cache)) rb_raise(rb_eRangeError, "reference index beyond end");
|
||||||
|
ret = RARRAY_PTR(des->obj_cache)[tmp];
|
||||||
|
break;
|
||||||
|
case AMF0_DATE_MARKER:
|
||||||
|
ret = des0_read_time(self);
|
||||||
|
break;
|
||||||
|
case AMF0_XML_MARKER:
|
||||||
|
case AMF0_LONG_STRING_MARKER:
|
||||||
|
ret = des_read_string(des, des_read_uint32(des));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
rb_raise(rb_eRuntimeError, "Not supported: %d", type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE des3_read_string(AMF_DESERIALIZER *des) {
|
||||||
|
int header = des_read_int(des);
|
||||||
|
if((header & 1) == 0) {
|
||||||
|
header >>= 1;
|
||||||
|
if(header >= RARRAY_LEN(des->str_cache)) rb_raise(rb_eRangeError, "str reference index beyond end");
|
||||||
|
return RARRAY_PTR(des->str_cache)[header];
|
||||||
|
} else {
|
||||||
|
VALUE str = des_read_string(des, header >> 1);
|
||||||
|
if(RSTRING_LEN(str) > 0) rb_ary_push(des->str_cache, str);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Same as des3_read_string, but XML uses the object cache, rather than the
|
||||||
|
* string cache
|
||||||
|
*/
|
||||||
|
static VALUE des3_read_xml(VALUE self) {
|
||||||
|
AMF_DESERIALIZER *des;
|
||||||
|
Data_Get_Struct(self, AMF_DESERIALIZER, des);
|
||||||
|
|
||||||
|
int header = des_read_int(des);
|
||||||
|
if((header & 1) == 0) {
|
||||||
|
header >>= 1;
|
||||||
|
if(header >= RARRAY_LEN(des->obj_cache)) rb_raise(rb_eRangeError, "obj reference index beyond end");
|
||||||
|
return RARRAY_PTR(des->obj_cache)[header];
|
||||||
|
} else {
|
||||||
|
VALUE str = des_read_string(des, header >> 1);
|
||||||
|
if(RSTRING_LEN(str) > 0) rb_ary_push(des->obj_cache, str);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE des3_read_object(VALUE self) {
|
||||||
|
AMF_DESERIALIZER *des;
|
||||||
|
Data_Get_Struct(self, AMF_DESERIALIZER, des);
|
||||||
|
|
||||||
|
int header = des_read_int(des);
|
||||||
|
if((header & 1) == 0) {
|
||||||
|
header >>= 1;
|
||||||
|
if(header >= RARRAY_LEN(des->obj_cache)) rb_raise(rb_eRangeError, "obj reference index beyond end");
|
||||||
|
return RARRAY_PTR(des->obj_cache)[header];
|
||||||
|
} else {
|
||||||
|
VALUE externalizable, dynamic, members, class_name, traits;
|
||||||
|
long i, members_len;
|
||||||
|
|
||||||
|
// Parse traits
|
||||||
|
header >>= 1;
|
||||||
|
if((header & 1) == 0) {
|
||||||
|
header >>= 1;
|
||||||
|
if(header >= RARRAY_LEN(des->trait_cache)) rb_raise(rb_eRangeError, "trait reference index beyond end");
|
||||||
|
traits = RARRAY_PTR(des->trait_cache)[header];
|
||||||
|
externalizable = rb_hash_aref(traits, sym_externalizable);
|
||||||
|
dynamic = rb_hash_aref(traits, sym_dynamic);
|
||||||
|
members = rb_hash_aref(traits, sym_members);
|
||||||
|
members_len = members == Qnil ? 0 : RARRAY_LEN(members);
|
||||||
|
class_name = rb_hash_aref(traits, sym_class_name);
|
||||||
|
} else {
|
||||||
|
externalizable = (header & 2) != 0 ? Qtrue : Qfalse;
|
||||||
|
dynamic = (header & 4) != 0 ? Qtrue : Qfalse;
|
||||||
|
members_len = header >> 3;
|
||||||
|
class_name = des3_read_string(des);
|
||||||
|
|
||||||
|
members = rb_ary_new2(members_len);
|
||||||
|
for(i = 0; i < members_len; i++) rb_ary_push(members, des3_read_string(des));
|
||||||
|
|
||||||
|
traits = rb_hash_new();
|
||||||
|
rb_hash_aset(traits, sym_externalizable, externalizable);
|
||||||
|
rb_hash_aset(traits, sym_dynamic, dynamic);
|
||||||
|
rb_hash_aset(traits, sym_members, members);
|
||||||
|
rb_hash_aset(traits, sym_class_name, class_name);
|
||||||
|
rb_ary_push(des->trait_cache, traits);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optimization for deserializing ArrayCollection
|
||||||
|
if(strcmp(RSTRING_PTR(class_name), "flex.messaging.io.ArrayCollection") == 0) {
|
||||||
|
VALUE arr = des3_deserialize(self); // Adds ArrayCollection array to object cache automatically
|
||||||
|
rb_ary_push(des->obj_cache, arr); // Add again for ArrayCollection source array
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE obj = rb_funcall(des->class_mapper, id_get_ruby_obj, 1, class_name);
|
||||||
|
rb_ary_push(des->obj_cache, obj);
|
||||||
|
|
||||||
|
if(externalizable == Qtrue) {
|
||||||
|
rb_funcall(des->src, rb_intern("pos="), 1, LONG2NUM(des->pos)); // Update source StringIO pos
|
||||||
|
rb_funcall(obj, rb_intern("read_external"), 1, self);
|
||||||
|
des->pos = NUM2LONG(rb_funcall(des->src, rb_intern("pos"), 0)); // Update from source
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE props = rb_hash_new();
|
||||||
|
for(i = 0; i < members_len; i++) {
|
||||||
|
rb_hash_aset(props, RARRAY_PTR(members)[i], des3_deserialize(self));
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE dynamic_props = Qnil;
|
||||||
|
if(dynamic == Qtrue) {
|
||||||
|
dynamic_props = rb_hash_new();
|
||||||
|
while(1) {
|
||||||
|
VALUE key = des3_read_string(des);
|
||||||
|
if(RSTRING_LEN(key) == 0) break;
|
||||||
|
rb_hash_aset(dynamic_props, key, des3_deserialize(self));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rb_funcall(des->class_mapper, id_populate_ruby_obj, 3, obj, props, dynamic_props);
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE des3_read_array(VALUE self) {
|
||||||
|
AMF_DESERIALIZER *des;
|
||||||
|
Data_Get_Struct(self, AMF_DESERIALIZER, des);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
int header = des_read_int(des);
|
||||||
|
if((header & 1) == 0) {
|
||||||
|
header >>= 1;
|
||||||
|
if(header >= RARRAY_LEN(des->obj_cache)) rb_raise(rb_eRangeError, "obj reference index beyond end");
|
||||||
|
return RARRAY_PTR(des->obj_cache)[header];
|
||||||
|
} else {
|
||||||
|
header >>= 1;
|
||||||
|
VALUE obj;
|
||||||
|
VALUE key = des3_read_string(des);
|
||||||
|
if(key == Qnil) rb_raise(rb_eRangeError, "key is Qnil");
|
||||||
|
if(RSTRING_LEN(key) != 0) {
|
||||||
|
obj = rb_hash_new();
|
||||||
|
rb_ary_push(des->obj_cache, obj);
|
||||||
|
while(RSTRING_LEN(key) != 0) {
|
||||||
|
rb_hash_aset(obj, key, des3_deserialize(self));
|
||||||
|
key = des3_read_string(des);
|
||||||
|
}
|
||||||
|
for(i = 0; i < header; i++) {
|
||||||
|
rb_hash_aset(obj, INT2FIX(i), des3_deserialize(self));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Limit size of pre-allocation to force remote user to actually send data,
|
||||||
|
// rather than just sending a size of 2**32-1 and nothing afterwards to
|
||||||
|
// crash the server
|
||||||
|
obj = rb_ary_new2(header < MAX_ARRAY_PREALLOC ? header : MAX_ARRAY_PREALLOC);
|
||||||
|
rb_ary_push(des->obj_cache, obj);
|
||||||
|
for(i = 0; i < header; i++) {
|
||||||
|
rb_ary_push(obj, des3_deserialize(self));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE des3_read_time(VALUE self) {
|
||||||
|
AMF_DESERIALIZER *des;
|
||||||
|
Data_Get_Struct(self, AMF_DESERIALIZER, des);
|
||||||
|
|
||||||
|
int header = des_read_int(des);
|
||||||
|
if((header & 1) == 0) {
|
||||||
|
header >>= 1;
|
||||||
|
if(header >= RARRAY_LEN(des->obj_cache)) rb_raise(rb_eRangeError, "obj reference index beyond end");
|
||||||
|
return RARRAY_PTR(des->obj_cache)[header];
|
||||||
|
} else {
|
||||||
|
double milli = des_read_double(des);
|
||||||
|
time_t sec = milli/1000.0;
|
||||||
|
time_t micro = (milli-sec*1000)*1000;
|
||||||
|
VALUE time = rb_time_new(sec, micro);
|
||||||
|
rb_ary_push(des->obj_cache, time);
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE des3_read_byte_array(VALUE self) {
|
||||||
|
AMF_DESERIALIZER *des;
|
||||||
|
Data_Get_Struct(self, AMF_DESERIALIZER, des);
|
||||||
|
|
||||||
|
int header = des_read_int(des);
|
||||||
|
if((header & 1) == 0) {
|
||||||
|
header >>= 1;
|
||||||
|
if(header >= RARRAY_LEN(des->obj_cache)) rb_raise(rb_eRangeError, "obj reference index beyond end");
|
||||||
|
return RARRAY_PTR(des->obj_cache)[header];
|
||||||
|
} else {
|
||||||
|
header >>= 1;
|
||||||
|
VALUE args[1] = {des_read_string(des, header)};
|
||||||
|
#ifdef HAVE_RB_STR_ENCODE
|
||||||
|
// Need to force encoding to ASCII-8BIT
|
||||||
|
rb_encoding *ascii = rb_ascii8bit_encoding();
|
||||||
|
rb_enc_associate(args[0], ascii);
|
||||||
|
ENC_CODERANGE_CLEAR(args[0]);
|
||||||
|
#endif
|
||||||
|
VALUE ba = rb_class_new_instance(1, args, cStringIO);
|
||||||
|
rb_ary_push(des->obj_cache, ba);
|
||||||
|
return ba;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE des3_read_dict(VALUE self) {
|
||||||
|
AMF_DESERIALIZER *des;
|
||||||
|
Data_Get_Struct(self, AMF_DESERIALIZER, des);
|
||||||
|
|
||||||
|
int header = des_read_int(des);
|
||||||
|
if((header & 1) == 0) {
|
||||||
|
header >>= 1;
|
||||||
|
if(header >= RARRAY_LEN(des->obj_cache)) rb_raise(rb_eRangeError, "obj reference index beyond end");
|
||||||
|
return RARRAY_PTR(des->obj_cache)[header];
|
||||||
|
} else {
|
||||||
|
header >>= 1;
|
||||||
|
|
||||||
|
VALUE dict = rb_hash_new();
|
||||||
|
rb_ary_push(des->obj_cache, dict);
|
||||||
|
|
||||||
|
des_read_byte(des); // Weak Keys: Not supported in ruby
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < header; i++) {
|
||||||
|
VALUE key = des3_deserialize(self);
|
||||||
|
VALUE val = des3_deserialize(self);
|
||||||
|
rb_hash_aset(dict, key, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE des3_read_vec(VALUE self, char type) {
|
||||||
|
AMF_DESERIALIZER *des;
|
||||||
|
Data_Get_Struct(self, AMF_DESERIALIZER, des);
|
||||||
|
|
||||||
|
int header = des_read_int(des);
|
||||||
|
if((header & 1) == 0) {
|
||||||
|
header >>= 1;
|
||||||
|
if(header >= RARRAY_LEN(des->obj_cache)) rb_raise(rb_eRangeError, "obj reference index beyond end");
|
||||||
|
return RARRAY_PTR(des->obj_cache)[header];
|
||||||
|
} else {
|
||||||
|
header >>= 1;
|
||||||
|
|
||||||
|
// Limit size of pre-allocation to force remote user to actually send data,
|
||||||
|
// rather than just sending a size of 2**32-1 and nothing afterwards to
|
||||||
|
// crash the server
|
||||||
|
VALUE vec = rb_ary_new2(header < MAX_ARRAY_PREALLOC ? header : MAX_ARRAY_PREALLOC);
|
||||||
|
rb_ary_push(des->obj_cache, vec);
|
||||||
|
|
||||||
|
des_read_byte(des); // Fixed Length: Not supported in ruby
|
||||||
|
|
||||||
|
// On 32-bit ARCH, FIXNUM has a limit of 2**31-1, resulting in truncation of large ints/uints
|
||||||
|
int i;
|
||||||
|
switch(type) {
|
||||||
|
case AMF3_VECTOR_INT_MARKER:
|
||||||
|
for(i = 0; i < header; i++) {
|
||||||
|
int ival = des_read_uint32(des);
|
||||||
|
rb_ary_push(vec, INT2FIX(ival));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AMF3_VECTOR_UINT_MARKER:
|
||||||
|
for(i = 0; i < header; i++) {
|
||||||
|
rb_ary_push(vec, INT2FIX(des_read_uint32(des)));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AMF3_VECTOR_DOUBLE_MARKER:
|
||||||
|
for(i = 0; i < header; i++) {
|
||||||
|
rb_ary_push(vec, rb_float_new(des_read_double(des)));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AMF3_VECTOR_OBJECT_MARKER:
|
||||||
|
des3_read_string(des); // Class name of objects - ignored
|
||||||
|
for(i = 0; i < header; i++) {
|
||||||
|
rb_ary_push(vec, des3_deserialize(self));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return vec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Internal deserialize call - unlike des0_deserialize, it reads the type
|
||||||
|
* itself, due to minor changes in the specs that make that modification
|
||||||
|
* unnecessary.
|
||||||
|
*/
|
||||||
|
static VALUE des3_deserialize(VALUE self) {
|
||||||
|
AMF_DESERIALIZER *des;
|
||||||
|
Data_Get_Struct(self, AMF_DESERIALIZER, des);
|
||||||
|
|
||||||
|
char type = des_read_byte(des);
|
||||||
|
VALUE ret = Qnil;
|
||||||
|
switch(type) {
|
||||||
|
case AMF3_UNDEFINED_MARKER:
|
||||||
|
case AMF3_NULL_MARKER:
|
||||||
|
ret = Qnil;
|
||||||
|
break;
|
||||||
|
case AMF3_FALSE_MARKER:
|
||||||
|
ret = Qfalse;
|
||||||
|
break;
|
||||||
|
case AMF3_TRUE_MARKER:
|
||||||
|
ret = Qtrue;
|
||||||
|
break;
|
||||||
|
case AMF3_INTEGER_MARKER:
|
||||||
|
ret = INT2FIX(des_read_int(des));
|
||||||
|
break;
|
||||||
|
case AMF3_DOUBLE_MARKER:
|
||||||
|
ret = rb_float_new(des_read_double(des));
|
||||||
|
break;
|
||||||
|
case AMF3_STRING_MARKER:
|
||||||
|
ret = des3_read_string(des);
|
||||||
|
break;
|
||||||
|
case AMF3_ARRAY_MARKER:
|
||||||
|
ret = des3_read_array(self);
|
||||||
|
break;
|
||||||
|
case AMF3_OBJECT_MARKER:
|
||||||
|
ret = des3_read_object(self);
|
||||||
|
break;
|
||||||
|
case AMF3_DATE_MARKER:
|
||||||
|
ret = des3_read_time(self);
|
||||||
|
break;
|
||||||
|
case AMF3_XML_DOC_MARKER:
|
||||||
|
case AMF3_XML_MARKER:
|
||||||
|
ret = des3_read_xml(self);
|
||||||
|
break;
|
||||||
|
case AMF3_BYTE_ARRAY_MARKER:
|
||||||
|
ret = des3_read_byte_array(self);
|
||||||
|
break;
|
||||||
|
case AMF3_VECTOR_INT_MARKER:
|
||||||
|
case AMF3_VECTOR_UINT_MARKER:
|
||||||
|
case AMF3_VECTOR_DOUBLE_MARKER:
|
||||||
|
case AMF3_VECTOR_OBJECT_MARKER:
|
||||||
|
ret = des3_read_vec(self, type);
|
||||||
|
break;
|
||||||
|
case AMF3_DICT_MARKER:
|
||||||
|
ret = des3_read_dict(self);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
rb_raise(rb_eRuntimeError, "Not supported: %d", type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mark the reader and its source. If caches are populated mark them as well.
|
||||||
|
*/
|
||||||
|
static void des_mark(AMF_DESERIALIZER *des) {
|
||||||
|
if(!des) return;
|
||||||
|
rb_gc_mark(des->class_mapper);
|
||||||
|
rb_gc_mark(des->src);
|
||||||
|
if(des->obj_cache) rb_gc_mark(des->obj_cache);
|
||||||
|
if(des->str_cache) rb_gc_mark(des->str_cache);
|
||||||
|
if(des->trait_cache) rb_gc_mark(des->trait_cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free the reader. Don't need to free anything but the struct because we didn't
|
||||||
|
* alloc anything - source is from the ruby source object.
|
||||||
|
*/
|
||||||
|
static void des_free(AMF_DESERIALIZER *des) {
|
||||||
|
xfree(des);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create new struct and wrap with class
|
||||||
|
*/
|
||||||
|
static VALUE des_alloc(VALUE klass) {
|
||||||
|
AMF_DESERIALIZER *des = ALLOC(AMF_DESERIALIZER);
|
||||||
|
memset(des, 0, sizeof(AMF_DESERIALIZER));
|
||||||
|
return Data_Wrap_Struct(klass, des_mark, des_free, des);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initializer
|
||||||
|
*/
|
||||||
|
static VALUE des_initialize(VALUE self, VALUE class_mapper) {
|
||||||
|
AMF_DESERIALIZER *des;
|
||||||
|
Data_Get_Struct(self, AMF_DESERIALIZER, des);
|
||||||
|
des->class_mapper = class_mapper;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* ser.stream => StringIO
|
||||||
|
*
|
||||||
|
* Returns the source that the deserializer is reading from
|
||||||
|
*/
|
||||||
|
static VALUE des_source(VALUE self) {
|
||||||
|
AMF_DESERIALIZER *des;
|
||||||
|
Data_Get_Struct(self, AMF_DESERIALIZER, des);
|
||||||
|
return des->src;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* des.deserialize(amf_ver, str) => obj
|
||||||
|
* des.deserialize(amf_ver, StringIO) => obj
|
||||||
|
*
|
||||||
|
* Deserialize the string or StringIO from AMF to a ruby object.
|
||||||
|
*/
|
||||||
|
VALUE des_deserialize(VALUE self, VALUE ver, VALUE src) {
|
||||||
|
AMF_DESERIALIZER *des;
|
||||||
|
Data_Get_Struct(self, AMF_DESERIALIZER, des);
|
||||||
|
|
||||||
|
// Process version
|
||||||
|
int int_ver = FIX2INT(ver);
|
||||||
|
if(int_ver != 0 && int_ver != 3) rb_raise(rb_eArgError, "unsupported version %d", int_ver);
|
||||||
|
des->version = int_ver;
|
||||||
|
|
||||||
|
// Process source
|
||||||
|
if(src != Qnil) {
|
||||||
|
des_set_src(des, src);
|
||||||
|
} else if(!des->src) {
|
||||||
|
rb_raise(rb_eArgError, "Missing deserialization source");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deserialize from source
|
||||||
|
VALUE ret;
|
||||||
|
if(des->version == 0) {
|
||||||
|
des->obj_cache = rb_ary_new();
|
||||||
|
ret = des0_deserialize(self, des_read_byte(des));
|
||||||
|
} else {
|
||||||
|
des->obj_cache = rb_ary_new();
|
||||||
|
des->str_cache = rb_ary_new();
|
||||||
|
des->trait_cache = rb_ary_new();
|
||||||
|
ret = des3_deserialize(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update source position
|
||||||
|
rb_funcall(des->src, rb_intern("pos="), 1, LONG2NUM(des->pos)); // Update source StringIO pos
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* des.read_object => obj
|
||||||
|
*
|
||||||
|
* Reads an object from the deserializer's stream and returns it.
|
||||||
|
*/
|
||||||
|
VALUE des_read_object(VALUE self) {
|
||||||
|
AMF_DESERIALIZER *des;
|
||||||
|
Data_Get_Struct(self, AMF_DESERIALIZER, des);
|
||||||
|
|
||||||
|
// Update internal pos from source in case they've modified it
|
||||||
|
des->pos = NUM2LONG(rb_funcall(des->src, rb_intern("pos"), 0));
|
||||||
|
|
||||||
|
// Deserialize
|
||||||
|
VALUE ret;
|
||||||
|
if(des->version == 0) {
|
||||||
|
ret = des0_deserialize(self, des_read_byte(des));
|
||||||
|
} else {
|
||||||
|
ret = des3_deserialize(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update source position
|
||||||
|
rb_funcall(des->src, rb_intern("pos="), 1, LONG2NUM(des->pos)); // Update source StringIO pos
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Init_rocket_amf_deserializer() {
|
||||||
|
// Define Deserializer
|
||||||
|
cDeserializer = rb_define_class_under(mRocketAMFExt, "Deserializer", rb_cObject);
|
||||||
|
rb_define_alloc_func(cDeserializer, des_alloc);
|
||||||
|
rb_define_method(cDeserializer, "initialize", des_initialize, 1);
|
||||||
|
rb_define_method(cDeserializer, "source", des_source, 0);
|
||||||
|
rb_define_method(cDeserializer, "deserialize", des_deserialize, 2);
|
||||||
|
rb_define_method(cDeserializer, "read_object", des_read_object, 0);
|
||||||
|
|
||||||
|
// Get refs to commonly used symbols and ids
|
||||||
|
id_get_ruby_obj = rb_intern("get_ruby_obj");
|
||||||
|
id_populate_ruby_obj = rb_intern("populate_ruby_obj");
|
||||||
|
}
|
||||||
28
vendor/gems/RocketAMF-1.0.0/ext/rocketamf_ext/deserializer.h
vendored
Normal file
28
vendor/gems/RocketAMF-1.0.0/ext/rocketamf_ext/deserializer.h
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#include <ruby.h>
|
||||||
|
#ifdef HAVE_RB_STR_ENCODE
|
||||||
|
#include <ruby/encoding.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int version;
|
||||||
|
VALUE class_mapper;
|
||||||
|
VALUE src;
|
||||||
|
char* stream;
|
||||||
|
unsigned long pos;
|
||||||
|
unsigned long size;
|
||||||
|
VALUE obj_cache;
|
||||||
|
VALUE str_cache;
|
||||||
|
VALUE trait_cache;
|
||||||
|
} AMF_DESERIALIZER;
|
||||||
|
|
||||||
|
char des_read_byte(AMF_DESERIALIZER *des);
|
||||||
|
char des_read_ahead_byte(AMF_DESERIALIZER *des);
|
||||||
|
int des_read_uint16(AMF_DESERIALIZER *des);
|
||||||
|
unsigned int des_read_uint32(AMF_DESERIALIZER *des);
|
||||||
|
double des_read_double(AMF_DESERIALIZER *des);
|
||||||
|
int des_read_int(AMF_DESERIALIZER *des);
|
||||||
|
VALUE des_read_string(AMF_DESERIALIZER *des, unsigned int len);
|
||||||
|
VALUE des_read_sym(AMF_DESERIALIZER *des, unsigned int len);
|
||||||
|
void des_set_src(AMF_DESERIALIZER *des, VALUE src);
|
||||||
|
|
||||||
|
VALUE des_deserialize(VALUE self, VALUE ver, VALUE src);
|
||||||
18
vendor/gems/RocketAMF-1.0.0/ext/rocketamf_ext/extconf.rb
vendored
Normal file
18
vendor/gems/RocketAMF-1.0.0/ext/rocketamf_ext/extconf.rb
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
require 'mkmf'
|
||||||
|
|
||||||
|
# Disable the native extension by creating an empty Makefile on JRuby
|
||||||
|
if defined? JRUBY_VERSION
|
||||||
|
message "Generating phony Makefile for JRuby so the gem installs"
|
||||||
|
mfile = File.join(File.dirname(__FILE__), 'Makefile')
|
||||||
|
File.open(mfile, 'w') {|f| f.write dummy_makefile(File.dirname(__FILE__)) }
|
||||||
|
exit 0
|
||||||
|
end
|
||||||
|
|
||||||
|
if enable_config("sort-props", false)
|
||||||
|
$defs.push("-DSORT_PROPS") unless $defs.include? "-DSORT_PROPS"
|
||||||
|
end
|
||||||
|
have_func('rb_str_encode')
|
||||||
|
|
||||||
|
$CFLAGS += " -Wall"
|
||||||
|
|
||||||
|
create_makefile('rocketamf_ext')
|
||||||
184
vendor/gems/RocketAMF-1.0.0/ext/rocketamf_ext/remoting.c
vendored
Normal file
184
vendor/gems/RocketAMF-1.0.0/ext/rocketamf_ext/remoting.c
vendored
Normal file
|
|
@ -0,0 +1,184 @@
|
||||||
|
#include "deserializer.h"
|
||||||
|
#include "serializer.h"
|
||||||
|
#include "constants.h"
|
||||||
|
|
||||||
|
extern VALUE mRocketAMF;
|
||||||
|
extern VALUE mRocketAMFExt;
|
||||||
|
extern VALUE cDeserializer;
|
||||||
|
extern VALUE cSerializer;
|
||||||
|
VALUE cRocketAMFHeader;
|
||||||
|
VALUE cRocketAMFMessage;
|
||||||
|
VALUE cRocketAMFAbstractMessage;
|
||||||
|
ID id_amf_version;
|
||||||
|
ID id_headers;
|
||||||
|
ID id_messages;
|
||||||
|
ID id_data;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* env.populate_from_stream(stream, class_mapper=nil)
|
||||||
|
*
|
||||||
|
* Included into RocketAMF::Envelope, this method handles deserializing an AMF
|
||||||
|
* request/response into the envelope
|
||||||
|
*/
|
||||||
|
static VALUE env_populate_from_stream(int argc, VALUE *argv, VALUE self) {
|
||||||
|
static VALUE cClassMapper = 0;
|
||||||
|
if(cClassMapper == 0) cClassMapper = rb_const_get(mRocketAMF, rb_intern("ClassMapper"));
|
||||||
|
|
||||||
|
// Parse args
|
||||||
|
VALUE src;
|
||||||
|
VALUE class_mapper;
|
||||||
|
rb_scan_args(argc, argv, "11", &src, &class_mapper);
|
||||||
|
if(class_mapper == Qnil) class_mapper = rb_class_new_instance(0, NULL, cClassMapper);
|
||||||
|
|
||||||
|
// Create AMF0 deserializer
|
||||||
|
VALUE args[3];
|
||||||
|
args[0] = class_mapper;
|
||||||
|
VALUE des_rb = rb_class_new_instance(1, args, cDeserializer);
|
||||||
|
AMF_DESERIALIZER *des;
|
||||||
|
Data_Get_Struct(des_rb, AMF_DESERIALIZER, des);
|
||||||
|
des_set_src(des, src);
|
||||||
|
|
||||||
|
// Read amf version
|
||||||
|
int amf_ver = des_read_uint16(des);
|
||||||
|
|
||||||
|
// Read headers
|
||||||
|
VALUE headers = rb_hash_new();
|
||||||
|
int header_cnt = des_read_uint16(des);
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < header_cnt; i++) {
|
||||||
|
VALUE name = des_read_string(des, des_read_uint16(des));
|
||||||
|
VALUE must_understand = des_read_byte(des) != 0 ? Qtrue : Qfalse;
|
||||||
|
des_read_uint32(des); // Length is ignored
|
||||||
|
VALUE data = des_deserialize(des_rb, INT2FIX(0), Qnil);
|
||||||
|
|
||||||
|
args[0] = name;
|
||||||
|
args[1] = must_understand;
|
||||||
|
args[2] = data;
|
||||||
|
rb_hash_aset(headers, name, rb_class_new_instance(3, args, cRocketAMFHeader));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read messages
|
||||||
|
VALUE messages = rb_ary_new();
|
||||||
|
int message_cnt = des_read_uint16(des);
|
||||||
|
for(i = 0; i < message_cnt; i++) {
|
||||||
|
VALUE target_uri = des_read_string(des, des_read_uint16(des));
|
||||||
|
VALUE response_uri = des_read_string(des, des_read_uint16(des));
|
||||||
|
des_read_uint32(des); // Length is ignored
|
||||||
|
VALUE data = des_deserialize(des_rb, INT2FIX(0), Qnil);
|
||||||
|
|
||||||
|
// If they're using the flex remoting APIs, remove array wrapper
|
||||||
|
if(TYPE(data) == T_ARRAY && RARRAY_LEN(data) == 1 && rb_obj_is_kind_of(RARRAY_PTR(data)[0], cRocketAMFAbstractMessage) == Qtrue) {
|
||||||
|
data = RARRAY_PTR(data)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
args[0] = target_uri;
|
||||||
|
args[1] = response_uri;
|
||||||
|
args[2] = data;
|
||||||
|
rb_ary_push(messages, rb_class_new_instance(3, args, cRocketAMFMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate remoting object
|
||||||
|
rb_ivar_set(self, id_amf_version, INT2FIX(amf_ver));
|
||||||
|
rb_ivar_set(self, id_headers, headers);
|
||||||
|
rb_ivar_set(self, id_messages, messages);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* env.serialize(class_mapper=nil)
|
||||||
|
*
|
||||||
|
* Included into RocketAMF::Envelope, this method handles serializing an AMF
|
||||||
|
* request/response into a string
|
||||||
|
*/
|
||||||
|
static VALUE env_serialize(int argc, VALUE *argv, VALUE self) {
|
||||||
|
static VALUE cClassMapper = 0;
|
||||||
|
if(cClassMapper == 0) cClassMapper = rb_const_get(mRocketAMF, rb_intern("ClassMapper"));
|
||||||
|
|
||||||
|
// Parse args
|
||||||
|
VALUE class_mapper;
|
||||||
|
rb_scan_args(argc, argv, "01", &class_mapper);
|
||||||
|
if(class_mapper == Qnil) class_mapper = rb_class_new_instance(0, NULL, cClassMapper);
|
||||||
|
|
||||||
|
// Get instance variables
|
||||||
|
long amf_ver = FIX2LONG(rb_ivar_get(self, id_amf_version));
|
||||||
|
VALUE headers = rb_funcall(rb_ivar_get(self, id_headers), rb_intern("values"), 0); // Get array of header values
|
||||||
|
VALUE messages = rb_ivar_get(self, id_messages);
|
||||||
|
|
||||||
|
// Create AMF0 serializer
|
||||||
|
VALUE args[1] = {class_mapper};
|
||||||
|
VALUE ser_rb = rb_class_new_instance(1, args, cSerializer);
|
||||||
|
AMF_SERIALIZER *ser;
|
||||||
|
Data_Get_Struct(ser_rb, AMF_SERIALIZER, ser);
|
||||||
|
|
||||||
|
// Write version
|
||||||
|
ser_write_uint16(ser, amf_ver);
|
||||||
|
|
||||||
|
// Write headers
|
||||||
|
long header_cnt = RARRAY_LEN(headers);
|
||||||
|
ser_write_uint16(ser, header_cnt);
|
||||||
|
int i;
|
||||||
|
char *str;
|
||||||
|
long str_len;
|
||||||
|
for(i = 0; i < header_cnt; i++) {
|
||||||
|
VALUE header = RARRAY_PTR(headers)[i];
|
||||||
|
|
||||||
|
// Write header name
|
||||||
|
ser_get_string(rb_funcall(header, rb_intern("name"), 0), Qtrue, &str, &str_len);
|
||||||
|
ser_write_uint16(ser, str_len);
|
||||||
|
rb_str_buf_cat(ser->stream, str, str_len);
|
||||||
|
|
||||||
|
// Write understand flag
|
||||||
|
ser_write_byte(ser, rb_funcall(header, rb_intern("must_understand"), 0) == Qtrue ? 1 : 0);
|
||||||
|
|
||||||
|
// Serialize data
|
||||||
|
ser_write_uint32(ser, -1); // length of data - -1 if you don't know
|
||||||
|
ser_serialize(ser_rb, INT2FIX(0), rb_funcall(header, id_data, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write messages
|
||||||
|
long message_cnt = RARRAY_LEN(messages);
|
||||||
|
ser_write_uint16(ser, message_cnt);
|
||||||
|
for(i = 0; i < message_cnt; i++) {
|
||||||
|
VALUE message = RARRAY_PTR(messages)[i];
|
||||||
|
|
||||||
|
// Write target_uri
|
||||||
|
ser_get_string(rb_funcall(message, rb_intern("target_uri"), 0), Qtrue, &str, &str_len);
|
||||||
|
ser_write_uint16(ser, str_len);
|
||||||
|
rb_str_buf_cat(ser->stream, str, str_len);
|
||||||
|
|
||||||
|
// Write response_uri
|
||||||
|
ser_get_string(rb_funcall(message, rb_intern("response_uri"), 0), Qtrue, &str, &str_len);
|
||||||
|
ser_write_uint16(ser, str_len);
|
||||||
|
rb_str_buf_cat(ser->stream, str, str_len);
|
||||||
|
|
||||||
|
// Serialize data
|
||||||
|
ser_write_uint32(ser, -1); // length of data - -1 if you don't know
|
||||||
|
if(amf_ver == 3) {
|
||||||
|
ser_write_byte(ser, AMF0_AMF3_MARKER);
|
||||||
|
ser_serialize(ser_rb, INT2FIX(3), rb_funcall(message, id_data, 0));
|
||||||
|
} else {
|
||||||
|
ser_serialize(ser_rb, INT2FIX(0), rb_funcall(message, id_data, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ser->stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Init_rocket_amf_remoting() {
|
||||||
|
VALUE mEnvelope = rb_define_module_under(mRocketAMFExt, "Envelope");
|
||||||
|
rb_define_method(mEnvelope, "populate_from_stream", env_populate_from_stream, -1);
|
||||||
|
rb_define_method(mEnvelope, "serialize", env_serialize, -1);
|
||||||
|
|
||||||
|
// Get refs to commonly used symbols and ids
|
||||||
|
id_amf_version = rb_intern("@amf_version");
|
||||||
|
id_headers = rb_intern("@headers");
|
||||||
|
id_messages = rb_intern("@messages");
|
||||||
|
id_data = rb_intern("data");
|
||||||
|
cRocketAMFHeader = rb_const_get(mRocketAMF, rb_intern("Header"));
|
||||||
|
cRocketAMFMessage = rb_const_get(mRocketAMF, rb_intern("Message"));
|
||||||
|
cRocketAMFAbstractMessage = rb_const_get(rb_const_get(mRocketAMF, rb_intern("Values")), rb_intern("AbstractMessage"));
|
||||||
|
}
|
||||||
38
vendor/gems/RocketAMF-1.0.0/ext/rocketamf_ext/rocketamf_ext.c
vendored
Normal file
38
vendor/gems/RocketAMF-1.0.0/ext/rocketamf_ext/rocketamf_ext.c
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
#include <ruby.h>
|
||||||
|
|
||||||
|
VALUE mRocketAMF;
|
||||||
|
VALUE mRocketAMFExt;
|
||||||
|
VALUE cDeserializer;
|
||||||
|
VALUE cSerializer;
|
||||||
|
VALUE cStringIO;
|
||||||
|
VALUE cDate;
|
||||||
|
VALUE cDateTime;
|
||||||
|
VALUE sym_class_name;
|
||||||
|
VALUE sym_members;
|
||||||
|
VALUE sym_externalizable;
|
||||||
|
VALUE sym_dynamic;
|
||||||
|
|
||||||
|
void Init_rocket_amf_deserializer();
|
||||||
|
void Init_rocket_amf_serializer();
|
||||||
|
void Init_rocket_amf_fast_class_mapping();
|
||||||
|
void Init_rocket_amf_remoting();
|
||||||
|
|
||||||
|
void Init_rocketamf_ext() {
|
||||||
|
mRocketAMF = rb_define_module("RocketAMF");
|
||||||
|
mRocketAMFExt = rb_define_module_under(mRocketAMF, "Ext");
|
||||||
|
|
||||||
|
// Set up classes
|
||||||
|
Init_rocket_amf_deserializer();
|
||||||
|
Init_rocket_amf_serializer();
|
||||||
|
Init_rocket_amf_fast_class_mapping();
|
||||||
|
Init_rocket_amf_remoting();
|
||||||
|
|
||||||
|
// Get refs to commonly used symbols and ids
|
||||||
|
cStringIO = rb_const_get(rb_cObject, rb_intern("StringIO"));
|
||||||
|
cDate = rb_const_get(rb_cObject, rb_intern("Date"));
|
||||||
|
cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
|
||||||
|
sym_class_name = ID2SYM(rb_intern("class_name"));
|
||||||
|
sym_members = ID2SYM(rb_intern("members"));
|
||||||
|
sym_externalizable = ID2SYM(rb_intern("externalizable"));
|
||||||
|
sym_dynamic = ID2SYM(rb_intern("dynamic"));
|
||||||
|
}
|
||||||
834
vendor/gems/RocketAMF-1.0.0/ext/rocketamf_ext/serializer.c
vendored
Normal file
834
vendor/gems/RocketAMF-1.0.0/ext/rocketamf_ext/serializer.c
vendored
Normal file
|
|
@ -0,0 +1,834 @@
|
||||||
|
#include "serializer.h"
|
||||||
|
#include "constants.h"
|
||||||
|
#include "utility.h"
|
||||||
|
|
||||||
|
extern VALUE mRocketAMF;
|
||||||
|
extern VALUE mRocketAMFExt;
|
||||||
|
extern VALUE cSerializer;
|
||||||
|
extern VALUE cStringIO;
|
||||||
|
extern VALUE cDate;
|
||||||
|
extern VALUE cDateTime;
|
||||||
|
extern VALUE sym_class_name;
|
||||||
|
extern VALUE sym_members;
|
||||||
|
extern VALUE sym_externalizable;
|
||||||
|
extern VALUE sym_dynamic;
|
||||||
|
VALUE cArrayCollection;
|
||||||
|
ID id_haskey;
|
||||||
|
ID id_encode_amf;
|
||||||
|
ID id_is_array_collection;
|
||||||
|
ID id_use_array_collection;
|
||||||
|
ID id_get_as_class_name;
|
||||||
|
ID id_props_for_serialization;
|
||||||
|
ID id_utc;
|
||||||
|
ID id_to_f;
|
||||||
|
ID id_is_integer;
|
||||||
|
|
||||||
|
static VALUE ser0_serialize(VALUE self, VALUE obj);
|
||||||
|
static VALUE ser3_serialize(VALUE self, VALUE obj);
|
||||||
|
|
||||||
|
void ser_write_byte(AMF_SERIALIZER *ser, char byte) {
|
||||||
|
char bytes[2] = {byte, '\0'};
|
||||||
|
rb_str_buf_cat(ser->stream, bytes, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ser_write_int(AMF_SERIALIZER *ser, int num) {
|
||||||
|
char tmp[4];
|
||||||
|
int tmp_len;
|
||||||
|
|
||||||
|
num &= 0x1fffffff;
|
||||||
|
if (num < 0x80) {
|
||||||
|
tmp_len = 1;
|
||||||
|
tmp[0] = num;
|
||||||
|
} else if (num < 0x4000) {
|
||||||
|
tmp_len = 2;
|
||||||
|
tmp[0] = (num >> 7 & 0x7f) | 0x80;
|
||||||
|
tmp[1] = num & 0x7f;
|
||||||
|
} else if (num < 0x200000) {
|
||||||
|
tmp_len = 3;
|
||||||
|
tmp[0] = (num >> 14 & 0x7f) | 0x80;
|
||||||
|
tmp[1] = (num >> 7 & 0x7f) | 0x80;
|
||||||
|
tmp[2] = num & 0x7f;
|
||||||
|
} else if (num < 0x40000000) {
|
||||||
|
tmp_len = 4;
|
||||||
|
tmp[0] = (num >> 22 & 0x7f) | 0x80;
|
||||||
|
tmp[1] = (num >> 15 & 0x7f) | 0x80;
|
||||||
|
tmp[2] = (num >> 8 & 0x7f) | 0x80;
|
||||||
|
tmp[3] = (num & 0xff);
|
||||||
|
} else {
|
||||||
|
rb_raise(rb_eRangeError, "int %d out of range", num);
|
||||||
|
}
|
||||||
|
|
||||||
|
rb_str_buf_cat(ser->stream, tmp, tmp_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ser_write_uint16(AMF_SERIALIZER *ser, long num) {
|
||||||
|
if(num > 0xffff) rb_raise(rb_eRangeError, "int %ld out of range", num);
|
||||||
|
char tmp[2] = {(num >> 8) & 0xff, num & 0xff};
|
||||||
|
rb_str_buf_cat(ser->stream, tmp, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ser_write_uint32(AMF_SERIALIZER *ser, long num) {
|
||||||
|
if(num > 0xffffffff) rb_raise(rb_eRangeError, "int %ld out of range", num);
|
||||||
|
char tmp[4] = {(num >> 24) & 0xff, (num >> 16) & 0xff, (num >> 8) & 0xff, num & 0xff};
|
||||||
|
rb_str_buf_cat(ser->stream, tmp, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ser_write_double(AMF_SERIALIZER *ser, double num) {
|
||||||
|
union aligned {
|
||||||
|
double dval;
|
||||||
|
char cval[8];
|
||||||
|
} d;
|
||||||
|
const char *number = d.cval;
|
||||||
|
d.dval = num;
|
||||||
|
|
||||||
|
#ifdef WORDS_BIGENDIAN
|
||||||
|
rb_str_buf_cat(ser->stream, number, 8);
|
||||||
|
#else
|
||||||
|
char netnum[8] = {number[7],number[6],number[5],number[4],number[3],number[2],number[1],number[0]};
|
||||||
|
rb_str_buf_cat(ser->stream, netnum, 8);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ser_get_string(VALUE obj, VALUE encode, char** str, long* len) {
|
||||||
|
int type = TYPE(obj);
|
||||||
|
if(type == T_STRING) {
|
||||||
|
#ifdef HAVE_RB_STR_ENCODE
|
||||||
|
if(encode == Qtrue) {
|
||||||
|
rb_encoding *enc = rb_enc_get(obj);
|
||||||
|
if (enc != rb_ascii8bit_encoding()) {
|
||||||
|
rb_encoding *utf8 = rb_utf8_encoding();
|
||||||
|
if (enc != utf8) obj = rb_str_encode(obj, rb_enc_from_encoding(utf8), 0, Qnil);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
*str = RSTRING_PTR(obj);
|
||||||
|
*len = RSTRING_LEN(obj);
|
||||||
|
} else if(type == T_SYMBOL) {
|
||||||
|
*str = (char*)rb_id2name(SYM2ID(obj));
|
||||||
|
*len = strlen(*str);
|
||||||
|
} else if(obj == Qnil) {
|
||||||
|
*len = 0;
|
||||||
|
} else {
|
||||||
|
rb_raise(rb_eArgError, "Invalid type in ser_get_string: %d", type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write the given array in AMF0 notation
|
||||||
|
*/
|
||||||
|
static void ser0_write_array(VALUE self, VALUE ary) {
|
||||||
|
AMF_SERIALIZER *ser;
|
||||||
|
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
||||||
|
|
||||||
|
// Cache it
|
||||||
|
st_add_direct(ser->obj_cache, ary, LONG2FIX(ser->obj_index));
|
||||||
|
ser->obj_index++;
|
||||||
|
|
||||||
|
// Write it out
|
||||||
|
long i, len = RARRAY_LEN(ary);
|
||||||
|
ser_write_byte(ser, AMF0_STRICT_ARRAY_MARKER);
|
||||||
|
ser_write_uint32(ser, len);
|
||||||
|
for(i = 0; i < len; i++) {
|
||||||
|
ser0_serialize(self, RARRAY_PTR(ary)[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Supports writing strings and symbols. For hash keys, strings all have 16 bit
|
||||||
|
* lengths, so writing a type marker is unnecessary. In that case the third
|
||||||
|
* parameter should be set to Qfalse instead of Qtrue.
|
||||||
|
*/
|
||||||
|
static void ser0_write_string(AMF_SERIALIZER *ser, VALUE obj, VALUE write_marker) {
|
||||||
|
// Extract char array and length from object
|
||||||
|
char* str;
|
||||||
|
long len;
|
||||||
|
ser_get_string(obj, Qtrue, &str, &len);
|
||||||
|
|
||||||
|
// Write string
|
||||||
|
if(len > 0xffff) {
|
||||||
|
if(write_marker == Qtrue) ser_write_byte(ser, AMF0_LONG_STRING_MARKER);
|
||||||
|
ser_write_uint32(ser, len);
|
||||||
|
} else {
|
||||||
|
if(write_marker == Qtrue) ser_write_byte(ser, AMF0_STRING_MARKER);
|
||||||
|
ser_write_uint16(ser, len);
|
||||||
|
}
|
||||||
|
rb_str_buf_cat(ser->stream, str, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hash iterator for object properties that writes the key and then serializes
|
||||||
|
* the value
|
||||||
|
*/
|
||||||
|
static int ser0_hash_iter(VALUE key, VALUE val, const VALUE args[1]) {
|
||||||
|
AMF_SERIALIZER *ser;
|
||||||
|
Data_Get_Struct(args[0], AMF_SERIALIZER, ser);
|
||||||
|
|
||||||
|
// Write key and value
|
||||||
|
ser0_write_string(ser, key, Qfalse); // Technically incorrect if key length is longer than a 16 bit string, but if you run into that you're screwed anyways
|
||||||
|
ser0_serialize(args[0], val);
|
||||||
|
|
||||||
|
return ST_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Used for both hashes and objects. Takes the object and the props hash or Qnil,
|
||||||
|
* which forces a call to the class mapper for props for serialization. Prop
|
||||||
|
* sorting must be enabled by an explicit call to extconf.rb, so the tests will
|
||||||
|
* not pass typically on Ruby 1.8.
|
||||||
|
*/
|
||||||
|
static void ser0_write_object(VALUE self, VALUE obj, VALUE props) {
|
||||||
|
AMF_SERIALIZER *ser;
|
||||||
|
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
||||||
|
|
||||||
|
// Cache it
|
||||||
|
st_add_direct(ser->obj_cache, obj, LONG2FIX(ser->obj_index));
|
||||||
|
ser->obj_index++;
|
||||||
|
|
||||||
|
// Make a request for props hash unless we already have it
|
||||||
|
if(props == Qnil) {
|
||||||
|
props = rb_funcall(ser->class_mapper, id_props_for_serialization, 1, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write header
|
||||||
|
VALUE class_name = rb_funcall(ser->class_mapper, id_get_as_class_name, 1, obj);
|
||||||
|
if(class_name != Qnil) {
|
||||||
|
ser_write_byte(ser, AMF0_TYPED_OBJECT_MARKER);
|
||||||
|
ser0_write_string(ser, class_name, Qfalse);
|
||||||
|
} else {
|
||||||
|
ser_write_byte(ser, AMF0_OBJECT_MARKER);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write out data
|
||||||
|
VALUE args[1] = {self};
|
||||||
|
#ifdef SORT_PROPS
|
||||||
|
// Sort is required prior to Ruby 1.9 to pass all the tests, as Ruby 1.8 hashes don't store insert order
|
||||||
|
VALUE sorted_props = rb_funcall(props, rb_intern("sort"), 0);
|
||||||
|
long i, len = RARRAY_LEN(sorted_props);
|
||||||
|
for(i = 0; i < len; i++) {
|
||||||
|
VALUE pair = RARRAY_PTR(sorted_props)[i];
|
||||||
|
ser0_hash_iter(RARRAY_PTR(pair)[0], RARRAY_PTR(pair)[1], args);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
rb_hash_foreach(props, ser0_hash_iter, (st_data_t)args);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ser_write_uint16(ser, 0);
|
||||||
|
ser_write_byte(ser, AMF0_OBJECT_END_MARKER);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ser0_write_time(VALUE self, VALUE time) {
|
||||||
|
AMF_SERIALIZER *ser;
|
||||||
|
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
||||||
|
|
||||||
|
ser_write_byte(ser, AMF0_DATE_MARKER);
|
||||||
|
|
||||||
|
// Write time
|
||||||
|
time = rb_obj_dup(time);
|
||||||
|
rb_funcall(time, id_utc, 0);
|
||||||
|
double tmp_num = NUM2DBL(rb_funcall(time, id_to_f, 0)) * 1000;
|
||||||
|
ser_write_double(ser, tmp_num);
|
||||||
|
ser_write_uint16(ser, 0); // Time zone
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ser0_write_date(VALUE self, VALUE date) {
|
||||||
|
AMF_SERIALIZER *ser;
|
||||||
|
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
||||||
|
|
||||||
|
ser_write_byte(ser, AMF0_DATE_MARKER);
|
||||||
|
|
||||||
|
// Write time
|
||||||
|
double tmp_num = rb_str_to_dbl(rb_funcall(date, rb_intern("strftime"), 1, rb_str_new2("%Q")), Qfalse);
|
||||||
|
ser_write_double(ser, tmp_num);
|
||||||
|
ser_write_uint16(ser, 0); // Time zone
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Serializes the object to a string and returns that string
|
||||||
|
*/
|
||||||
|
static VALUE ser0_serialize(VALUE self, VALUE obj) {
|
||||||
|
AMF_SERIALIZER *ser;
|
||||||
|
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
||||||
|
|
||||||
|
int type = TYPE(obj);
|
||||||
|
VALUE klass = Qnil;
|
||||||
|
if(type == T_OBJECT || type == T_DATA) {
|
||||||
|
klass = CLASS_OF(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUE obj_index;
|
||||||
|
if(st_lookup(ser->obj_cache, obj, &obj_index)) {
|
||||||
|
ser_write_byte(ser, AMF0_REFERENCE_MARKER);
|
||||||
|
ser_write_uint16(ser, FIX2LONG(obj_index));
|
||||||
|
} else if(rb_respond_to(obj, id_encode_amf)) {
|
||||||
|
rb_funcall(obj, id_encode_amf, 1, self);
|
||||||
|
} else if(type == T_STRING || type == T_SYMBOL) {
|
||||||
|
ser0_write_string(ser, obj, Qtrue);
|
||||||
|
} else if(rb_obj_is_kind_of(obj, rb_cNumeric)) {
|
||||||
|
ser_write_byte(ser, AMF0_NUMBER_MARKER);
|
||||||
|
ser_write_double(ser, RFLOAT_VALUE(rb_Float(obj)));
|
||||||
|
} else if(type == T_NIL) {
|
||||||
|
ser_write_byte(ser, AMF0_NULL_MARKER);
|
||||||
|
} else if(type == T_TRUE || type == T_FALSE) {
|
||||||
|
ser_write_byte(ser, AMF0_BOOLEAN_MARKER);
|
||||||
|
ser_write_byte(ser, type == T_TRUE ? 1 : 0);
|
||||||
|
} else if(type == T_ARRAY) {
|
||||||
|
ser0_write_array(self, obj);
|
||||||
|
} else if(klass == rb_cTime) {
|
||||||
|
ser0_write_time(self, obj);
|
||||||
|
} else if(klass == cDate || klass == cDateTime) {
|
||||||
|
ser0_write_date(self, obj);
|
||||||
|
} else if(type == T_HASH || type == T_OBJECT) {
|
||||||
|
ser0_write_object(self, obj, Qnil);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ser->stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Writes an AMF3 style string. Accepts strings, symbols, and nil, and handles
|
||||||
|
* all the necessary encoding and caching.
|
||||||
|
*/
|
||||||
|
static void ser3_write_utf8vr(AMF_SERIALIZER *ser, VALUE obj) {
|
||||||
|
// Extract char array and length from object
|
||||||
|
char* str;
|
||||||
|
long len;
|
||||||
|
ser_get_string(obj, Qtrue, &str, &len);
|
||||||
|
|
||||||
|
// Write string
|
||||||
|
VALUE str_index;
|
||||||
|
if(len == 0) {
|
||||||
|
ser_write_byte(ser, AMF3_EMPTY_STRING);
|
||||||
|
} else if(st_lookup(ser->str_cache, (st_data_t)str, &str_index)) {
|
||||||
|
ser_write_int(ser, FIX2INT(str_index) << 1);
|
||||||
|
} else {
|
||||||
|
st_add_direct(ser->str_cache, (st_data_t)strdup(str), LONG2FIX(ser->str_index));
|
||||||
|
ser->str_index++;
|
||||||
|
|
||||||
|
ser_write_int(ser, ((int)len) << 1 | 1);
|
||||||
|
rb_str_buf_cat(ser->stream, str, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Writes Numeric conforming object using AMF3 notation
|
||||||
|
*/
|
||||||
|
static void ser3_write_numeric(AMF_SERIALIZER *ser, VALUE num) {
|
||||||
|
// Is it an integer in range?
|
||||||
|
if(rb_funcall(num, id_is_integer, 0) == Qtrue) {
|
||||||
|
// It's an integer internally, so now we need to check if it's in range
|
||||||
|
VALUE int_obj = rb_Integer(num);
|
||||||
|
if(TYPE(int_obj) == T_FIXNUM) {
|
||||||
|
long long_val = FIX2LONG(int_obj);
|
||||||
|
if(long_val < MIN_INTEGER || long_val > MAX_INTEGER) {
|
||||||
|
// Outside range, but we have a value already, so just cast to double
|
||||||
|
ser_write_byte(ser, AMF3_DOUBLE_MARKER);
|
||||||
|
ser_write_double(ser, (double)long_val);
|
||||||
|
} else {
|
||||||
|
// Inside valid integer range
|
||||||
|
ser_write_byte(ser, AMF3_INTEGER_MARKER);
|
||||||
|
ser_write_int(ser, (int)long_val);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's either not an integer or out of range, so write as a double
|
||||||
|
ser_write_byte(ser, AMF3_DOUBLE_MARKER);
|
||||||
|
ser_write_double(ser, RFLOAT_VALUE(rb_Float(num)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Writes the given array using AMF3 notation
|
||||||
|
*/
|
||||||
|
static void ser3_write_array(VALUE self, VALUE ary) {
|
||||||
|
AMF_SERIALIZER *ser;
|
||||||
|
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
||||||
|
|
||||||
|
// Is it an array collection?
|
||||||
|
VALUE is_ac = Qfalse;
|
||||||
|
if(rb_respond_to(ary, id_is_array_collection)) {
|
||||||
|
is_ac = rb_funcall(ary, id_is_array_collection, 0);
|
||||||
|
} else {
|
||||||
|
is_ac = rb_funcall(ser->class_mapper, id_use_array_collection, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write type marker
|
||||||
|
ser_write_byte(ser, is_ac ? AMF3_OBJECT_MARKER : AMF3_ARRAY_MARKER);
|
||||||
|
|
||||||
|
// Write object ref, or cache it
|
||||||
|
VALUE obj_index;
|
||||||
|
if(st_lookup(ser->obj_cache, ary, &obj_index)) {
|
||||||
|
ser_write_int(ser, FIX2INT(obj_index) << 1);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
st_add_direct(ser->obj_cache, ary, LONG2FIX(ser->obj_index));
|
||||||
|
ser->obj_index++;
|
||||||
|
if(is_ac) ser->obj_index++; // The array collection source array
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write out traits and array marker if it's an array collection
|
||||||
|
if(is_ac) {
|
||||||
|
VALUE trait_index;
|
||||||
|
char array_collection_name[34] = "flex.messaging.io.ArrayCollection";
|
||||||
|
if(st_lookup(ser->trait_cache, (st_data_t)array_collection_name, &trait_index)) {
|
||||||
|
ser_write_int(ser, FIX2INT(trait_index) << 2 | 0x01);
|
||||||
|
} else {
|
||||||
|
st_add_direct(ser->trait_cache, (st_data_t)strdup(array_collection_name), LONG2FIX(ser->trait_index));
|
||||||
|
ser->trait_index++;
|
||||||
|
ser_write_byte(ser, 0x07); // Trait header
|
||||||
|
ser3_write_utf8vr(ser, rb_str_new2(array_collection_name));
|
||||||
|
}
|
||||||
|
ser_write_byte(ser, AMF3_ARRAY_MARKER);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write header
|
||||||
|
int header = ((int)RARRAY_LEN(ary)) << 1 | 1;
|
||||||
|
ser_write_int(ser, header);
|
||||||
|
ser_write_byte(ser, AMF3_CLOSE_DYNAMIC_ARRAY);
|
||||||
|
|
||||||
|
// Write contents
|
||||||
|
long i, len = RARRAY_LEN(ary);
|
||||||
|
for(i = 0; i < len; i++) {
|
||||||
|
ser3_serialize(self, RARRAY_PTR(ary)[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AMF3 property hash write iterator. Checks the args->extra hash, if given,
|
||||||
|
* and skips properties that are keys in that hash.
|
||||||
|
*/
|
||||||
|
static int ser3_hash_iter(VALUE key, VALUE val, const VALUE args[2]) {
|
||||||
|
AMF_SERIALIZER *ser;
|
||||||
|
Data_Get_Struct(args[0], AMF_SERIALIZER, ser);
|
||||||
|
|
||||||
|
if(args[1] == Qnil || rb_funcall(args[1], id_haskey, 1, key) == Qfalse) {
|
||||||
|
// Write key and value
|
||||||
|
ser3_write_utf8vr(ser, key);
|
||||||
|
ser3_serialize(args[0], val);
|
||||||
|
}
|
||||||
|
return ST_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Used for both hashes and objects. Takes the object and the props hash or Qnil,
|
||||||
|
* which forces a call to the class mapper for props for serialization. Prop
|
||||||
|
* sorting must be enabled by an explicit call to extconf.rb, so the tests will
|
||||||
|
* not pass typically on Ruby 1.8. If you need to have specific traits, you can
|
||||||
|
* also pass that in, or pass Qnil to use the default traits - dynamic with no
|
||||||
|
* defined members.
|
||||||
|
*/
|
||||||
|
static void ser3_write_object(VALUE self, VALUE obj, VALUE props, VALUE traits) {
|
||||||
|
AMF_SERIALIZER *ser;
|
||||||
|
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
||||||
|
long i;
|
||||||
|
|
||||||
|
// Write type marker
|
||||||
|
ser_write_byte(ser, AMF3_OBJECT_MARKER);
|
||||||
|
|
||||||
|
// Write object ref, or cache it
|
||||||
|
VALUE obj_index;
|
||||||
|
if(st_lookup(ser->obj_cache, obj, &obj_index)) {
|
||||||
|
ser_write_int(ser, FIX2INT(obj_index) << 1);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
st_add_direct(ser->obj_cache, obj, LONG2FIX(ser->obj_index));
|
||||||
|
ser->obj_index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract traits data, or use defaults
|
||||||
|
VALUE is_default = Qfalse;
|
||||||
|
VALUE class_name = Qnil;
|
||||||
|
VALUE members = Qnil;
|
||||||
|
long members_len = 0;
|
||||||
|
VALUE dynamic = Qtrue;
|
||||||
|
VALUE externalizable = Qfalse;
|
||||||
|
if(traits == Qnil) {
|
||||||
|
class_name = rb_funcall(ser->class_mapper, id_get_as_class_name, 1, obj);
|
||||||
|
if(class_name == Qnil) is_default = Qtrue;
|
||||||
|
} else {
|
||||||
|
class_name = rb_hash_aref(traits, sym_class_name);
|
||||||
|
members = rb_hash_aref(traits, sym_members);
|
||||||
|
if(members != Qnil) members_len = RARRAY_LEN(members);
|
||||||
|
dynamic = rb_hash_aref(traits, sym_dynamic);
|
||||||
|
externalizable = rb_hash_aref(traits, sym_externalizable);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle trait caching
|
||||||
|
int did_ref = 0;
|
||||||
|
VALUE trait_index;
|
||||||
|
if(is_default == Qtrue || class_name != Qnil) {
|
||||||
|
const char *ref_class_name = is_default == Qtrue ? "__default__" : RSTRING_PTR(class_name);
|
||||||
|
if(st_lookup(ser->trait_cache, (st_data_t)ref_class_name, &trait_index)) {
|
||||||
|
ser_write_int(ser, FIX2INT(trait_index) << 2 | 0x01);
|
||||||
|
did_ref = 1;
|
||||||
|
} else {
|
||||||
|
st_add_direct(ser->trait_cache, (st_data_t)strdup(ref_class_name), LONG2FIX(ser->trait_index));
|
||||||
|
ser->trait_index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write traits outs if didn't write reference
|
||||||
|
if(!did_ref) {
|
||||||
|
// Write out trait header
|
||||||
|
int header = 0x03;
|
||||||
|
if(dynamic == Qtrue) header |= 0x02 << 2;
|
||||||
|
if(externalizable == Qtrue) header |= 0x01 << 2;
|
||||||
|
header |= ((int)members_len) << 4;
|
||||||
|
ser_write_int(ser, header);
|
||||||
|
|
||||||
|
// Write class name
|
||||||
|
ser3_write_utf8vr(ser, class_name);
|
||||||
|
|
||||||
|
// Write out members
|
||||||
|
for(i = 0; i < members_len; i++) {
|
||||||
|
ser3_write_utf8vr(ser, RARRAY_PTR(members)[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raise exception if marked externalizable
|
||||||
|
if(externalizable == Qtrue) {
|
||||||
|
rb_funcall(obj, rb_intern("write_external"), 1, self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a request for props hash unless we already have it
|
||||||
|
if(props == Qnil) {
|
||||||
|
props = rb_funcall(ser->class_mapper, id_props_for_serialization, 1, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write sealed members
|
||||||
|
VALUE skipped_members = members_len ? rb_hash_new() : Qnil;
|
||||||
|
for(i = 0; i < members_len; i++) {
|
||||||
|
ser3_serialize(self, rb_hash_aref(props, RARRAY_PTR(members)[i]));
|
||||||
|
rb_hash_aset(skipped_members, RARRAY_PTR(members)[i], Qtrue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write dynamic properties
|
||||||
|
if(dynamic == Qtrue) {
|
||||||
|
VALUE args[2] = {self, skipped_members};
|
||||||
|
#ifdef SORT_PROPS
|
||||||
|
// Sort is required prior to Ruby 1.9 to pass all the tests, as Ruby 1.8 hashes don't store insert order
|
||||||
|
VALUE sorted_props = rb_funcall(props, rb_intern("sort"), 0);
|
||||||
|
for(i = 0; i < RARRAY_LEN(sorted_props); i++) {
|
||||||
|
VALUE pair = RARRAY_PTR(sorted_props)[i];
|
||||||
|
ser3_hash_iter(RARRAY_PTR(pair)[0], RARRAY_PTR(pair)[1], args);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
rb_hash_foreach(props, ser3_hash_iter, (st_data_t)args);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ser_write_byte(ser, AMF3_CLOSE_DYNAMIC_OBJECT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ser3_write_time(VALUE self, VALUE time_obj) {
|
||||||
|
AMF_SERIALIZER *ser;
|
||||||
|
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
||||||
|
|
||||||
|
ser_write_byte(ser, AMF3_DATE_MARKER);
|
||||||
|
|
||||||
|
// Write object ref, or cache it
|
||||||
|
VALUE obj_index;
|
||||||
|
if(st_lookup(ser->obj_cache, time_obj, &obj_index)) {
|
||||||
|
ser_write_int(ser, FIX2INT(obj_index) << 1);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
st_add_direct(ser->obj_cache, time_obj, LONG2FIX(ser->obj_index));
|
||||||
|
ser->obj_index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write time
|
||||||
|
ser_write_byte(ser, AMF3_NULL_MARKER); // Ref header
|
||||||
|
time_obj = rb_obj_dup(time_obj);
|
||||||
|
rb_funcall(time_obj, id_utc, 0);
|
||||||
|
double tmp_num = NUM2DBL(rb_funcall(time_obj, id_to_f, 0)) * 1000;
|
||||||
|
ser_write_double(ser, tmp_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ser3_write_date(VALUE self, VALUE date) {
|
||||||
|
AMF_SERIALIZER *ser;
|
||||||
|
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
||||||
|
|
||||||
|
ser_write_byte(ser, AMF3_DATE_MARKER);
|
||||||
|
|
||||||
|
// Write object ref, or cache it
|
||||||
|
VALUE obj_index;
|
||||||
|
if(st_lookup(ser->obj_cache, date, &obj_index)) {
|
||||||
|
ser_write_int(ser, FIX2INT(obj_index) << 1);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
st_add_direct(ser->obj_cache, date, LONG2FIX(ser->obj_index));
|
||||||
|
ser->obj_index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write time
|
||||||
|
ser_write_byte(ser, AMF3_NULL_MARKER); // Ref header
|
||||||
|
double tmp_num = rb_str_to_dbl(rb_funcall(date, rb_intern("strftime"), 1, rb_str_new2("%Q")), Qfalse);
|
||||||
|
ser_write_double(ser, tmp_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ser3_write_byte_array(VALUE self, VALUE ba) {
|
||||||
|
AMF_SERIALIZER *ser;
|
||||||
|
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
||||||
|
|
||||||
|
ser_write_byte(ser, AMF3_BYTE_ARRAY_MARKER);
|
||||||
|
|
||||||
|
// Write object ref, or cache it
|
||||||
|
VALUE obj_index;
|
||||||
|
if(st_lookup(ser->obj_cache, ba, &obj_index)) {
|
||||||
|
ser_write_int(ser, FIX2INT(obj_index) << 1);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
st_add_direct(ser->obj_cache, ba, LONG2FIX(ser->obj_index));
|
||||||
|
ser->obj_index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write byte array
|
||||||
|
VALUE str = rb_funcall(ba, rb_intern("string"), 0);
|
||||||
|
int len = (int)(RSTRING_LEN(str) << 1); // Explicitly cast to int to avoid compiler warning
|
||||||
|
ser_write_int(ser, len | 1);
|
||||||
|
rb_str_buf_cat(ser->stream, RSTRING_PTR(str), RSTRING_LEN(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Serializes the object to a string and returns that string
|
||||||
|
*/
|
||||||
|
static VALUE ser3_serialize(VALUE self, VALUE obj) {
|
||||||
|
AMF_SERIALIZER *ser;
|
||||||
|
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
||||||
|
|
||||||
|
int type = TYPE(obj);
|
||||||
|
VALUE klass = Qnil;
|
||||||
|
if(type == T_OBJECT || type == T_DATA || type == T_ARRAY) {
|
||||||
|
klass = CLASS_OF(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rb_respond_to(obj, id_encode_amf)) {
|
||||||
|
rb_funcall(obj, id_encode_amf, 1, self);
|
||||||
|
} else if(type == T_STRING || type == T_SYMBOL) {
|
||||||
|
ser_write_byte(ser, AMF3_STRING_MARKER);
|
||||||
|
ser3_write_utf8vr(ser, obj);
|
||||||
|
} else if(rb_obj_is_kind_of(obj, rb_cNumeric)) {
|
||||||
|
ser3_write_numeric(ser, obj);
|
||||||
|
} else if(type == T_NIL) {
|
||||||
|
ser_write_byte(ser, AMF3_NULL_MARKER);
|
||||||
|
} else if(type == T_TRUE) {
|
||||||
|
ser_write_byte(ser, AMF3_TRUE_MARKER);
|
||||||
|
} else if(type == T_FALSE) {
|
||||||
|
ser_write_byte(ser, AMF3_FALSE_MARKER);
|
||||||
|
} else if(type == T_ARRAY) {
|
||||||
|
ser3_write_array(self, obj);
|
||||||
|
} else if(type == T_HASH) {
|
||||||
|
ser3_write_object(self, obj, Qnil, Qnil);
|
||||||
|
} else if(klass == rb_cTime) {
|
||||||
|
ser3_write_time(self, obj);
|
||||||
|
} else if(klass == cDate || klass == cDateTime) {
|
||||||
|
ser3_write_date(self, obj);
|
||||||
|
} else if(klass == cStringIO) {
|
||||||
|
ser3_write_byte_array(self, obj);
|
||||||
|
} else if(type == T_OBJECT) {
|
||||||
|
ser3_write_object(self, obj, Qnil, Qnil);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ser->stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mark ruby objects for GC
|
||||||
|
*/
|
||||||
|
static void ser_mark(AMF_SERIALIZER *ser) {
|
||||||
|
if(!ser) return;
|
||||||
|
rb_gc_mark(ser->class_mapper);
|
||||||
|
rb_gc_mark(ser->stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free cache tables, stream and the struct itself
|
||||||
|
*/
|
||||||
|
int ser_free_strtable_key(st_data_t key, st_data_t value, st_data_t ignored)
|
||||||
|
{
|
||||||
|
xfree((void *)key);
|
||||||
|
|
||||||
|
return ST_DELETE;
|
||||||
|
}
|
||||||
|
static inline void ser_free_cache(AMF_SERIALIZER *ser) {
|
||||||
|
if(ser->str_cache) {
|
||||||
|
st_foreach(ser->str_cache, ser_free_strtable_key, 0);
|
||||||
|
st_free_table(ser->str_cache);
|
||||||
|
ser->str_cache = NULL;
|
||||||
|
}
|
||||||
|
if(ser->trait_cache) {
|
||||||
|
st_foreach(ser->trait_cache, ser_free_strtable_key, 0);
|
||||||
|
st_free_table(ser->trait_cache);
|
||||||
|
ser->trait_cache = NULL;
|
||||||
|
}
|
||||||
|
if(ser->obj_cache) {
|
||||||
|
st_free_table(ser->obj_cache);
|
||||||
|
ser->obj_cache = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void ser_free(AMF_SERIALIZER *ser) {
|
||||||
|
ser_free_cache(ser);
|
||||||
|
xfree(ser);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create new struct and wrap with class
|
||||||
|
*/
|
||||||
|
static VALUE ser_alloc(VALUE klass) {
|
||||||
|
// Allocate struct
|
||||||
|
AMF_SERIALIZER *ser = ALLOC(AMF_SERIALIZER);
|
||||||
|
memset(ser, 0, sizeof(AMF_SERIALIZER));
|
||||||
|
return Data_Wrap_Struct(klass, ser_mark, ser_free, ser);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initializer
|
||||||
|
*/
|
||||||
|
static VALUE ser_initialize(VALUE self, VALUE class_mapper) {
|
||||||
|
AMF_SERIALIZER *ser;
|
||||||
|
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
||||||
|
|
||||||
|
ser->class_mapper = class_mapper;
|
||||||
|
ser->depth = 0;
|
||||||
|
ser->stream = rb_str_buf_new(0);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* ser.version => int
|
||||||
|
*
|
||||||
|
* Returns the serializer version number, so that a custom encode_amf method
|
||||||
|
* knows which version to encode for
|
||||||
|
*/
|
||||||
|
static VALUE ser_version(VALUE self) {
|
||||||
|
AMF_SERIALIZER *ser;
|
||||||
|
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
||||||
|
return INT2FIX(ser->version);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* ser.stream => string
|
||||||
|
*
|
||||||
|
* Returns the string that the serializer is writing to
|
||||||
|
*/
|
||||||
|
static VALUE ser_stream(VALUE self) {
|
||||||
|
AMF_SERIALIZER *ser;
|
||||||
|
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
||||||
|
return ser->stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* ser.serialize(amf_ver, obj) => string
|
||||||
|
*
|
||||||
|
* Serialize the given object to the current stream and returns the stream
|
||||||
|
*/
|
||||||
|
VALUE ser_serialize(VALUE self, VALUE ver, VALUE obj) {
|
||||||
|
AMF_SERIALIZER *ser;
|
||||||
|
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
||||||
|
|
||||||
|
// Process version
|
||||||
|
int int_ver = FIX2INT(ver);
|
||||||
|
if(int_ver != 0 && int_ver != 3) rb_raise(rb_eArgError, "unsupported version %d", int_ver);
|
||||||
|
ser->version = int_ver;
|
||||||
|
|
||||||
|
// Initialize caches
|
||||||
|
if(ser->depth == 0) {
|
||||||
|
ser->obj_cache = st_init_numtable();
|
||||||
|
ser->obj_index = 0;
|
||||||
|
if(ser->version == 3) {
|
||||||
|
ser->str_cache = st_init_strtable();
|
||||||
|
ser->str_index = 0;
|
||||||
|
ser->trait_cache = st_init_strtable();
|
||||||
|
ser->trait_index = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ser->depth++;
|
||||||
|
|
||||||
|
// Perform serialization
|
||||||
|
if(ser->version == 0) {
|
||||||
|
ser0_serialize(self, obj);
|
||||||
|
} else {
|
||||||
|
ser3_serialize(self, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
ser->depth--;
|
||||||
|
if(ser->depth == 0) ser_free_cache(ser);
|
||||||
|
|
||||||
|
return ser->stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* ser.write_array(ary) => ser
|
||||||
|
*
|
||||||
|
* Serializes the given array to the serializer stream
|
||||||
|
*/
|
||||||
|
static VALUE ser_write_array(VALUE self, VALUE ary) {
|
||||||
|
AMF_SERIALIZER *ser;
|
||||||
|
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
||||||
|
if(ser->version == 0) {
|
||||||
|
ser0_write_array(self, ary);
|
||||||
|
} else {
|
||||||
|
ser3_write_array(self, ary);
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* ser.write_object(obj, props=nil) => ser
|
||||||
|
* ser.write_object(obj, props=nil, traits=nil) => ser
|
||||||
|
*
|
||||||
|
* Serializes the given object or hash to the serializer stream using
|
||||||
|
* the proper serializer version. If given a props hash, uses that
|
||||||
|
* instead of using the class mapper to calculate it. If given a traits
|
||||||
|
* hash for AMF3, uses that instead of the default dynamic traits with
|
||||||
|
* the mapped class name.
|
||||||
|
*/
|
||||||
|
static VALUE ser_write_object(int argc, VALUE *argv, VALUE self) {
|
||||||
|
AMF_SERIALIZER *ser;
|
||||||
|
Data_Get_Struct(self, AMF_SERIALIZER, ser);
|
||||||
|
|
||||||
|
// Check args and call implementation
|
||||||
|
VALUE obj;
|
||||||
|
VALUE props = Qnil;
|
||||||
|
VALUE traits = Qnil;
|
||||||
|
if(ser->version == 0) {
|
||||||
|
rb_scan_args(argc, argv, "11", &obj, &props);
|
||||||
|
ser0_write_object(self, obj, props);
|
||||||
|
} else {
|
||||||
|
rb_scan_args(argc, argv, "12", &obj, &props, &traits);
|
||||||
|
ser3_write_object(self, obj, props, traits);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Init_rocket_amf_serializer() {
|
||||||
|
// Define Serializer
|
||||||
|
cSerializer = rb_define_class_under(mRocketAMFExt, "Serializer", rb_cObject);
|
||||||
|
rb_define_alloc_func(cSerializer, ser_alloc);
|
||||||
|
rb_define_method(cSerializer, "initialize", ser_initialize, 1);
|
||||||
|
rb_define_method(cSerializer, "version", ser_version, 0);
|
||||||
|
rb_define_method(cSerializer, "stream", ser_stream, 0);
|
||||||
|
rb_define_method(cSerializer, "serialize", ser_serialize, 2);
|
||||||
|
rb_define_method(cSerializer, "write_array", ser_write_array, 1);
|
||||||
|
rb_define_method(cSerializer, "write_object", ser_write_object, -1);
|
||||||
|
|
||||||
|
// Get refs to commonly used symbols and ids
|
||||||
|
id_haskey = rb_intern("has_key?");
|
||||||
|
id_encode_amf = rb_intern("encode_amf");
|
||||||
|
id_is_array_collection = rb_intern("is_array_collection?");
|
||||||
|
id_use_array_collection = rb_intern("use_array_collection");
|
||||||
|
id_get_as_class_name = rb_intern("get_as_class_name");
|
||||||
|
id_props_for_serialization = rb_intern("props_for_serialization");
|
||||||
|
id_utc = rb_intern("utc");
|
||||||
|
id_to_f = rb_intern("to_f");
|
||||||
|
id_is_integer = rb_intern("integer?");
|
||||||
|
}
|
||||||
29
vendor/gems/RocketAMF-1.0.0/ext/rocketamf_ext/serializer.h
vendored
Normal file
29
vendor/gems/RocketAMF-1.0.0/ext/rocketamf_ext/serializer.h
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
#include <ruby.h>
|
||||||
|
#ifdef HAVE_RB_STR_ENCODE
|
||||||
|
#include <ruby/st.h>
|
||||||
|
#include <ruby/encoding.h>
|
||||||
|
#else
|
||||||
|
#include <st.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int version;
|
||||||
|
VALUE class_mapper;
|
||||||
|
VALUE stream;
|
||||||
|
long depth;
|
||||||
|
st_table* str_cache;
|
||||||
|
long str_index;
|
||||||
|
st_table* trait_cache;
|
||||||
|
long trait_index;
|
||||||
|
st_table* obj_cache;
|
||||||
|
long obj_index;
|
||||||
|
} AMF_SERIALIZER;
|
||||||
|
|
||||||
|
void ser_write_byte(AMF_SERIALIZER *ser, char byte);
|
||||||
|
void ser_write_int(AMF_SERIALIZER *ser, int num);
|
||||||
|
void ser_write_uint16(AMF_SERIALIZER *ser, long num);
|
||||||
|
void ser_write_uint32(AMF_SERIALIZER *ser, long num);
|
||||||
|
void ser_write_double(AMF_SERIALIZER *ser, double num);
|
||||||
|
void ser_get_string(VALUE obj, VALUE encode, char** str, long* len);
|
||||||
|
|
||||||
|
VALUE ser_serialize(VALUE self, VALUE ver, VALUE obj);
|
||||||
4
vendor/gems/RocketAMF-1.0.0/ext/rocketamf_ext/utility.h
vendored
Normal file
4
vendor/gems/RocketAMF-1.0.0/ext/rocketamf_ext/utility.h
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
// Before RFLOAT_VALUE, value was in a different place in the struct
|
||||||
|
#ifndef RFLOAT_VALUE
|
||||||
|
#define RFLOAT_VALUE(v) (RFLOAT(v)->value)
|
||||||
|
#endif
|
||||||
216
vendor/gems/RocketAMF-1.0.0/lib/rocketamf.rb
vendored
Normal file
216
vendor/gems/RocketAMF-1.0.0/lib/rocketamf.rb
vendored
Normal file
|
|
@ -0,0 +1,216 @@
|
||||||
|
$:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
||||||
|
$:.unshift "#{File.expand_path(File.dirname(__FILE__))}/rocketamf/"
|
||||||
|
|
||||||
|
require "date"
|
||||||
|
require "stringio"
|
||||||
|
require 'rocketamf/extensions'
|
||||||
|
require 'rocketamf/class_mapping'
|
||||||
|
require 'rocketamf/constants'
|
||||||
|
require 'rocketamf/remoting'
|
||||||
|
|
||||||
|
# RocketAMF is a full featured AMF0/3 serializer and deserializer with support for
|
||||||
|
# bi-directional Flash to Ruby class mapping, custom serialization and mapping,
|
||||||
|
# remoting gateway helpers that follow AMF0/3 messaging specs, and a suite of specs
|
||||||
|
# to ensure adherence to the specification documents put out by Adobe. If the C
|
||||||
|
# components compile, then RocketAMF automatically takes advantage of them to
|
||||||
|
# provide a substantial performance benefit. In addition, RocketAMF is fully
|
||||||
|
# compatible with Ruby 1.9.
|
||||||
|
#
|
||||||
|
# == Performance
|
||||||
|
#
|
||||||
|
# RocketAMF provides native C extensions for serialization, deserialization,
|
||||||
|
# remoting, and class mapping. If your environment supports them, RocketAMF will
|
||||||
|
# automatically take advantage of the C serializer, deserializer, and remoting
|
||||||
|
# support. The C class mapper has some substantial performance optimizations that
|
||||||
|
# make it incompatible with the pure Ruby class mapper, and so it must be manually
|
||||||
|
# enabled. For more information see <tt>RocketAMF::ClassMapping</tt>. Below are
|
||||||
|
# some benchmarks I took using using a simple little benchmarking utility I whipped
|
||||||
|
# up, which can be found in the root of the repository.
|
||||||
|
#
|
||||||
|
# # 100000 objects
|
||||||
|
# # Ruby 1.8
|
||||||
|
# Testing native AMF0:
|
||||||
|
# minimum serialize time: 1.229868s
|
||||||
|
# minimum deserialize time: 0.86465s
|
||||||
|
# Testing native AMF3:
|
||||||
|
# minimum serialize time: 1.444652s
|
||||||
|
# minimum deserialize time: 0.879407s
|
||||||
|
# Testing pure AMF0:
|
||||||
|
# minimum serialize time: 25.427931s
|
||||||
|
# minimum deserialize time: 11.706084s
|
||||||
|
# Testing pure AMF3:
|
||||||
|
# minimum serialize time: 31.637864s
|
||||||
|
# minimum deserialize time: 14.773969s
|
||||||
|
#
|
||||||
|
# == Serialization & Deserialization
|
||||||
|
#
|
||||||
|
# RocketAMF provides two main methods - <tt>serialize</tt> and <tt>deserialize</tt>.
|
||||||
|
# Deserialization takes a String or StringIO object and the AMF version if different
|
||||||
|
# from the default. Serialization takes any Ruby object and the version if different
|
||||||
|
# from the default. Both default to AMF0, as it's more widely supported and slightly
|
||||||
|
# faster, but AMF3 does a better job of not sending duplicate data. Which you choose
|
||||||
|
# depends on what you need to communicate with and how much serialized size matters.
|
||||||
|
#
|
||||||
|
# == Mapping Classes Between Flash and Ruby
|
||||||
|
#
|
||||||
|
# RocketAMF provides a simple class mapping tool to facilitate serialization and
|
||||||
|
# deserialization of typed objects. Refer to the documentation of
|
||||||
|
# <tt>RocketAMF::ClassMapping</tt> for more details. If the provided class
|
||||||
|
# mapping tool is not sufficient for your needs, you also have the option to
|
||||||
|
# replace it with a class mapper of your own devising that matches the documented
|
||||||
|
# API.
|
||||||
|
#
|
||||||
|
# == Remoting
|
||||||
|
#
|
||||||
|
# You can use RocketAMF bare to write an AMF gateway using the following code.
|
||||||
|
# In addition, you can use rack-amf (http://github.com/rubyamf/rack-amf) or
|
||||||
|
# RubyAMF (http://github.com/rubyamf/rubyamf), both of which provide rack-compliant
|
||||||
|
# AMF gateways.
|
||||||
|
#
|
||||||
|
# # helloworld.ru
|
||||||
|
# require 'rocketamf'
|
||||||
|
#
|
||||||
|
# class HelloWorldApp
|
||||||
|
# APPLICATION_AMF = 'application/x-amf'.freeze
|
||||||
|
#
|
||||||
|
# def call env
|
||||||
|
# if is_amf?(env)
|
||||||
|
# # Wrap request and response
|
||||||
|
# env['rack.input'].rewind
|
||||||
|
# request = RocketAMF::Envelope.new.populate_from_stream(env['rack.input'].read)
|
||||||
|
# response = RocketAMF::Envelope.new
|
||||||
|
#
|
||||||
|
# # Handle request
|
||||||
|
# response.each_method_call request do |method, args|
|
||||||
|
# raise "Service #{method} does not exists" unless method == 'App.helloWorld'
|
||||||
|
# 'Hello world'
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# # Pass back response
|
||||||
|
# response_str = response.serialize
|
||||||
|
# return [200, {'Content-Type' => APPLICATION_AMF, 'Content-Length' => response_str.length.to_s}, [response_str]]
|
||||||
|
# else
|
||||||
|
# return [200, {'Content-Type' => 'text/plain', 'Content-Length' => '16' }, ["Rack AMF gateway"]]
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# private
|
||||||
|
# def is_amf? env
|
||||||
|
# return false unless env['CONTENT_TYPE'] == APPLICATION_AMF
|
||||||
|
# return false unless env['PATH_INFO'] == '/amf'
|
||||||
|
# return true
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# run HelloWorldApp.new
|
||||||
|
#
|
||||||
|
# == Advanced Serialization (encode_amf and IExternalizable)
|
||||||
|
#
|
||||||
|
# RocketAMF provides some additional functionality to support advanced
|
||||||
|
# serialization techniques. If you define an <tt>encode_amf</tt> method on your
|
||||||
|
# object, it will get called during serialization. It is passed a single argument,
|
||||||
|
# the serializer, and it can use the serializer stream, the <tt>serialize</tt>
|
||||||
|
# method, the <tt>write_array</tt> method, the <tt>write_object</tt> method, and
|
||||||
|
# the serializer version. Below is a simple example that uses <tt>write_object</tt>
|
||||||
|
# to customize the property hash that is used for serialization.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# class TestObject
|
||||||
|
# def encode_amf ser
|
||||||
|
# ser.write_object self, @attributes
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# If you plan on using the <tt>serialize</tt> method, make sure to pass in the
|
||||||
|
# current serializer version, or you could create a message that cannot be deserialized.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# class VariableObject
|
||||||
|
# def encode_amf ser
|
||||||
|
# if ser.version == 0
|
||||||
|
# ser.serialize 0, true
|
||||||
|
# else
|
||||||
|
# ser.serialize 3, false
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# If you wish to send and receive IExternalizable objects, you will need to
|
||||||
|
# implement <tt>encode_amf</tt>, <tt>read_external</tt>, and <tt>write_external</tt>.
|
||||||
|
# Below is an example of a ResultSet class that extends Array and serializes as
|
||||||
|
# an array collection. RocketAMF can automatically serialize arrays as
|
||||||
|
# ArrayCollection objects, so this is just an example of how you might implement
|
||||||
|
# an object that conforms to IExternalizable.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# class ResultSet < Array
|
||||||
|
# def encode_amf ser
|
||||||
|
# if ser.version == 0
|
||||||
|
# # Serialize as simple array in AMF0
|
||||||
|
# ser.write_array self
|
||||||
|
# else
|
||||||
|
# # Serialize as an ArrayCollection object
|
||||||
|
# # It conforms to IExternalizable, does not have any dynamic properties,
|
||||||
|
# # and has no "sealed" members. See the AMF3 specs for more details about
|
||||||
|
# # object traits.
|
||||||
|
# ser.write_object self, nil, {
|
||||||
|
# :class_name => "flex.messaging.io.ArrayCollection",
|
||||||
|
# :externalizable => true,
|
||||||
|
# :dynamic => false,
|
||||||
|
# :members => []
|
||||||
|
# }
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# # Write self as array to stream
|
||||||
|
# def write_external ser
|
||||||
|
# ser.write_array(self)
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# # Read array out and replace data with deserialized array.
|
||||||
|
# def read_external des
|
||||||
|
# replace(des.read_object)
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
module RocketAMF
|
||||||
|
begin
|
||||||
|
require 'rocketamf/ext'
|
||||||
|
rescue LoadError
|
||||||
|
require 'rocketamf/pure'
|
||||||
|
end
|
||||||
|
|
||||||
|
# Deserialize the AMF string _source_ of the given AMF version into a Ruby
|
||||||
|
# data structure and return it. Creates an instance of <tt>RocketAMF::Deserializer</tt>
|
||||||
|
# with a new instance of <tt>RocketAMF::ClassMapper</tt> and calls deserialize
|
||||||
|
# on it with the given source and amf version, returning the result.
|
||||||
|
def self.deserialize source, amf_version = 0
|
||||||
|
des = RocketAMF::Deserializer.new(RocketAMF::ClassMapper.new)
|
||||||
|
des.deserialize(amf_version, source)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Serialize the given Ruby data structure _obj_ into an AMF stream using the
|
||||||
|
# given AMF version. Creates an instance of <tt>RocketAMF::Serializer</tt>
|
||||||
|
# with a new instance of <tt>RocketAMF::ClassMapper</tt> and calls serialize
|
||||||
|
# on it with the given object and amf version, returning the result.
|
||||||
|
def self.serialize obj, amf_version = 0
|
||||||
|
ser = RocketAMF::Serializer.new(RocketAMF::ClassMapper.new)
|
||||||
|
ser.serialize(amf_version, obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
# We use const_missing to define the active ClassMapper at runtime. This way,
|
||||||
|
# heavy modification of class mapping functionality is still possible without
|
||||||
|
# forcing extenders to redefine the constant.
|
||||||
|
def self.const_missing const #:nodoc:
|
||||||
|
if const == :ClassMapper
|
||||||
|
RocketAMF.const_set(:ClassMapper, RocketAMF::ClassMapping)
|
||||||
|
else
|
||||||
|
super(const)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# The base exception for AMF errors.
|
||||||
|
class AMFError < StandardError; end
|
||||||
|
end
|
||||||
237
vendor/gems/RocketAMF-1.0.0/lib/rocketamf/class_mapping.rb
vendored
Normal file
237
vendor/gems/RocketAMF-1.0.0/lib/rocketamf/class_mapping.rb
vendored
Normal file
|
|
@ -0,0 +1,237 @@
|
||||||
|
require 'rocketamf/values/typed_hash'
|
||||||
|
require 'rocketamf/values/messages'
|
||||||
|
|
||||||
|
module RocketAMF
|
||||||
|
# Container for all mapped classes
|
||||||
|
class MappingSet
|
||||||
|
# Creates a mapping set object and populates the default mappings
|
||||||
|
def initialize
|
||||||
|
@as_mappings = {}
|
||||||
|
@ruby_mappings = {}
|
||||||
|
map_defaults
|
||||||
|
end
|
||||||
|
|
||||||
|
# Adds required mapping configs, calling map for the required base mappings.
|
||||||
|
# Designed to allow extenders to take advantage of required default mappings.
|
||||||
|
def map_defaults
|
||||||
|
map :as => 'flex.messaging.messages.AbstractMessage', :ruby => 'RocketAMF::Values::AbstractMessage'
|
||||||
|
map :as => 'flex.messaging.messages.RemotingMessage', :ruby => 'RocketAMF::Values::RemotingMessage'
|
||||||
|
map :as => 'flex.messaging.messages.AsyncMessage', :ruby => 'RocketAMF::Values::AsyncMessage'
|
||||||
|
map :as => 'DSA', :ruby => 'RocketAMF::Values::AsyncMessageExt'
|
||||||
|
map :as => 'flex.messaging.messages.CommandMessage', :ruby => 'RocketAMF::Values::CommandMessage'
|
||||||
|
map :as => 'DSC', :ruby => 'RocketAMF::Values::CommandMessageExt'
|
||||||
|
map :as => 'flex.messaging.messages.AcknowledgeMessage', :ruby => 'RocketAMF::Values::AcknowledgeMessage'
|
||||||
|
map :as => 'DSK', :ruby => 'RocketAMF::Values::AcknowledgeMessageExt'
|
||||||
|
map :as => 'flex.messaging.messages.ErrorMessage', :ruby => 'RocketAMF::Values::ErrorMessage'
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
# Map a given AS class to a ruby class.
|
||||||
|
#
|
||||||
|
# Use fully qualified names for both.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# m.map :as => 'com.example.Date', :ruby => 'Example::Date'
|
||||||
|
def map params
|
||||||
|
[:as, :ruby].each {|k| params[k] = params[k].to_s} # Convert params to strings
|
||||||
|
@as_mappings[params[:as]] = params[:ruby]
|
||||||
|
@ruby_mappings[params[:ruby]] = params[:as]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the AS class name for the given ruby class name, returing nil if
|
||||||
|
# not found
|
||||||
|
def get_as_class_name class_name #:nodoc:
|
||||||
|
@ruby_mappings[class_name.to_s]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the ruby class name for the given AS class name, returing nil if
|
||||||
|
# not found
|
||||||
|
def get_ruby_class_name class_name #:nodoc:
|
||||||
|
@as_mappings[class_name.to_s]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Handles class name mapping between actionscript and ruby and assists in
|
||||||
|
# serializing and deserializing data between them. Simply map an AS class to a
|
||||||
|
# ruby class and when the object is (de)serialized it will end up as the
|
||||||
|
# appropriate class.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# RocketAMF::ClassMapper.define do |m|
|
||||||
|
# m.map :as => 'AsClass', :ruby => 'RubyClass'
|
||||||
|
# m.map :as => 'vo.User', :ruby => 'Model::User'
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# == Object Population/Serialization
|
||||||
|
#
|
||||||
|
# In addition to handling class name mapping, it also provides helper methods
|
||||||
|
# for populating ruby objects from AMF and extracting properties from ruby objects
|
||||||
|
# for serialization. Support for hash-like objects and objects using
|
||||||
|
# <tt>attr_accessor</tt> for properties is currently built in, but custom classes
|
||||||
|
# may require subclassing the class mapper to add support.
|
||||||
|
#
|
||||||
|
# == Complete Replacement
|
||||||
|
#
|
||||||
|
# In some cases, it may be beneficial to replace the default provider of class
|
||||||
|
# mapping completely. In this case, simply assign your class mapper class to
|
||||||
|
# <tt>RocketAMF::ClassMapper</tt> after loading RocketAMF. Through the magic of
|
||||||
|
# <tt>const_missing</tt>, <tt>ClassMapper</tt> is only defined after the first
|
||||||
|
# access by default, so you get no annoying warning messages. Custom class mappers
|
||||||
|
# must implement the following methods on instances: <tt>use_array_collection</tt>,
|
||||||
|
# <tt>get_as_class_name</tt>, <tt>get_ruby_obj</tt>, <tt>populate_ruby_obj</tt>,
|
||||||
|
# and <tt>props_for_serialization</tt>. In addition, it should have a class level
|
||||||
|
# <tt>mappings</tt> method that returns the mapping set it's using, although its
|
||||||
|
# not required. If you'd like to see an example of what complete replacement
|
||||||
|
# offers, check out RubyAMF (http://github.com/rubyamf/rubyamf).
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# require 'rubygems'
|
||||||
|
# require 'rocketamf'
|
||||||
|
#
|
||||||
|
# RocketAMF::ClassMapper = MyCustomClassMapper
|
||||||
|
# # No warning about already initialized constant ClassMapper
|
||||||
|
# RocketAMF::ClassMapper # MyCustomClassMapper
|
||||||
|
#
|
||||||
|
# == C ClassMapper
|
||||||
|
#
|
||||||
|
# The C class mapper, <tt>RocketAMF::Ext::FastClassMapping</tt>, has the same
|
||||||
|
# public API that <tt>RubyAMF::ClassMapping</tt> does, but has some additional
|
||||||
|
# performance optimizations that may interfere with the proper serialization of
|
||||||
|
# objects. To reduce the cost of processing public methods for every object,
|
||||||
|
# its implementation of <tt>props_for_serialization</tt> caches valid properties
|
||||||
|
# by class, using the class as the hash key for property lookup. This means that
|
||||||
|
# adding and removing properties from instances while serializing using a given
|
||||||
|
# class mapper instance will result in the changes not being detected. As such,
|
||||||
|
# it's not enabled by default. So long as you aren't planning on modifying
|
||||||
|
# classes during serialization using <tt>encode_amf</tt>, the faster C class
|
||||||
|
# mapper should be perfectly safe to use.
|
||||||
|
#
|
||||||
|
# Activating the C Class Mapper:
|
||||||
|
#
|
||||||
|
# require 'rubygems'
|
||||||
|
# require 'rocketamf'
|
||||||
|
# RocketAMF::ClassMapper = RocketAMF::Ext::FastClassMapping
|
||||||
|
class ClassMapping
|
||||||
|
class << self
|
||||||
|
# Global configuration variable for sending Arrays as ArrayCollections.
|
||||||
|
# Defaults to false.
|
||||||
|
attr_accessor :use_array_collection
|
||||||
|
|
||||||
|
# Returns the mapping set with all the class mappings that is currently
|
||||||
|
# being used.
|
||||||
|
def mappings
|
||||||
|
@mappings ||= MappingSet.new
|
||||||
|
end
|
||||||
|
|
||||||
|
# Define class mappings in the block. Block is passed a <tt>MappingSet</tt> object
|
||||||
|
# as the first parameter.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# RocketAMF::ClassMapper.define do |m|
|
||||||
|
# m.map :as => 'AsClass', :ruby => 'RubyClass'
|
||||||
|
# end
|
||||||
|
def define &block #:yields: mapping_set
|
||||||
|
yield mappings
|
||||||
|
end
|
||||||
|
|
||||||
|
# Reset all class mappings except the defaults and return
|
||||||
|
# <tt>use_array_collection</tt> to false
|
||||||
|
def reset
|
||||||
|
@use_array_collection = false
|
||||||
|
@mappings = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :use_array_collection
|
||||||
|
|
||||||
|
# Copies configuration from class level configs to populate object
|
||||||
|
def initialize
|
||||||
|
@mappings = self.class.mappings
|
||||||
|
@use_array_collection = self.class.use_array_collection === true
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the ActionScript class name for the given ruby object. Will also
|
||||||
|
# take a string containing the ruby class name.
|
||||||
|
def get_as_class_name obj
|
||||||
|
# Get class name
|
||||||
|
if obj.is_a?(String)
|
||||||
|
ruby_class_name = obj
|
||||||
|
elsif obj.is_a?(Values::TypedHash)
|
||||||
|
ruby_class_name = obj.type
|
||||||
|
elsif obj.is_a?(Hash)
|
||||||
|
return nil
|
||||||
|
else
|
||||||
|
ruby_class_name = obj.class.name
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get mapped AS class name
|
||||||
|
@mappings.get_as_class_name ruby_class_name
|
||||||
|
end
|
||||||
|
|
||||||
|
# Instantiates a ruby object using the mapping configuration based on the
|
||||||
|
# source ActionScript class name. If there is no mapping defined, it returns
|
||||||
|
# a <tt>RocketAMF::Values::TypedHash</tt> with the serialized class name.
|
||||||
|
def get_ruby_obj as_class_name
|
||||||
|
ruby_class_name = @mappings.get_ruby_class_name as_class_name
|
||||||
|
if ruby_class_name.nil?
|
||||||
|
# Populate a simple hash, since no mapping
|
||||||
|
return Values::TypedHash.new(as_class_name)
|
||||||
|
else
|
||||||
|
ruby_class = ruby_class_name.split('::').inject(Kernel) {|scope, const_name| scope.const_get(const_name)}
|
||||||
|
return ruby_class.new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Populates the ruby object using the given properties. props and
|
||||||
|
# dynamic_props will be hashes with symbols for keys.
|
||||||
|
def populate_ruby_obj obj, props, dynamic_props=nil
|
||||||
|
props.merge! dynamic_props if dynamic_props
|
||||||
|
|
||||||
|
# Don't even bother checking if it responds to setter methods if it's a TypedHash
|
||||||
|
if obj.is_a?(Values::TypedHash)
|
||||||
|
obj.merge! props
|
||||||
|
return obj
|
||||||
|
end
|
||||||
|
|
||||||
|
# Some type of object
|
||||||
|
hash_like = obj.respond_to?("[]=")
|
||||||
|
props.each do |key, value|
|
||||||
|
if obj.respond_to?("#{key}=")
|
||||||
|
obj.send("#{key}=", value)
|
||||||
|
elsif hash_like
|
||||||
|
obj[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
obj
|
||||||
|
end
|
||||||
|
|
||||||
|
# Extracts all exportable properties from the given ruby object and returns
|
||||||
|
# them in a hash. If overriding, make sure to return a hash wth string keys
|
||||||
|
# unless you are only going to be using the native C extensions, as the pure
|
||||||
|
# ruby serializer performs a sort on the keys to acheive consistent, testable
|
||||||
|
# results.
|
||||||
|
def props_for_serialization ruby_obj
|
||||||
|
# Handle hashes
|
||||||
|
if ruby_obj.is_a?(Hash)
|
||||||
|
# Stringify keys to make it easier later on and allow sorting
|
||||||
|
h = {}
|
||||||
|
ruby_obj.each {|k,v| h[k.to_s] = v}
|
||||||
|
return h
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generic object serializer
|
||||||
|
props = {}
|
||||||
|
@ignored_props ||= Object.new.public_methods
|
||||||
|
(ruby_obj.public_methods - @ignored_props).each do |method_name|
|
||||||
|
# Add them to the prop hash if they take no arguments
|
||||||
|
method_def = ruby_obj.method(method_name)
|
||||||
|
props[method_name.to_s] = ruby_obj.send(method_name) if method_def.arity == 0
|
||||||
|
end
|
||||||
|
props
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
50
vendor/gems/RocketAMF-1.0.0/lib/rocketamf/constants.rb
vendored
Normal file
50
vendor/gems/RocketAMF-1.0.0/lib/rocketamf/constants.rb
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
module RocketAMF
|
||||||
|
# AMF0 Type Markers
|
||||||
|
AMF0_NUMBER_MARKER = 0x00 #"\000"
|
||||||
|
AMF0_BOOLEAN_MARKER = 0x01 #"\001"
|
||||||
|
AMF0_STRING_MARKER = 0x02 #"\002"
|
||||||
|
AMF0_OBJECT_MARKER = 0x03 #"\003"
|
||||||
|
AMF0_MOVIE_CLIP_MARKER = 0x04 #"\004" # Unused
|
||||||
|
AMF0_NULL_MARKER = 0x05 #"\005"
|
||||||
|
AMF0_UNDEFINED_MARKER = 0x06 #"\006"
|
||||||
|
AMF0_REFERENCE_MARKER = 0x07 #"\a"
|
||||||
|
AMF0_HASH_MARKER = 0x08 #"\b"
|
||||||
|
AMF0_OBJECT_END_MARKER = 0x09 #"\t"
|
||||||
|
AMF0_STRICT_ARRAY_MARKER = 0x0A #"\n"
|
||||||
|
AMF0_DATE_MARKER = 0x0B #"\v"
|
||||||
|
AMF0_LONG_STRING_MARKER = 0x0C #"\f"
|
||||||
|
AMF0_UNSUPPORTED_MARKER = 0x0D #"\r"
|
||||||
|
AMF0_RECORDSET_MARKER = 0x0E #"\016" # Unused
|
||||||
|
AMF0_XML_MARKER = 0x0F #"\017"
|
||||||
|
AMF0_TYPED_OBJECT_MARKER = 0x10 #"\020"
|
||||||
|
AMF0_AMF3_MARKER = 0x11 #"\021"
|
||||||
|
|
||||||
|
# AMF3 Type Markers
|
||||||
|
AMF3_UNDEFINED_MARKER = 0x00 #"\000"
|
||||||
|
AMF3_NULL_MARKER = 0x01 #"\001"
|
||||||
|
AMF3_FALSE_MARKER = 0x02 #"\002"
|
||||||
|
AMF3_TRUE_MARKER = 0x03 #"\003"
|
||||||
|
AMF3_INTEGER_MARKER = 0x04 #"\004"
|
||||||
|
AMF3_DOUBLE_MARKER = 0x05 #"\005"
|
||||||
|
AMF3_STRING_MARKER = 0x06 #"\006"
|
||||||
|
AMF3_XML_DOC_MARKER = 0x07 #"\a"
|
||||||
|
AMF3_DATE_MARKER = 0x08 #"\b"
|
||||||
|
AMF3_ARRAY_MARKER = 0x09 #"\t"
|
||||||
|
AMF3_OBJECT_MARKER = 0x0A #"\n"
|
||||||
|
AMF3_XML_MARKER = 0x0B #"\v"
|
||||||
|
AMF3_BYTE_ARRAY_MARKER = 0x0C #"\f"
|
||||||
|
AMF3_VECTOR_INT_MARKER = 0x0D #"\r"
|
||||||
|
AMF3_VECTOR_UINT_MARKER = 0x0E #"\016"
|
||||||
|
AMF3_VECTOR_DOUBLE_MARKER = 0x0F #"\017"
|
||||||
|
AMF3_VECTOR_OBJECT_MARKER = 0x10 #"\020"
|
||||||
|
AMF3_DICT_MARKER = 0x11 #"\021"
|
||||||
|
|
||||||
|
# Other AMF3 Markers
|
||||||
|
AMF3_EMPTY_STRING = 0x01
|
||||||
|
AMF3_CLOSE_DYNAMIC_OBJECT = 0x01
|
||||||
|
AMF3_CLOSE_DYNAMIC_ARRAY = 0x01
|
||||||
|
|
||||||
|
# Other Constants
|
||||||
|
MAX_INTEGER = 268435455
|
||||||
|
MIN_INTEGER = -268435456
|
||||||
|
end
|
||||||
28
vendor/gems/RocketAMF-1.0.0/lib/rocketamf/ext.rb
vendored
Normal file
28
vendor/gems/RocketAMF-1.0.0/lib/rocketamf/ext.rb
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
begin
|
||||||
|
# Fat binaries for Windows
|
||||||
|
RUBY_VERSION =~ /(\d+.\d+)/
|
||||||
|
require "#{$1}/rocketamf_ext"
|
||||||
|
rescue LoadError
|
||||||
|
require "rocketamf_ext"
|
||||||
|
end
|
||||||
|
|
||||||
|
module RocketAMF
|
||||||
|
# This module holds all the modules/classes that implement AMF's functionality
|
||||||
|
# in C
|
||||||
|
module Ext
|
||||||
|
$DEBUG and warn "Using C library for RocketAMF."
|
||||||
|
end
|
||||||
|
|
||||||
|
#:stopdoc:
|
||||||
|
# Import serializer/deserializer
|
||||||
|
Deserializer = RocketAMF::Ext::Deserializer
|
||||||
|
Serializer = RocketAMF::Ext::Serializer
|
||||||
|
|
||||||
|
# Modify envelope so it can serialize/deserialize
|
||||||
|
class Envelope
|
||||||
|
remove_method :populate_from_stream
|
||||||
|
remove_method :serialize
|
||||||
|
include RocketAMF::Ext::Envelope
|
||||||
|
end
|
||||||
|
#:startdoc:
|
||||||
|
end
|
||||||
22
vendor/gems/RocketAMF-1.0.0/lib/rocketamf/extensions.rb
vendored
Normal file
22
vendor/gems/RocketAMF-1.0.0/lib/rocketamf/extensions.rb
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Joc's monkeypatch for string bytesize (only available in 1.8.7+)
|
||||||
|
if !"amf".respond_to? :bytesize
|
||||||
|
class String #:nodoc:
|
||||||
|
def bytesize
|
||||||
|
self.size
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add <tt>ArrayCollection</tt> override to arrays
|
||||||
|
class Array
|
||||||
|
# Override <tt>RocketAMF::ClassMapper.use_array_collection</tt> setting for
|
||||||
|
# this array. Adds <tt>is_array_collection?</tt> method, which is used by the
|
||||||
|
# serializer over the global config if defined.
|
||||||
|
def is_array_collection= a
|
||||||
|
@is_array_collection = a
|
||||||
|
|
||||||
|
def self.is_array_collection? #:nodoc:
|
||||||
|
@is_array_collection
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
24
vendor/gems/RocketAMF-1.0.0/lib/rocketamf/pure.rb
vendored
Normal file
24
vendor/gems/RocketAMF-1.0.0/lib/rocketamf/pure.rb
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
require 'rocketamf/pure/deserializer'
|
||||||
|
require 'rocketamf/pure/serializer'
|
||||||
|
require 'rocketamf/pure/remoting'
|
||||||
|
|
||||||
|
module RocketAMF
|
||||||
|
# This module holds all the modules/classes that implement AMF's functionality
|
||||||
|
# in pure ruby
|
||||||
|
module Pure
|
||||||
|
$DEBUG and warn "Using pure library for RocketAMF."
|
||||||
|
end
|
||||||
|
|
||||||
|
#:stopdoc:
|
||||||
|
# Import serializer/deserializer
|
||||||
|
Deserializer = RocketAMF::Pure::Deserializer
|
||||||
|
Serializer = RocketAMF::Pure::Serializer
|
||||||
|
|
||||||
|
# Modify envelope so it can serialize/deserialize
|
||||||
|
class Envelope
|
||||||
|
remove_method :populate_from_stream
|
||||||
|
remove_method :serialize
|
||||||
|
include RocketAMF::Pure::Envelope
|
||||||
|
end
|
||||||
|
#:startdoc:
|
||||||
|
end
|
||||||
455
vendor/gems/RocketAMF-1.0.0/lib/rocketamf/pure/deserializer.rb
vendored
Normal file
455
vendor/gems/RocketAMF-1.0.0/lib/rocketamf/pure/deserializer.rb
vendored
Normal file
|
|
@ -0,0 +1,455 @@
|
||||||
|
require 'rocketamf/pure/io_helpers'
|
||||||
|
|
||||||
|
module RocketAMF
|
||||||
|
module Pure
|
||||||
|
# Pure ruby deserializer for AMF0 and AMF3
|
||||||
|
class Deserializer
|
||||||
|
attr_accessor :source
|
||||||
|
|
||||||
|
# Pass in the class mapper instance to use when deserializing. This
|
||||||
|
# enables better caching behavior in the class mapper and allows
|
||||||
|
# one to change mappings between deserialization attempts.
|
||||||
|
def initialize class_mapper
|
||||||
|
@class_mapper = class_mapper
|
||||||
|
end
|
||||||
|
|
||||||
|
# Deserialize the source using AMF0 or AMF3. Source should either
|
||||||
|
# be a string or StringIO object. If you pass a StringIO object,
|
||||||
|
# it will have its position updated to the end of the deserialized
|
||||||
|
# data.
|
||||||
|
def deserialize version, source
|
||||||
|
raise ArgumentError, "unsupported version #{version}" unless [0,3].include?(version)
|
||||||
|
@version = version
|
||||||
|
|
||||||
|
if StringIO === source
|
||||||
|
@source = source
|
||||||
|
elsif source
|
||||||
|
@source = StringIO.new(source)
|
||||||
|
elsif @source.nil?
|
||||||
|
raise AMFError, "no source to deserialize"
|
||||||
|
end
|
||||||
|
|
||||||
|
if @version == 0
|
||||||
|
@ref_cache = []
|
||||||
|
return amf0_deserialize
|
||||||
|
else
|
||||||
|
@string_cache = []
|
||||||
|
@object_cache = []
|
||||||
|
@trait_cache = []
|
||||||
|
return amf3_deserialize
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Reads an object from the deserializer's stream and returns it.
|
||||||
|
def read_object
|
||||||
|
if @version == 0
|
||||||
|
return amf0_deserialize
|
||||||
|
else
|
||||||
|
return amf3_deserialize
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
include RocketAMF::Pure::ReadIOHelpers
|
||||||
|
|
||||||
|
def amf0_deserialize type=nil
|
||||||
|
type = read_int8 @source unless type
|
||||||
|
case type
|
||||||
|
when AMF0_NUMBER_MARKER
|
||||||
|
amf0_read_number
|
||||||
|
when AMF0_BOOLEAN_MARKER
|
||||||
|
amf0_read_boolean
|
||||||
|
when AMF0_STRING_MARKER
|
||||||
|
amf0_read_string
|
||||||
|
when AMF0_OBJECT_MARKER
|
||||||
|
amf0_read_object
|
||||||
|
when AMF0_NULL_MARKER
|
||||||
|
nil
|
||||||
|
when AMF0_UNDEFINED_MARKER
|
||||||
|
nil
|
||||||
|
when AMF0_REFERENCE_MARKER
|
||||||
|
amf0_read_reference
|
||||||
|
when AMF0_HASH_MARKER
|
||||||
|
amf0_read_hash
|
||||||
|
when AMF0_STRICT_ARRAY_MARKER
|
||||||
|
amf0_read_array
|
||||||
|
when AMF0_DATE_MARKER
|
||||||
|
amf0_read_date
|
||||||
|
when AMF0_LONG_STRING_MARKER
|
||||||
|
amf0_read_string true
|
||||||
|
when AMF0_UNSUPPORTED_MARKER
|
||||||
|
nil
|
||||||
|
when AMF0_XML_MARKER
|
||||||
|
amf0_read_string true
|
||||||
|
when AMF0_TYPED_OBJECT_MARKER
|
||||||
|
amf0_read_typed_object
|
||||||
|
when AMF0_AMF3_MARKER
|
||||||
|
deserialize(3, nil)
|
||||||
|
else
|
||||||
|
raise AMFError, "Invalid type: #{type}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf0_read_number
|
||||||
|
res = read_double @source
|
||||||
|
(res.is_a?(Float) && res.nan?) ? nil : res # check for NaN and convert them to nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf0_read_boolean
|
||||||
|
read_int8(@source) != 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf0_read_string long=false
|
||||||
|
len = long ? read_word32_network(@source) : read_word16_network(@source)
|
||||||
|
str = @source.read(len)
|
||||||
|
str.force_encoding("UTF-8") if str.respond_to?(:force_encoding)
|
||||||
|
str
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf0_read_reference
|
||||||
|
index = read_word16_network(@source)
|
||||||
|
@ref_cache[index]
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf0_read_array
|
||||||
|
len = read_word32_network(@source)
|
||||||
|
array = []
|
||||||
|
@ref_cache << array
|
||||||
|
|
||||||
|
0.upto(len - 1) do
|
||||||
|
array << amf0_deserialize
|
||||||
|
end
|
||||||
|
array
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf0_read_date
|
||||||
|
seconds = read_double(@source).to_f/1000
|
||||||
|
time = Time.at(seconds)
|
||||||
|
tz = read_word16_network(@source) # Unused
|
||||||
|
time
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf0_read_props obj={}
|
||||||
|
while true
|
||||||
|
key = amf0_read_string
|
||||||
|
type = read_int8 @source
|
||||||
|
break if type == AMF0_OBJECT_END_MARKER
|
||||||
|
obj[key] = amf0_deserialize(type)
|
||||||
|
end
|
||||||
|
obj
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf0_read_hash
|
||||||
|
len = read_word32_network(@source) # Read and ignore length
|
||||||
|
obj = {}
|
||||||
|
@ref_cache << obj
|
||||||
|
amf0_read_props obj
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf0_read_object add_to_ref_cache=true
|
||||||
|
# Create "object" and add to ref cache (it's always a Hash)
|
||||||
|
obj = @class_mapper.get_ruby_obj ""
|
||||||
|
@ref_cache << obj
|
||||||
|
|
||||||
|
# Populate object
|
||||||
|
props = amf0_read_props
|
||||||
|
@class_mapper.populate_ruby_obj obj, props
|
||||||
|
return obj
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf0_read_typed_object
|
||||||
|
# Create object to add to ref cache
|
||||||
|
class_name = amf0_read_string
|
||||||
|
obj = @class_mapper.get_ruby_obj class_name
|
||||||
|
@ref_cache << obj
|
||||||
|
|
||||||
|
# Populate object
|
||||||
|
props = amf0_read_props
|
||||||
|
@class_mapper.populate_ruby_obj obj, props
|
||||||
|
return obj
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf3_deserialize
|
||||||
|
type = read_int8 @source
|
||||||
|
case type
|
||||||
|
when AMF3_UNDEFINED_MARKER
|
||||||
|
nil
|
||||||
|
when AMF3_NULL_MARKER
|
||||||
|
nil
|
||||||
|
when AMF3_FALSE_MARKER
|
||||||
|
false
|
||||||
|
when AMF3_TRUE_MARKER
|
||||||
|
true
|
||||||
|
when AMF3_INTEGER_MARKER
|
||||||
|
amf3_read_integer
|
||||||
|
when AMF3_DOUBLE_MARKER
|
||||||
|
amf3_read_number
|
||||||
|
when AMF3_STRING_MARKER
|
||||||
|
amf3_read_string
|
||||||
|
when AMF3_XML_DOC_MARKER, AMF3_XML_MARKER
|
||||||
|
amf3_read_xml
|
||||||
|
when AMF3_DATE_MARKER
|
||||||
|
amf3_read_date
|
||||||
|
when AMF3_ARRAY_MARKER
|
||||||
|
amf3_read_array
|
||||||
|
when AMF3_OBJECT_MARKER
|
||||||
|
amf3_read_object
|
||||||
|
when AMF3_BYTE_ARRAY_MARKER
|
||||||
|
amf3_read_byte_array
|
||||||
|
when AMF3_VECTOR_INT_MARKER, AMF3_VECTOR_UINT_MARKER, AMF3_VECTOR_DOUBLE_MARKER, AMF3_VECTOR_OBJECT_MARKER
|
||||||
|
amf3_read_vector type
|
||||||
|
when AMF3_DICT_MARKER
|
||||||
|
amf3_read_dict
|
||||||
|
else
|
||||||
|
raise AMFError, "Invalid type: #{type}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf3_read_integer
|
||||||
|
n = 0
|
||||||
|
b = read_word8(@source) || 0
|
||||||
|
result = 0
|
||||||
|
|
||||||
|
while ((b & 0x80) != 0 && n < 3)
|
||||||
|
result = result << 7
|
||||||
|
result = result | (b & 0x7f)
|
||||||
|
b = read_word8(@source) || 0
|
||||||
|
n = n + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if (n < 3)
|
||||||
|
result = result << 7
|
||||||
|
result = result | b
|
||||||
|
else
|
||||||
|
#Use all 8 bits from the 4th byte
|
||||||
|
result = result << 8
|
||||||
|
result = result | b
|
||||||
|
|
||||||
|
#Check if the integer should be negative
|
||||||
|
if (result > MAX_INTEGER)
|
||||||
|
result -= (1 << 29)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf3_read_number
|
||||||
|
res = read_double @source
|
||||||
|
(res.is_a?(Float) && res.nan?) ? nil : res # check for NaN and convert them to nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf3_read_string
|
||||||
|
type = amf3_read_integer
|
||||||
|
is_reference = (type & 0x01) == 0
|
||||||
|
|
||||||
|
if is_reference
|
||||||
|
reference = type >> 1
|
||||||
|
return @string_cache[reference]
|
||||||
|
else
|
||||||
|
length = type >> 1
|
||||||
|
str = ""
|
||||||
|
if length > 0
|
||||||
|
str = @source.read(length)
|
||||||
|
str.force_encoding("UTF-8") if str.respond_to?(:force_encoding)
|
||||||
|
@string_cache << str
|
||||||
|
end
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf3_read_xml
|
||||||
|
type = amf3_read_integer
|
||||||
|
is_reference = (type & 0x01) == 0
|
||||||
|
|
||||||
|
if is_reference
|
||||||
|
reference = type >> 1
|
||||||
|
return @object_cache[reference]
|
||||||
|
else
|
||||||
|
length = type >> 1
|
||||||
|
str = ""
|
||||||
|
if length > 0
|
||||||
|
str = @source.read(length)
|
||||||
|
str.force_encoding("UTF-8") if str.respond_to?(:force_encoding)
|
||||||
|
@object_cache << str
|
||||||
|
end
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf3_read_byte_array
|
||||||
|
type = amf3_read_integer
|
||||||
|
is_reference = (type & 0x01) == 0
|
||||||
|
|
||||||
|
if is_reference
|
||||||
|
reference = type >> 1
|
||||||
|
return @object_cache[reference]
|
||||||
|
else
|
||||||
|
length = type >> 1
|
||||||
|
obj = StringIO.new @source.read(length)
|
||||||
|
@object_cache << obj
|
||||||
|
obj
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf3_read_array
|
||||||
|
type = amf3_read_integer
|
||||||
|
is_reference = (type & 0x01) == 0
|
||||||
|
|
||||||
|
if is_reference
|
||||||
|
reference = type >> 1
|
||||||
|
return @object_cache[reference]
|
||||||
|
else
|
||||||
|
length = type >> 1
|
||||||
|
property_name = amf3_read_string
|
||||||
|
array = property_name.length > 0 ? {} : []
|
||||||
|
@object_cache << array
|
||||||
|
|
||||||
|
while property_name.length > 0
|
||||||
|
value = amf3_deserialize
|
||||||
|
array[property_name] = value
|
||||||
|
property_name = amf3_read_string
|
||||||
|
end
|
||||||
|
0.upto(length - 1) {|i| array[i] = amf3_deserialize }
|
||||||
|
|
||||||
|
array
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf3_read_object
|
||||||
|
type = amf3_read_integer
|
||||||
|
is_reference = (type & 0x01) == 0
|
||||||
|
|
||||||
|
if is_reference
|
||||||
|
reference = type >> 1
|
||||||
|
return @object_cache[reference]
|
||||||
|
else
|
||||||
|
class_type = type >> 1
|
||||||
|
class_is_reference = (class_type & 0x01) == 0
|
||||||
|
|
||||||
|
if class_is_reference
|
||||||
|
reference = class_type >> 1
|
||||||
|
traits = @trait_cache[reference]
|
||||||
|
else
|
||||||
|
externalizable = (class_type & 0x02) != 0
|
||||||
|
dynamic = (class_type & 0x04) != 0
|
||||||
|
attribute_count = class_type >> 3
|
||||||
|
class_name = amf3_read_string
|
||||||
|
|
||||||
|
class_attributes = []
|
||||||
|
attribute_count.times{class_attributes << amf3_read_string} # Read class members
|
||||||
|
|
||||||
|
traits = {
|
||||||
|
:class_name => class_name,
|
||||||
|
:members => class_attributes,
|
||||||
|
:externalizable => externalizable,
|
||||||
|
:dynamic => dynamic
|
||||||
|
}
|
||||||
|
@trait_cache << traits
|
||||||
|
end
|
||||||
|
|
||||||
|
# Optimization for deserializing ArrayCollection
|
||||||
|
if traits[:class_name] == "flex.messaging.io.ArrayCollection"
|
||||||
|
arr = amf3_deserialize # Adds ArrayCollection array to object cache
|
||||||
|
@object_cache << arr # Add again for ArrayCollection source array
|
||||||
|
return arr
|
||||||
|
end
|
||||||
|
|
||||||
|
obj = @class_mapper.get_ruby_obj traits[:class_name]
|
||||||
|
@object_cache << obj
|
||||||
|
|
||||||
|
if traits[:externalizable]
|
||||||
|
obj.read_external self
|
||||||
|
else
|
||||||
|
props = {}
|
||||||
|
traits[:members].each do |key|
|
||||||
|
value = amf3_deserialize
|
||||||
|
props[key] = value
|
||||||
|
end
|
||||||
|
|
||||||
|
dynamic_props = nil
|
||||||
|
if traits[:dynamic]
|
||||||
|
dynamic_props = {}
|
||||||
|
while (key = amf3_read_string) && key.length != 0 do # read next key
|
||||||
|
value = amf3_deserialize
|
||||||
|
dynamic_props[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@class_mapper.populate_ruby_obj obj, props, dynamic_props
|
||||||
|
end
|
||||||
|
obj
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf3_read_date
|
||||||
|
type = amf3_read_integer
|
||||||
|
is_reference = (type & 0x01) == 0
|
||||||
|
if is_reference
|
||||||
|
reference = type >> 1
|
||||||
|
return @object_cache[reference]
|
||||||
|
else
|
||||||
|
seconds = read_double(@source).to_f/1000
|
||||||
|
time = Time.at(seconds)
|
||||||
|
@object_cache << time
|
||||||
|
time
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf3_read_dict
|
||||||
|
type = amf3_read_integer
|
||||||
|
is_reference = (type & 0x01) == 0
|
||||||
|
if is_reference
|
||||||
|
reference = type >> 1
|
||||||
|
return @object_cache[reference]
|
||||||
|
else
|
||||||
|
dict = {}
|
||||||
|
@object_cache << dict
|
||||||
|
length = type >> 1
|
||||||
|
weak_keys = read_int8 @source # Ignore: Not supported in ruby
|
||||||
|
0.upto(length - 1) do |i|
|
||||||
|
dict[amf3_deserialize] = amf3_deserialize
|
||||||
|
end
|
||||||
|
dict
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf3_read_vector vector_type
|
||||||
|
type = amf3_read_integer
|
||||||
|
is_reference = (type & 0x01) == 0
|
||||||
|
if is_reference
|
||||||
|
reference = type >> 1
|
||||||
|
return @object_cache[reference]
|
||||||
|
else
|
||||||
|
vec = []
|
||||||
|
@object_cache << vec
|
||||||
|
length = type >> 1
|
||||||
|
fixed_vector = read_int8 @source # Ignore
|
||||||
|
case vector_type
|
||||||
|
when AMF3_VECTOR_INT_MARKER
|
||||||
|
0.upto(length - 1) do |i|
|
||||||
|
int = read_word32_network(@source)
|
||||||
|
int = int - 2**32 if int > MAX_INTEGER
|
||||||
|
vec << int
|
||||||
|
end
|
||||||
|
when AMF3_VECTOR_UINT_MARKER
|
||||||
|
0.upto(length - 1) do |i|
|
||||||
|
vec << read_word32_network(@source)
|
||||||
|
puts vec[i].to_s(2)
|
||||||
|
end
|
||||||
|
when AMF3_VECTOR_DOUBLE_MARKER
|
||||||
|
0.upto(length - 1) do |i|
|
||||||
|
vec << amf3_read_number
|
||||||
|
end
|
||||||
|
when AMF3_VECTOR_OBJECT_MARKER
|
||||||
|
vector_class = amf3_read_string # Ignore
|
||||||
|
puts vector_class
|
||||||
|
0.upto(length - 1) do |i|
|
||||||
|
vec << amf3_deserialize
|
||||||
|
end
|
||||||
|
end
|
||||||
|
vec
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
94
vendor/gems/RocketAMF-1.0.0/lib/rocketamf/pure/io_helpers.rb
vendored
Normal file
94
vendor/gems/RocketAMF-1.0.0/lib/rocketamf/pure/io_helpers.rb
vendored
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
module RocketAMF
|
||||||
|
module Pure
|
||||||
|
module ReadIOHelpers #:nodoc:
|
||||||
|
def read_int8 source
|
||||||
|
source.read(1).unpack('c').first
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_word8 source
|
||||||
|
source.read(1).unpack('C').first
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_double source
|
||||||
|
source.read(8).unpack('G').first
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_word16_network source
|
||||||
|
source.read(2).unpack('n').first
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_int16_network source
|
||||||
|
str = source.read(2)
|
||||||
|
str.reverse! if byte_order_little? # swap bytes as native=little (and we want network)
|
||||||
|
str.unpack('s').first
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_word32_network source
|
||||||
|
source.read(4).unpack('N').first
|
||||||
|
end
|
||||||
|
|
||||||
|
def byte_order
|
||||||
|
if [0x12345678].pack("L") == "\x12\x34\x56\x78"
|
||||||
|
:BigEndian
|
||||||
|
else
|
||||||
|
:LittleEndian
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def byte_order_little?
|
||||||
|
(byte_order == :LittleEndian) ? true : false;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module WriteIOHelpers #:nodoc:
|
||||||
|
def pack_integer(integer)
|
||||||
|
integer = integer & 0x1fffffff
|
||||||
|
if(integer < 0x80)
|
||||||
|
[integer].pack('c')
|
||||||
|
elsif(integer < 0x4000)
|
||||||
|
[integer >> 7 & 0x7f | 0x80].pack('c')+
|
||||||
|
[integer & 0x7f].pack('c')
|
||||||
|
elsif(integer < 0x200000)
|
||||||
|
[integer >> 14 & 0x7f | 0x80].pack('c') +
|
||||||
|
[integer >> 7 & 0x7f | 0x80].pack('c') +
|
||||||
|
[integer & 0x7f].pack('c')
|
||||||
|
else
|
||||||
|
[integer >> 22 & 0x7f | 0x80].pack('c')+
|
||||||
|
[integer >> 15 & 0x7f | 0x80].pack('c')+
|
||||||
|
[integer >> 8 & 0x7f | 0x80].pack('c')+
|
||||||
|
[integer & 0xff].pack('c')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def pack_double(double)
|
||||||
|
[double].pack('G')
|
||||||
|
end
|
||||||
|
|
||||||
|
def pack_int8(val)
|
||||||
|
[val].pack('c')
|
||||||
|
end
|
||||||
|
|
||||||
|
def pack_int16_network(val)
|
||||||
|
[val].pack('n')
|
||||||
|
end
|
||||||
|
|
||||||
|
def pack_word32_network(val)
|
||||||
|
str = [val].pack('L')
|
||||||
|
str.reverse! if byte_order_little? # swap bytes as native=little (and we want network)
|
||||||
|
str
|
||||||
|
end
|
||||||
|
|
||||||
|
def byte_order
|
||||||
|
if [0x12345678].pack("L") == "\x12\x34\x56\x78"
|
||||||
|
:BigEndian
|
||||||
|
else
|
||||||
|
:LittleEndian
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def byte_order_little?
|
||||||
|
(byte_order == :LittleEndian) ? true : false;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
117
vendor/gems/RocketAMF-1.0.0/lib/rocketamf/pure/remoting.rb
vendored
Normal file
117
vendor/gems/RocketAMF-1.0.0/lib/rocketamf/pure/remoting.rb
vendored
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
require 'rocketamf/pure/io_helpers'
|
||||||
|
|
||||||
|
module RocketAMF
|
||||||
|
module Pure
|
||||||
|
# Included into RocketAMF::Envelope, this module replaces the
|
||||||
|
# populate_from_stream and serialize methods with actual working versions
|
||||||
|
module Envelope
|
||||||
|
# Included into RocketAMF::Envelope, this method handles deserializing an
|
||||||
|
# AMF request/response into the envelope
|
||||||
|
def populate_from_stream stream, class_mapper=nil
|
||||||
|
stream = StringIO.new(stream) unless StringIO === stream
|
||||||
|
des = Deserializer.new(class_mapper || RocketAMF::ClassMapper.new)
|
||||||
|
des.source = stream
|
||||||
|
|
||||||
|
# Initialize
|
||||||
|
@amf_version = 0
|
||||||
|
@headers = {}
|
||||||
|
@messages = []
|
||||||
|
|
||||||
|
# Read AMF version
|
||||||
|
@amf_version = read_word16_network stream
|
||||||
|
|
||||||
|
# Read in headers
|
||||||
|
header_count = read_word16_network stream
|
||||||
|
0.upto(header_count-1) do
|
||||||
|
name = stream.read(read_word16_network(stream))
|
||||||
|
name.force_encoding("UTF-8") if name.respond_to?(:force_encoding)
|
||||||
|
|
||||||
|
must_understand = read_int8(stream) != 0
|
||||||
|
|
||||||
|
length = read_word32_network stream
|
||||||
|
data = des.deserialize(0, nil)
|
||||||
|
|
||||||
|
@headers[name] = RocketAMF::Header.new(name, must_understand, data)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Read in messages
|
||||||
|
message_count = read_word16_network stream
|
||||||
|
0.upto(message_count-1) do
|
||||||
|
target_uri = stream.read(read_word16_network(stream))
|
||||||
|
target_uri.force_encoding("UTF-8") if target_uri.respond_to?(:force_encoding)
|
||||||
|
|
||||||
|
response_uri = stream.read(read_word16_network(stream))
|
||||||
|
response_uri.force_encoding("UTF-8") if response_uri.respond_to?(:force_encoding)
|
||||||
|
|
||||||
|
length = read_word32_network stream
|
||||||
|
data = des.deserialize(0, nil)
|
||||||
|
if data.is_a?(Array) && data.length == 1 && data[0].is_a?(::RocketAMF::Values::AbstractMessage)
|
||||||
|
data = data[0]
|
||||||
|
end
|
||||||
|
|
||||||
|
@messages << RocketAMF::Message.new(target_uri, response_uri, data)
|
||||||
|
end
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
# Included into RocketAMF::Envelope, this method handles serializing an
|
||||||
|
# AMF request/response into a string
|
||||||
|
def serialize class_mapper=nil
|
||||||
|
ser = Serializer.new(class_mapper || RocketAMF::ClassMapper.new)
|
||||||
|
stream = ser.stream
|
||||||
|
|
||||||
|
# Write version
|
||||||
|
stream << pack_int16_network(@amf_version)
|
||||||
|
|
||||||
|
# Write headers
|
||||||
|
stream << pack_int16_network(@headers.length) # Header count
|
||||||
|
@headers.each_value do |h|
|
||||||
|
# Write header name
|
||||||
|
name_str = h.name
|
||||||
|
name_str.encode!("UTF-8").force_encoding("ASCII-8BIT") if name_str.respond_to?(:encode)
|
||||||
|
stream << pack_int16_network(name_str.bytesize)
|
||||||
|
stream << name_str
|
||||||
|
|
||||||
|
# Write must understand flag
|
||||||
|
stream << pack_int8(h.must_understand ? 1 : 0)
|
||||||
|
|
||||||
|
# Serialize data
|
||||||
|
stream << pack_word32_network(-1) # length of data - -1 if you don't know
|
||||||
|
ser.serialize(0, h.data)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Write messages
|
||||||
|
stream << pack_int16_network(@messages.length) # Message count
|
||||||
|
@messages.each do |m|
|
||||||
|
# Write target_uri
|
||||||
|
uri_str = m.target_uri
|
||||||
|
uri_str.encode!("UTF-8").force_encoding("ASCII-8BIT") if uri_str.respond_to?(:encode)
|
||||||
|
stream << pack_int16_network(uri_str.bytesize)
|
||||||
|
stream << uri_str
|
||||||
|
|
||||||
|
# Write response_uri
|
||||||
|
uri_str = m.response_uri
|
||||||
|
uri_str.encode!("UTF-8").force_encoding("ASCII-8BIT") if uri_str.respond_to?(:encode)
|
||||||
|
stream << pack_int16_network(uri_str.bytesize)
|
||||||
|
stream << uri_str
|
||||||
|
|
||||||
|
# Serialize data
|
||||||
|
stream << pack_word32_network(-1) # length of data - -1 if you don't know
|
||||||
|
if @amf_version == 3
|
||||||
|
stream << AMF0_AMF3_MARKER
|
||||||
|
ser.serialize(3, m.data)
|
||||||
|
else
|
||||||
|
ser.serialize(0, m.data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
stream
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
include RocketAMF::Pure::ReadIOHelpers
|
||||||
|
include RocketAMF::Pure::WriteIOHelpers
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
474
vendor/gems/RocketAMF-1.0.0/lib/rocketamf/pure/serializer.rb
vendored
Normal file
474
vendor/gems/RocketAMF-1.0.0/lib/rocketamf/pure/serializer.rb
vendored
Normal file
|
|
@ -0,0 +1,474 @@
|
||||||
|
require 'rocketamf/pure/io_helpers'
|
||||||
|
|
||||||
|
module RocketAMF
|
||||||
|
module Pure
|
||||||
|
# Pure ruby serializer for AMF0 and AMF3
|
||||||
|
class Serializer
|
||||||
|
attr_reader :stream, :version
|
||||||
|
|
||||||
|
# Pass in the class mapper instance to use when serializing. This enables
|
||||||
|
# better caching behavior in the class mapper and allows one to change
|
||||||
|
# mappings between serialization attempts.
|
||||||
|
def initialize class_mapper
|
||||||
|
@class_mapper = class_mapper
|
||||||
|
@stream = ""
|
||||||
|
@depth = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
# Serialize the given object using AMF0 or AMF3. Can be called from inside
|
||||||
|
# encode_amf, but make sure to pass in the proper version or it may not be
|
||||||
|
# possible to decode. Use the serializer version attribute for this.
|
||||||
|
def serialize version, obj
|
||||||
|
raise ArgumentError, "unsupported version #{version}" unless [0,3].include?(version)
|
||||||
|
@version = version
|
||||||
|
|
||||||
|
# Initialize caches
|
||||||
|
if @depth == 0
|
||||||
|
if @version == 0
|
||||||
|
@ref_cache = SerializerCache.new :object
|
||||||
|
else
|
||||||
|
@string_cache = SerializerCache.new :string
|
||||||
|
@object_cache = SerializerCache.new :object
|
||||||
|
@trait_cache = SerializerCache.new :string
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@depth += 1
|
||||||
|
|
||||||
|
# Perform serialization
|
||||||
|
if @version == 0
|
||||||
|
amf0_serialize(obj)
|
||||||
|
else
|
||||||
|
amf3_serialize(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
@depth -= 1
|
||||||
|
if @depth == 0
|
||||||
|
@ref_cache = nil
|
||||||
|
@string_cache = nil
|
||||||
|
@object_cache = nil
|
||||||
|
@trait_cache = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return @stream
|
||||||
|
end
|
||||||
|
|
||||||
|
# Helper for writing arrays inside encode_amf. It uses the current AMF
|
||||||
|
# version to write the array.
|
||||||
|
def write_array arr
|
||||||
|
if @version == 0
|
||||||
|
amf0_write_array arr
|
||||||
|
else
|
||||||
|
amf3_write_array arr
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Helper for writing objects inside encode_amf. It uses the current AMF
|
||||||
|
# version to write the object. If you pass in a property hash, it will use
|
||||||
|
# it rather than having the class mapper determine properties. For AMF3,
|
||||||
|
# you can also specify a traits hash, which can be used to reduce serialized
|
||||||
|
# data size or serialize things as externalizable.
|
||||||
|
def write_object obj, props=nil, traits=nil
|
||||||
|
if @version == 0
|
||||||
|
amf0_write_object obj, props
|
||||||
|
else
|
||||||
|
amf3_write_object obj, props, traits
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
include RocketAMF::Pure::WriteIOHelpers
|
||||||
|
|
||||||
|
def amf0_serialize obj
|
||||||
|
if @ref_cache[obj] != nil
|
||||||
|
amf0_write_reference @ref_cache[obj]
|
||||||
|
elsif obj.respond_to?(:encode_amf)
|
||||||
|
obj.encode_amf(self)
|
||||||
|
elsif obj.is_a?(NilClass)
|
||||||
|
amf0_write_null
|
||||||
|
elsif obj.is_a?(TrueClass) || obj.is_a?(FalseClass)
|
||||||
|
amf0_write_boolean obj
|
||||||
|
elsif obj.is_a?(Numeric)
|
||||||
|
amf0_write_number obj
|
||||||
|
elsif obj.is_a?(Symbol) || obj.is_a?(String)
|
||||||
|
amf0_write_string obj.to_s
|
||||||
|
elsif obj.is_a?(Time)
|
||||||
|
amf0_write_time obj
|
||||||
|
elsif obj.is_a?(Date)
|
||||||
|
amf0_write_date obj
|
||||||
|
elsif obj.is_a?(Array)
|
||||||
|
amf0_write_array obj
|
||||||
|
elsif obj.is_a?(Hash) ||obj.is_a?(Object)
|
||||||
|
amf0_write_object obj
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf0_write_null
|
||||||
|
@stream << AMF0_NULL_MARKER
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf0_write_boolean bool
|
||||||
|
@stream << AMF0_BOOLEAN_MARKER
|
||||||
|
@stream << pack_int8(bool ? 1 : 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf0_write_number num
|
||||||
|
@stream << AMF0_NUMBER_MARKER
|
||||||
|
@stream << pack_double(num)
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf0_write_string str
|
||||||
|
str = str.encode("UTF-8").force_encoding("ASCII-8BIT") if str.respond_to?(:encode)
|
||||||
|
len = str.bytesize
|
||||||
|
if len > 2**16-1
|
||||||
|
@stream << AMF0_LONG_STRING_MARKER
|
||||||
|
@stream << pack_word32_network(len)
|
||||||
|
else
|
||||||
|
@stream << AMF0_STRING_MARKER
|
||||||
|
@stream << pack_int16_network(len)
|
||||||
|
end
|
||||||
|
@stream << str
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf0_write_time time
|
||||||
|
@stream << AMF0_DATE_MARKER
|
||||||
|
|
||||||
|
time = time.getutc # Dup and convert to UTC
|
||||||
|
milli = (time.to_f * 1000).to_i
|
||||||
|
@stream << pack_double(milli)
|
||||||
|
|
||||||
|
@stream << pack_int16_network(0) # Time zone
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf0_write_date date
|
||||||
|
@stream << AMF0_DATE_MARKER
|
||||||
|
@stream << pack_double(date.strftime("%Q").to_i)
|
||||||
|
@stream << pack_int16_network(0) # Time zone
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf0_write_reference index
|
||||||
|
@stream << AMF0_REFERENCE_MARKER
|
||||||
|
@stream << pack_int16_network(index)
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf0_write_array array
|
||||||
|
@ref_cache.add_obj array
|
||||||
|
@stream << AMF0_STRICT_ARRAY_MARKER
|
||||||
|
@stream << pack_word32_network(array.length)
|
||||||
|
array.each do |elem|
|
||||||
|
amf0_serialize elem
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf0_write_object obj, props=nil
|
||||||
|
@ref_cache.add_obj obj
|
||||||
|
|
||||||
|
props = @class_mapper.props_for_serialization obj if props.nil?
|
||||||
|
|
||||||
|
# Is it a typed object?
|
||||||
|
class_name = @class_mapper.get_as_class_name obj
|
||||||
|
if class_name
|
||||||
|
class_name = class_name.encode("UTF-8").force_encoding("ASCII-8BIT") if class_name.respond_to?(:encode)
|
||||||
|
@stream << AMF0_TYPED_OBJECT_MARKER
|
||||||
|
@stream << pack_int16_network(class_name.bytesize)
|
||||||
|
@stream << class_name
|
||||||
|
else
|
||||||
|
@stream << AMF0_OBJECT_MARKER
|
||||||
|
end
|
||||||
|
|
||||||
|
# Write prop list
|
||||||
|
props.sort.each do |key, value| # Sort keys before writing
|
||||||
|
key = key.encode("UTF-8").force_encoding("ASCII-8BIT") if key.respond_to?(:encode)
|
||||||
|
@stream << pack_int16_network(key.bytesize)
|
||||||
|
@stream << key
|
||||||
|
amf0_serialize value
|
||||||
|
end
|
||||||
|
|
||||||
|
# Write end
|
||||||
|
@stream << pack_int16_network(0)
|
||||||
|
@stream << AMF0_OBJECT_END_MARKER
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf3_serialize obj
|
||||||
|
if obj.respond_to?(:encode_amf)
|
||||||
|
obj.encode_amf(self)
|
||||||
|
elsif obj.is_a?(NilClass)
|
||||||
|
amf3_write_null
|
||||||
|
elsif obj.is_a?(TrueClass)
|
||||||
|
amf3_write_true
|
||||||
|
elsif obj.is_a?(FalseClass)
|
||||||
|
amf3_write_false
|
||||||
|
elsif obj.is_a?(Numeric)
|
||||||
|
amf3_write_numeric obj
|
||||||
|
elsif obj.is_a?(Symbol) || obj.is_a?(String)
|
||||||
|
amf3_write_string obj.to_s
|
||||||
|
elsif obj.is_a?(Time)
|
||||||
|
amf3_write_time obj
|
||||||
|
elsif obj.is_a?(Date)
|
||||||
|
amf3_write_date obj
|
||||||
|
elsif obj.is_a?(StringIO)
|
||||||
|
amf3_write_byte_array obj
|
||||||
|
elsif obj.is_a?(Array)
|
||||||
|
amf3_write_array obj
|
||||||
|
elsif obj.is_a?(Hash) || obj.is_a?(Object)
|
||||||
|
amf3_write_object obj
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf3_write_reference index
|
||||||
|
header = index << 1 # shift value left to leave a low bit of 0
|
||||||
|
@stream << pack_integer(header)
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf3_write_null
|
||||||
|
@stream << AMF3_NULL_MARKER
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf3_write_true
|
||||||
|
@stream << AMF3_TRUE_MARKER
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf3_write_false
|
||||||
|
@stream << AMF3_FALSE_MARKER
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf3_write_numeric num
|
||||||
|
if !num.integer? || num < MIN_INTEGER || num > MAX_INTEGER # Check valid range for 29 bits
|
||||||
|
@stream << AMF3_DOUBLE_MARKER
|
||||||
|
@stream << pack_double(num)
|
||||||
|
else
|
||||||
|
@stream << AMF3_INTEGER_MARKER
|
||||||
|
@stream << pack_integer(num)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf3_write_string str
|
||||||
|
@stream << AMF3_STRING_MARKER
|
||||||
|
amf3_write_utf8_vr str
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf3_write_time time
|
||||||
|
@stream << AMF3_DATE_MARKER
|
||||||
|
if @object_cache[time] != nil
|
||||||
|
amf3_write_reference @object_cache[time]
|
||||||
|
else
|
||||||
|
# Cache time
|
||||||
|
@object_cache.add_obj time
|
||||||
|
|
||||||
|
# Build AMF string
|
||||||
|
time = time.getutc # Dup and convert to UTC
|
||||||
|
milli = (time.to_f * 1000).to_i
|
||||||
|
@stream << AMF3_NULL_MARKER
|
||||||
|
@stream << pack_double(milli)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf3_write_date date
|
||||||
|
@stream << AMF3_DATE_MARKER
|
||||||
|
if @object_cache[date] != nil
|
||||||
|
amf3_write_reference @object_cache[date]
|
||||||
|
else
|
||||||
|
# Cache date
|
||||||
|
@object_cache.add_obj date
|
||||||
|
|
||||||
|
# Build AMF string
|
||||||
|
@stream << AMF3_NULL_MARKER
|
||||||
|
@stream << pack_double(date.strftime("%Q").to_i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf3_write_byte_array array
|
||||||
|
@stream << AMF3_BYTE_ARRAY_MARKER
|
||||||
|
if @object_cache[array] != nil
|
||||||
|
amf3_write_reference @object_cache[array]
|
||||||
|
else
|
||||||
|
@object_cache.add_obj array
|
||||||
|
str = array.string
|
||||||
|
@stream << pack_integer(str.bytesize << 1 | 1)
|
||||||
|
@stream << str
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf3_write_array array
|
||||||
|
# Is it an array collection?
|
||||||
|
is_ac = false
|
||||||
|
if array.respond_to?(:is_array_collection?)
|
||||||
|
is_ac = array.is_array_collection?
|
||||||
|
else
|
||||||
|
is_ac = @class_mapper.use_array_collection
|
||||||
|
end
|
||||||
|
|
||||||
|
# Write type marker
|
||||||
|
@stream << (is_ac ? AMF3_OBJECT_MARKER : AMF3_ARRAY_MARKER)
|
||||||
|
|
||||||
|
# Write reference or cache array
|
||||||
|
if @object_cache[array] != nil
|
||||||
|
amf3_write_reference @object_cache[array]
|
||||||
|
return
|
||||||
|
else
|
||||||
|
@object_cache.add_obj array
|
||||||
|
@object_cache.add_obj nil if is_ac # The array collection source array
|
||||||
|
end
|
||||||
|
|
||||||
|
# Write out traits and array marker if it's an array collection
|
||||||
|
if is_ac
|
||||||
|
class_name = "flex.messaging.io.ArrayCollection"
|
||||||
|
if @trait_cache[class_name] != nil
|
||||||
|
@stream << pack_integer(@trait_cache[class_name] << 2 | 0x01)
|
||||||
|
else
|
||||||
|
@trait_cache.add_obj class_name
|
||||||
|
@stream << "\a" # Externalizable, non-dynamic
|
||||||
|
amf3_write_utf8_vr(class_name)
|
||||||
|
end
|
||||||
|
@stream << AMF3_ARRAY_MARKER
|
||||||
|
end
|
||||||
|
|
||||||
|
# Build AMF string for array
|
||||||
|
header = array.length << 1 # make room for a low bit of 1
|
||||||
|
header = header | 1 # set the low bit to 1
|
||||||
|
@stream << pack_integer(header)
|
||||||
|
@stream << AMF3_CLOSE_DYNAMIC_ARRAY
|
||||||
|
array.each do |elem|
|
||||||
|
amf3_serialize elem
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf3_write_object obj, props=nil, traits=nil
|
||||||
|
@stream << AMF3_OBJECT_MARKER
|
||||||
|
|
||||||
|
# Caching...
|
||||||
|
if @object_cache[obj] != nil
|
||||||
|
amf3_write_reference @object_cache[obj]
|
||||||
|
return
|
||||||
|
end
|
||||||
|
@object_cache.add_obj obj
|
||||||
|
|
||||||
|
# Calculate traits if not given
|
||||||
|
is_default = false
|
||||||
|
if traits.nil?
|
||||||
|
traits = {
|
||||||
|
:class_name => @class_mapper.get_as_class_name(obj),
|
||||||
|
:members => [],
|
||||||
|
:externalizable => false,
|
||||||
|
:dynamic => true
|
||||||
|
}
|
||||||
|
is_default = true unless traits[:class_name]
|
||||||
|
end
|
||||||
|
class_name = is_default ? "__default__" : traits[:class_name]
|
||||||
|
|
||||||
|
# Write out traits
|
||||||
|
if (class_name && @trait_cache[class_name] != nil)
|
||||||
|
@stream << pack_integer(@trait_cache[class_name] << 2 | 0x01)
|
||||||
|
else
|
||||||
|
@trait_cache.add_obj class_name if class_name
|
||||||
|
|
||||||
|
# Write out trait header
|
||||||
|
header = 0x03 # Not object ref and not trait ref
|
||||||
|
header |= 0x02 << 2 if traits[:dynamic]
|
||||||
|
header |= 0x01 << 2 if traits[:externalizable]
|
||||||
|
header |= traits[:members].length << 4
|
||||||
|
@stream << pack_integer(header)
|
||||||
|
|
||||||
|
# Write out class name
|
||||||
|
if class_name == "__default__"
|
||||||
|
amf3_write_utf8_vr("")
|
||||||
|
else
|
||||||
|
amf3_write_utf8_vr(class_name.to_s)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Write out members
|
||||||
|
traits[:members].each {|m| amf3_write_utf8_vr(m)}
|
||||||
|
end
|
||||||
|
|
||||||
|
# If externalizable, take externalized data shortcut
|
||||||
|
if traits[:externalizable]
|
||||||
|
obj.write_external(self)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
# Extract properties if not given
|
||||||
|
props = @class_mapper.props_for_serialization(obj) if props.nil?
|
||||||
|
|
||||||
|
# Write out sealed properties
|
||||||
|
traits[:members].each do |m|
|
||||||
|
amf3_serialize props[m]
|
||||||
|
props.delete(m)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Write out dynamic properties
|
||||||
|
if traits[:dynamic]
|
||||||
|
# Write out dynamic properties
|
||||||
|
props.sort.each do |key, val| # Sort props until Ruby 1.9 becomes common
|
||||||
|
amf3_write_utf8_vr key.to_s
|
||||||
|
amf3_serialize val
|
||||||
|
end
|
||||||
|
|
||||||
|
# Write close
|
||||||
|
@stream << AMF3_CLOSE_DYNAMIC_OBJECT
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def amf3_write_utf8_vr str, encode=true
|
||||||
|
if str.respond_to?(:encode)
|
||||||
|
if encode
|
||||||
|
str = str.encode("UTF-8")
|
||||||
|
else
|
||||||
|
str = str.dup if str.frozen?
|
||||||
|
end
|
||||||
|
str.force_encoding("ASCII-8BIT")
|
||||||
|
end
|
||||||
|
|
||||||
|
if str == ''
|
||||||
|
@stream << AMF3_EMPTY_STRING
|
||||||
|
elsif @string_cache[str] != nil
|
||||||
|
amf3_write_reference @string_cache[str]
|
||||||
|
else
|
||||||
|
# Cache string
|
||||||
|
@string_cache.add_obj str
|
||||||
|
|
||||||
|
# Build AMF string
|
||||||
|
@stream << pack_integer(str.bytesize << 1 | 1)
|
||||||
|
@stream << str
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class SerializerCache #:nodoc:
|
||||||
|
def self.new type
|
||||||
|
if type == :string
|
||||||
|
StringCache.new
|
||||||
|
elsif type == :object
|
||||||
|
ObjectCache.new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class StringCache < Hash #:nodoc:
|
||||||
|
def initialize
|
||||||
|
@cache_index = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_obj str
|
||||||
|
self[str] = @cache_index
|
||||||
|
@cache_index += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class ObjectCache < Hash #:nodoc:
|
||||||
|
def initialize
|
||||||
|
@cache_index = 0
|
||||||
|
@obj_references = []
|
||||||
|
end
|
||||||
|
|
||||||
|
def [] obj
|
||||||
|
super(obj.object_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_obj obj
|
||||||
|
@obj_references << obj
|
||||||
|
self[obj.object_id] = @cache_index
|
||||||
|
@cache_index += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
196
vendor/gems/RocketAMF-1.0.0/lib/rocketamf/remoting.rb
vendored
Normal file
196
vendor/gems/RocketAMF-1.0.0/lib/rocketamf/remoting.rb
vendored
Normal file
|
|
@ -0,0 +1,196 @@
|
||||||
|
module RocketAMF
|
||||||
|
# Container for the AMF request/response.
|
||||||
|
class Envelope
|
||||||
|
attr_reader :amf_version, :headers, :messages
|
||||||
|
|
||||||
|
def initialize props={}
|
||||||
|
@amf_version = props[:amf_version] || 0
|
||||||
|
@headers = props[:headers] || {}
|
||||||
|
@messages = props[:messages] || []
|
||||||
|
end
|
||||||
|
|
||||||
|
# Populates the envelope from the given stream or string using the given
|
||||||
|
# class mapper, or creates a new one. Returns self for easy chaining.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# req = RocketAMF::Envelope.new.populate_from_stream(env['rack.input'].read)
|
||||||
|
#--
|
||||||
|
# Implemented in pure/remoting.rb RocketAMF::Pure::Envelope
|
||||||
|
def populate_from_stream stream, class_mapper=nil
|
||||||
|
raise AMFError, 'Must load "rocketamf/pure"'
|
||||||
|
end
|
||||||
|
|
||||||
|
# Creates the appropriate message and adds it to <tt>messages</tt> to call
|
||||||
|
# the given target using the standard (old) remoting APIs. You can call multiple
|
||||||
|
# targets in the same request, unlike with the flex remotings APIs.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# req = RocketAMF::Envelope.new
|
||||||
|
# req.call 'test', "arg_1", ["args", "args"]
|
||||||
|
# req.call 'Controller.action'
|
||||||
|
def call target, *args
|
||||||
|
raise "Cannot use different call types" unless @call_type.nil? || @call_type == :simple
|
||||||
|
@call_type = :simple
|
||||||
|
|
||||||
|
msg_num = messages.length+1
|
||||||
|
@messages << RocketAMF::Message.new(target, "/#{msg_num}", args)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Creates the appropriate message and adds it to <tt>messages</tt> using the
|
||||||
|
# new flex (RemoteObject) remoting APIs. You can only make one flex remoting
|
||||||
|
# call per envelope, and the AMF version must be set to 3.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# req = RocketAMF::Envelope.new :amf_version => 3
|
||||||
|
# req.call_flex 'Controller.action', "arg_1", ["args", "args"]
|
||||||
|
def call_flex target, *args
|
||||||
|
raise "Can only call one flex target per request" if @call_type == :flex
|
||||||
|
raise "Cannot use different call types" if @call_type == :simple
|
||||||
|
raise "Cannot use flex remoting calls with AMF0" if @amf_version != 3
|
||||||
|
@call_type = :flex
|
||||||
|
|
||||||
|
flex_msg = RocketAMF::Values::RemotingMessage.new
|
||||||
|
target_parts = target.split(".")
|
||||||
|
flex_msg.operation = target_parts.pop # Use pop so that a missing source is possible without issues
|
||||||
|
flex_msg.source = target_parts.pop
|
||||||
|
flex_msg.body = args
|
||||||
|
@messages << RocketAMF::Message.new('null', '/2', flex_msg) # /2 because it always sends a command message before
|
||||||
|
end
|
||||||
|
|
||||||
|
# Serializes the envelope to a string using the given class mapper, or creates
|
||||||
|
# a new one, and returns the result
|
||||||
|
#--
|
||||||
|
# Implemented in pure/remoting.rb RocketAMF::Pure::Envelope
|
||||||
|
def serialize class_mapper=nil
|
||||||
|
raise AMFError, 'Must load "rocketamf/pure"'
|
||||||
|
end
|
||||||
|
|
||||||
|
# Builds response from the request, iterating over each method call and using
|
||||||
|
# the return value as the method call's return value. Marks as envelope as
|
||||||
|
# constructed after running.
|
||||||
|
#--
|
||||||
|
# Iterate over all the sent messages. If they're somthing we can handle, like
|
||||||
|
# a command message, then simply add the response message ourselves. If it's
|
||||||
|
# a method call, then call the block with the method and args, catching errors
|
||||||
|
# for handling. Then create the appropriate response message using the return
|
||||||
|
# value of the block as the return value for the method call.
|
||||||
|
def each_method_call request, &block
|
||||||
|
raise 'Response already constructed' if @constructed
|
||||||
|
|
||||||
|
# Set version from response
|
||||||
|
# Can't just copy version because FMS sends version as 1
|
||||||
|
@amf_version = request.amf_version == 3 ? 3 : 0
|
||||||
|
|
||||||
|
request.messages.each do |m|
|
||||||
|
# What's the request body?
|
||||||
|
case m.data
|
||||||
|
when Values::CommandMessage
|
||||||
|
# Pings should be responded to with an AcknowledgeMessage built using the ping
|
||||||
|
# Everything else is unsupported
|
||||||
|
command_msg = m.data
|
||||||
|
if command_msg.operation == Values::CommandMessage::CLIENT_PING_OPERATION
|
||||||
|
response_value = Values::AcknowledgeMessage.new(command_msg)
|
||||||
|
else
|
||||||
|
e = Exception.new("CommandMessage #{command_msg.operation} not implemented")
|
||||||
|
e.set_backtrace ["RocketAMF::Envelope each_method_call"]
|
||||||
|
response_value = Values::ErrorMessage.new(command_msg, e)
|
||||||
|
end
|
||||||
|
when Values::RemotingMessage
|
||||||
|
# Using RemoteObject style message calls
|
||||||
|
remoting_msg = m.data
|
||||||
|
acknowledge_msg = Values::AcknowledgeMessage.new(remoting_msg)
|
||||||
|
method_base = remoting_msg.source.to_s.empty? ? '' : remoting_msg.source+'.'
|
||||||
|
body = dispatch_call :method => method_base+remoting_msg.operation, :args => remoting_msg.body, :source => remoting_msg, :block => block
|
||||||
|
|
||||||
|
# Response should be the bare ErrorMessage if there was an error
|
||||||
|
if body.is_a?(Values::ErrorMessage)
|
||||||
|
response_value = body
|
||||||
|
else
|
||||||
|
acknowledge_msg.body = body
|
||||||
|
response_value = acknowledge_msg
|
||||||
|
end
|
||||||
|
else
|
||||||
|
# Standard response message
|
||||||
|
response_value = dispatch_call :method => m.target_uri, :args => m.data, :source => m, :block => block
|
||||||
|
end
|
||||||
|
|
||||||
|
target_uri = m.response_uri
|
||||||
|
target_uri += response_value.is_a?(Values::ErrorMessage) ? '/onStatus' : '/onResult'
|
||||||
|
@messages << ::RocketAMF::Message.new(target_uri, '', response_value)
|
||||||
|
end
|
||||||
|
|
||||||
|
@constructed = true
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the result of a response envelope, or an array of results if there
|
||||||
|
# are multiple action call messages. It automatically unwraps flex-style
|
||||||
|
# RemoteObject response messages, where the response result is inside a
|
||||||
|
# RocketAMF::Values::AcknowledgeMessage.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# req = RocketAMF::Envelope.new
|
||||||
|
# req.call('TestController.test', 'first_arg', 'second_arg')
|
||||||
|
# res = RocketAMF::Envelope.new
|
||||||
|
# res.each_method_call req do |method, args|
|
||||||
|
# ['a', 'b']
|
||||||
|
# end
|
||||||
|
# res.result #=> ['a', 'b']
|
||||||
|
def result
|
||||||
|
results = []
|
||||||
|
messages.each do |msg|
|
||||||
|
if msg.data.is_a?(Values::AcknowledgeMessage)
|
||||||
|
results << msg.data.body
|
||||||
|
else
|
||||||
|
results << msg.data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
results.length > 1 ? results : results[0]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Whether or not the response has been constructed. Can be used to prevent
|
||||||
|
# serialization when no processing has taken place.
|
||||||
|
def constructed?
|
||||||
|
@constructed
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return the serialized envelope as a string
|
||||||
|
def to_s
|
||||||
|
serialize
|
||||||
|
end
|
||||||
|
|
||||||
|
def dispatch_call p #:nodoc:
|
||||||
|
begin
|
||||||
|
p[:block].call(p[:method], p[:args])
|
||||||
|
rescue Exception => e
|
||||||
|
# Create ErrorMessage object using the source message as the base
|
||||||
|
Values::ErrorMessage.new(p[:source], e)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# RocketAMF::Envelope header
|
||||||
|
class Header
|
||||||
|
attr_accessor :name, :must_understand, :data
|
||||||
|
|
||||||
|
def initialize name, must_understand, data
|
||||||
|
@name = name
|
||||||
|
@must_understand = must_understand
|
||||||
|
@data = data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# RocketAMF::Envelope message
|
||||||
|
class Message
|
||||||
|
attr_accessor :target_uri, :response_uri, :data
|
||||||
|
|
||||||
|
def initialize target_uri, response_uri, data
|
||||||
|
@target_uri = target_uri
|
||||||
|
@response_uri = response_uri
|
||||||
|
@data = data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
214
vendor/gems/RocketAMF-1.0.0/lib/rocketamf/values/messages.rb
vendored
Normal file
214
vendor/gems/RocketAMF-1.0.0/lib/rocketamf/values/messages.rb
vendored
Normal file
|
|
@ -0,0 +1,214 @@
|
||||||
|
module RocketAMF
|
||||||
|
module Values #:nodoc:
|
||||||
|
# Base class for all special AS3 response messages. Maps to
|
||||||
|
# <tt>flex.messaging.messages.AbstractMessage</tt>.
|
||||||
|
class AbstractMessage
|
||||||
|
EXTERNALIZABLE_FIELDS = [
|
||||||
|
%w[ body clientId destination headers messageId timestamp timeToLive ],
|
||||||
|
%w[ clientIdBytes messageIdBytes ]
|
||||||
|
]
|
||||||
|
attr_accessor :clientId
|
||||||
|
attr_accessor :destination
|
||||||
|
attr_accessor :messageId
|
||||||
|
attr_accessor :timestamp
|
||||||
|
attr_accessor :timeToLive
|
||||||
|
attr_accessor :headers
|
||||||
|
attr_accessor :body
|
||||||
|
|
||||||
|
def clientIdBytes= bytes
|
||||||
|
@clientId = pretty_uuid(bytes) unless bytes.nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
def messageIdBytes= bytes
|
||||||
|
@messageId = pretty_uuid(bytes) unless bytes.nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_external des
|
||||||
|
read_external_fields des, EXTERNALIZABLE_FIELDS
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def rand_uuid
|
||||||
|
[8,4,4,4,12].map {|n| rand_hex_3(n)}.join('-').to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def rand_hex_3(l)
|
||||||
|
"%0#{l}x" % rand(1 << l*4)
|
||||||
|
end
|
||||||
|
|
||||||
|
def pretty_uuid bytes
|
||||||
|
"%08x-%04x-%04x-%04x-%08x%04x" % bytes.string.unpack("NnnnNn")
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_external_fields des, fields
|
||||||
|
# Read flags
|
||||||
|
flags = []
|
||||||
|
loop do
|
||||||
|
flags << des.source.read(1).unpack('C').first
|
||||||
|
break if flags.last < 128
|
||||||
|
end
|
||||||
|
|
||||||
|
# Read fields and any remaining unmapped fields in a byte-set
|
||||||
|
fields.each_with_index do |list, i|
|
||||||
|
break if flags[i].nil?
|
||||||
|
|
||||||
|
list.each_with_index do |name, j|
|
||||||
|
if flags[i] & 2**j != 0
|
||||||
|
send("#{name}=", des.read_object)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Read remaining flags even though we don't recognize them
|
||||||
|
# Zero out high bit, as it's the has-next-field marker
|
||||||
|
f = (flags[i] & ~128) >> list.length
|
||||||
|
while f > 0
|
||||||
|
des.read_object if (f & 1) != 0
|
||||||
|
f >>= 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Maps to <tt>flex.messaging.messages.RemotingMessage</tt>
|
||||||
|
class RemotingMessage < AbstractMessage
|
||||||
|
# The name of the service to be called including package name
|
||||||
|
attr_accessor :source
|
||||||
|
|
||||||
|
# The name of the method to be called
|
||||||
|
attr_accessor :operation
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@clientId = nil
|
||||||
|
@destination = nil
|
||||||
|
@messageId = rand_uuid
|
||||||
|
@timestamp = Time.new.to_i*100
|
||||||
|
@timeToLive = 0
|
||||||
|
@headers = {}
|
||||||
|
@body = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Maps to <tt>flex.messaging.messages.AsyncMessage</tt>
|
||||||
|
class AsyncMessage < AbstractMessage
|
||||||
|
EXTERNALIZABLE_FIELDS = [
|
||||||
|
%w[ correlationId correlationIdBytes]
|
||||||
|
]
|
||||||
|
attr_accessor :correlationId
|
||||||
|
|
||||||
|
def correlationIdBytes= bytes
|
||||||
|
@correlationId = pretty_uuid(bytes) unless bytes.nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_external des
|
||||||
|
super des
|
||||||
|
read_external_fields des, EXTERNALIZABLE_FIELDS
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class AsyncMessageExt < AsyncMessage #:nodoc:
|
||||||
|
end
|
||||||
|
|
||||||
|
# Maps to <tt>flex.messaging.messages.CommandMessage</tt>
|
||||||
|
class CommandMessage < AsyncMessage
|
||||||
|
SUBSCRIBE_OPERATION = 0
|
||||||
|
UNSUSBSCRIBE_OPERATION = 1
|
||||||
|
POLL_OPERATION = 2
|
||||||
|
CLIENT_SYNC_OPERATION = 4
|
||||||
|
CLIENT_PING_OPERATION = 5
|
||||||
|
CLUSTER_REQUEST_OPERATION = 7
|
||||||
|
LOGIN_OPERATION = 8
|
||||||
|
LOGOUT_OPERATION = 9
|
||||||
|
SESSION_INVALIDATE_OPERATION = 10
|
||||||
|
MULTI_SUBSCRIBE_OPERATION = 11
|
||||||
|
DISCONNECT_OPERATION = 12
|
||||||
|
UNKNOWN_OPERATION = 10000
|
||||||
|
|
||||||
|
EXTERNALIZABLE_FIELDS = [
|
||||||
|
%w[ operation ]
|
||||||
|
]
|
||||||
|
attr_accessor :operation
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@operation = UNKNOWN_OPERATION
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_external des
|
||||||
|
super des
|
||||||
|
read_external_fields des, EXTERNALIZABLE_FIELDS
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class CommandMessageExt < CommandMessage #:nodoc:
|
||||||
|
end
|
||||||
|
|
||||||
|
# Maps to <tt>flex.messaging.messages.AcknowledgeMessage</tt>
|
||||||
|
class AcknowledgeMessage < AsyncMessage
|
||||||
|
EXTERNALIZABLE_FIELDS = [[]]
|
||||||
|
|
||||||
|
def initialize message=nil
|
||||||
|
@clientId = rand_uuid
|
||||||
|
@destination = nil
|
||||||
|
@messageId = rand_uuid
|
||||||
|
@timestamp = Time.new.to_i*100
|
||||||
|
@timeToLive = 0
|
||||||
|
@headers = {}
|
||||||
|
@body = nil
|
||||||
|
|
||||||
|
if message.is_a?(AbstractMessage)
|
||||||
|
@correlationId = message.messageId
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_external des
|
||||||
|
super des
|
||||||
|
read_external_fields des, EXTERNALIZABLE_FIELDS
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class AcknowledgeMessageExt < AcknowledgeMessage #:nodoc:
|
||||||
|
end
|
||||||
|
|
||||||
|
# Maps to <tt>flex.messaging.messages.ErrorMessage</tt> in AMF3 mode
|
||||||
|
class ErrorMessage < AcknowledgeMessage
|
||||||
|
# Extended data that will facilitate custom error processing on the client
|
||||||
|
attr_accessor :extendedData
|
||||||
|
|
||||||
|
# The fault code for the error, which defaults to the class name of the
|
||||||
|
# causing exception
|
||||||
|
attr_accessor :faultCode
|
||||||
|
|
||||||
|
# Detailed description of what caused the error
|
||||||
|
attr_accessor :faultDetail
|
||||||
|
|
||||||
|
# A simple description of the error
|
||||||
|
attr_accessor :faultString
|
||||||
|
|
||||||
|
# Optional "root cause" of the error
|
||||||
|
attr_accessor :rootCause
|
||||||
|
|
||||||
|
def initialize message=nil, exception=nil
|
||||||
|
super message
|
||||||
|
|
||||||
|
unless exception.nil?
|
||||||
|
@e = exception
|
||||||
|
@faultCode = @e.class.name
|
||||||
|
@faultDetail = @e.backtrace.join("\n")
|
||||||
|
@faultString = @e.message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def encode_amf serializer
|
||||||
|
if serializer.version == 0
|
||||||
|
data = {
|
||||||
|
:faultCode => @faultCode,
|
||||||
|
:faultDetail => @faultDetail,
|
||||||
|
:faultString => @faultString
|
||||||
|
}
|
||||||
|
serializer.write_object(data)
|
||||||
|
else
|
||||||
|
serializer.write_object(self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
13
vendor/gems/RocketAMF-1.0.0/lib/rocketamf/values/typed_hash.rb
vendored
Normal file
13
vendor/gems/RocketAMF-1.0.0/lib/rocketamf/values/typed_hash.rb
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
module RocketAMF
|
||||||
|
module Values #:nodoc:
|
||||||
|
# Hash-like object that can store a type string. Used to preserve type information
|
||||||
|
# for unmapped objects after deserialization.
|
||||||
|
class TypedHash < Hash
|
||||||
|
attr_reader :type
|
||||||
|
|
||||||
|
def initialize type
|
||||||
|
@type = type
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
110
vendor/gems/RocketAMF-1.0.0/spec/class_mapping_spec.rb
vendored
Normal file
110
vendor/gems/RocketAMF-1.0.0/spec/class_mapping_spec.rb
vendored
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
require "spec_helper.rb"
|
||||||
|
|
||||||
|
describe RocketAMF::ClassMapping do
|
||||||
|
before :each do
|
||||||
|
RocketAMF::ClassMapping.reset
|
||||||
|
RocketAMF::ClassMapping.define do |m|
|
||||||
|
m.map :as => 'ASClass', :ruby => 'ClassMappingTest'
|
||||||
|
end
|
||||||
|
@mapper = RocketAMF::ClassMapping.new
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "class name mapping" do
|
||||||
|
it "should allow resetting of mappings back to defaults" do
|
||||||
|
@mapper.get_as_class_name('ClassMappingTest').should_not be_nil
|
||||||
|
RocketAMF::ClassMapping.reset
|
||||||
|
@mapper = RocketAMF::ClassMapping.new
|
||||||
|
@mapper.get_as_class_name('ClassMappingTest').should be_nil
|
||||||
|
@mapper.get_as_class_name('RocketAMF::Values::AcknowledgeMessage').should_not be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return AS class name for ruby objects" do
|
||||||
|
@mapper.get_as_class_name(ClassMappingTest.new).should == 'ASClass'
|
||||||
|
@mapper.get_as_class_name('ClassMappingTest').should == 'ASClass'
|
||||||
|
@mapper.get_as_class_name(RocketAMF::Values::TypedHash.new('ClassMappingTest')).should == 'ASClass'
|
||||||
|
@mapper.get_as_class_name('BadClass').should be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should instantiate a ruby class" do
|
||||||
|
@mapper.get_ruby_obj('ASClass').should be_a(ClassMappingTest)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should properly instantiate namespaced classes" do
|
||||||
|
RocketAMF::ClassMapping.mappings.map :as => 'ASClass', :ruby => 'ANamespace::TestRubyClass'
|
||||||
|
@mapper = RocketAMF::ClassMapping.new
|
||||||
|
@mapper.get_ruby_obj('ASClass').should be_a(ANamespace::TestRubyClass)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return a hash with original type if not mapped" do
|
||||||
|
obj = @mapper.get_ruby_obj('UnmappedClass')
|
||||||
|
obj.should be_a(RocketAMF::Values::TypedHash)
|
||||||
|
obj.type.should == 'UnmappedClass'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should map special classes from AS by default" do
|
||||||
|
as_classes = [
|
||||||
|
'flex.messaging.messages.AcknowledgeMessage',
|
||||||
|
'flex.messaging.messages.CommandMessage',
|
||||||
|
'flex.messaging.messages.RemotingMessage'
|
||||||
|
]
|
||||||
|
|
||||||
|
as_classes.each do |as_class|
|
||||||
|
@mapper.get_ruby_obj(as_class).should_not be_a(RocketAMF::Values::TypedHash)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should map special classes from ruby by default" do
|
||||||
|
ruby_classes = [
|
||||||
|
'RocketAMF::Values::AcknowledgeMessage',
|
||||||
|
'RocketAMF::Values::ErrorMessage'
|
||||||
|
]
|
||||||
|
|
||||||
|
ruby_classes.each do |obj|
|
||||||
|
@mapper.get_as_class_name(obj).should_not be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should allow config modification" do
|
||||||
|
RocketAMF::ClassMapping.mappings.map :as => 'SecondClass', :ruby => 'ClassMappingTest'
|
||||||
|
@mapper = RocketAMF::ClassMapping.new
|
||||||
|
@mapper.get_as_class_name(ClassMappingTest.new).should == 'SecondClass'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "ruby object populator" do
|
||||||
|
it "should populate a ruby class" do
|
||||||
|
obj = @mapper.populate_ruby_obj ClassMappingTest.new, {:prop_a => 'Data'}
|
||||||
|
obj.prop_a.should == 'Data'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should populate a typed hash" do
|
||||||
|
obj = @mapper.populate_ruby_obj RocketAMF::Values::TypedHash.new('UnmappedClass'), {:prop_a => 'Data'}
|
||||||
|
obj[:prop_a].should == 'Data'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "property extractor" do
|
||||||
|
it "should extract hash properties" do
|
||||||
|
hash = {:a => 'test1', 'b' => 'test2'}
|
||||||
|
props = @mapper.props_for_serialization(hash)
|
||||||
|
props.should == {'a' => 'test1', 'b' => 'test2'}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should extract object properties" do
|
||||||
|
obj = ClassMappingTest.new
|
||||||
|
obj.prop_a = 'Test A'
|
||||||
|
|
||||||
|
hash = @mapper.props_for_serialization obj
|
||||||
|
hash.should == {'prop_a' => 'Test A', 'prop_b' => nil}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should extract inherited object properties" do
|
||||||
|
obj = ClassMappingTest2.new
|
||||||
|
obj.prop_a = 'Test A'
|
||||||
|
obj.prop_c = 'Test C'
|
||||||
|
|
||||||
|
hash = @mapper.props_for_serialization obj
|
||||||
|
hash.should == {'prop_a' => 'Test A', 'prop_b' => nil, 'prop_c' => 'Test C'}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
455
vendor/gems/RocketAMF-1.0.0/spec/deserializer_spec.rb
vendored
Normal file
455
vendor/gems/RocketAMF-1.0.0/spec/deserializer_spec.rb
vendored
Normal file
|
|
@ -0,0 +1,455 @@
|
||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
require "spec_helper.rb"
|
||||||
|
|
||||||
|
describe "when deserializing" do
|
||||||
|
before :each do
|
||||||
|
RocketAMF::ClassMapper.reset
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should raise exception with invalid version number" do
|
||||||
|
lambda {
|
||||||
|
RocketAMF.deserialize("", 5)
|
||||||
|
}.should raise_error("unsupported version 5")
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "AMF0" do
|
||||||
|
it "should update source pos if source is a StringIO object" do
|
||||||
|
input = StringIO.new(object_fixture('amf0-number.bin'))
|
||||||
|
input.pos.should == 0
|
||||||
|
output = RocketAMF.deserialize(input, 0)
|
||||||
|
input.pos.should == 9
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize numbers" do
|
||||||
|
input = object_fixture('amf0-number.bin')
|
||||||
|
output = RocketAMF.deserialize(input, 0)
|
||||||
|
output.should == 3.5
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize booleans" do
|
||||||
|
input = object_fixture('amf0-boolean.bin')
|
||||||
|
output = RocketAMF.deserialize(input, 0)
|
||||||
|
output.should === true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize UTF8 strings" do
|
||||||
|
input = object_fixture('amf0-string.bin')
|
||||||
|
output = RocketAMF.deserialize(input, 0)
|
||||||
|
output.should == "this is a テスト"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize nulls" do
|
||||||
|
input = object_fixture('amf0-null.bin')
|
||||||
|
output = RocketAMF.deserialize(input, 0)
|
||||||
|
output.should == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize undefineds" do
|
||||||
|
input = object_fixture('amf0-undefined.bin')
|
||||||
|
output = RocketAMF.deserialize(input, 0)
|
||||||
|
output.should == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize hashes" do
|
||||||
|
input = object_fixture('amf0-hash.bin')
|
||||||
|
output = RocketAMF.deserialize(input, 0)
|
||||||
|
output.should == {'a' => 'b', 'c' => 'd'}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize hashes with empty string keys" do
|
||||||
|
input = object_fixture('amf0-empty-string-key-hash.bin')
|
||||||
|
output = RocketAMF.deserialize(input, 0)
|
||||||
|
output.should == {'a' => 'b', 'c' => 'd', '' => 'last'}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize arrays from flash player" do
|
||||||
|
# Even Array is serialized as a "hash"
|
||||||
|
input = object_fixture('amf0-ecma-ordinal-array.bin')
|
||||||
|
output = RocketAMF.deserialize(input, 0)
|
||||||
|
output.should == {'0' => 'a', '1' => 'b', '2' => 'c', '3' => 'd'}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize strict arrays" do
|
||||||
|
input = object_fixture('amf0-strict-array.bin')
|
||||||
|
output = RocketAMF.deserialize(input, 0)
|
||||||
|
output.should == ['a', 'b', 'c', 'd']
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize dates" do
|
||||||
|
input = object_fixture('amf0-time.bin')
|
||||||
|
output = RocketAMF.deserialize(input, 0)
|
||||||
|
output.should == Time.utc(2003, 2, 13, 5)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize an XML document" do
|
||||||
|
input = object_fixture('amf0-xml-doc.bin')
|
||||||
|
output = RocketAMF.deserialize(input, 0)
|
||||||
|
output.should == '<parent><child prop="test" /></parent>'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize anonymous objects" do
|
||||||
|
input = object_fixture('amf0-object.bin')
|
||||||
|
output = RocketAMF.deserialize(input, 0)
|
||||||
|
output.should == {'foo' => 'baz', 'bar' => 3.14}
|
||||||
|
output.type.should == ""
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize an unmapped object as a dynamic anonymous object" do
|
||||||
|
input = object_fixture("amf0-typed-object.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 0)
|
||||||
|
|
||||||
|
output.type.should == 'org.amf.ASClass'
|
||||||
|
output.should == {'foo' => 'bar', 'baz' => nil}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize a mapped object as a mapped ruby class instance" do
|
||||||
|
RocketAMF::ClassMapper.define {|m| m.map :as => 'org.amf.ASClass', :ruby => 'RubyClass'}
|
||||||
|
|
||||||
|
input = object_fixture("amf0-typed-object.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 0)
|
||||||
|
|
||||||
|
output.should be_a(RubyClass)
|
||||||
|
output.foo.should == 'bar'
|
||||||
|
output.baz.should == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize references properly" do
|
||||||
|
input = object_fixture('amf0-ref-test.bin')
|
||||||
|
output = RocketAMF.deserialize(input, 0)
|
||||||
|
output.length.should == 2
|
||||||
|
output["0"].should === output["1"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "AMF3" do
|
||||||
|
it "should update source pos if source is a StringIO object" do
|
||||||
|
input = StringIO.new(object_fixture('amf3-null.bin'))
|
||||||
|
input.pos.should == 0
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
input.pos.should == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "simple messages" do
|
||||||
|
it "should deserialize a null" do
|
||||||
|
input = object_fixture("amf3-null.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output.should == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize a false" do
|
||||||
|
input = object_fixture("amf3-false.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output.should == false
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize a true" do
|
||||||
|
input = object_fixture("amf3-true.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output.should == true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize integers" do
|
||||||
|
input = object_fixture("amf3-max.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output.should == RocketAMF::MAX_INTEGER
|
||||||
|
|
||||||
|
input = object_fixture("amf3-0.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output.should == 0
|
||||||
|
|
||||||
|
input = object_fixture("amf3-min.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output.should == RocketAMF::MIN_INTEGER
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize large integers" do
|
||||||
|
input = object_fixture("amf3-large-max.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output.should == RocketAMF::MAX_INTEGER + 1
|
||||||
|
|
||||||
|
input = object_fixture("amf3-large-min.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output.should == RocketAMF::MIN_INTEGER - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize BigNums" do
|
||||||
|
input = object_fixture("amf3-bignum.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output.should == 2**1000
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize a simple string" do
|
||||||
|
input = object_fixture("amf3-string.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output.should == "String . String"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize a symbol as a string" do
|
||||||
|
input = object_fixture("amf3-symbol.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output.should == "foo"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize dates" do
|
||||||
|
input = object_fixture("amf3-date.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output.should == Time.at(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize XML" do
|
||||||
|
# XMLDocument tag
|
||||||
|
input = object_fixture("amf3-xml-doc.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output.should == '<parent><child prop="test" /></parent>'
|
||||||
|
|
||||||
|
# XML tag
|
||||||
|
input = object_fixture("amf3-xml.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output.should == '<parent><child prop="test"/></parent>'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "objects" do
|
||||||
|
it "should deserialize an unmapped object as a dynamic anonymous object" do
|
||||||
|
input = object_fixture("amf3-dynamic-object.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
|
||||||
|
expected = {
|
||||||
|
'property_one' => 'foo',
|
||||||
|
'nil_property' => nil,
|
||||||
|
'another_public_property' => 'a_public_value'
|
||||||
|
}
|
||||||
|
output.should == expected
|
||||||
|
output.type.should == ""
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize a mapped object as a mapped ruby class instance" do
|
||||||
|
RocketAMF::ClassMapper.define {|m| m.map :as => 'org.amf.ASClass', :ruby => 'RubyClass'}
|
||||||
|
|
||||||
|
input = object_fixture("amf3-typed-object.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
|
||||||
|
output.should be_a(RubyClass)
|
||||||
|
output.foo.should == 'bar'
|
||||||
|
output.baz.should == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize externalizable objects" do
|
||||||
|
RocketAMF::ClassMapper.define {|m| m.map :as => 'ExternalizableTest', :ruby => 'ExternalizableTest'}
|
||||||
|
|
||||||
|
input = object_fixture("amf3-externalizable.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
|
||||||
|
output.length.should == 2
|
||||||
|
output[0].one.should == 5
|
||||||
|
output[1].two.should == 5
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize a hash as a dynamic anonymous object" do
|
||||||
|
input = object_fixture("amf3-hash.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output.should == {'foo' => "bar", 'answer' => 42}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize an empty array" do
|
||||||
|
input = object_fixture("amf3-empty-array.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output.should == []
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize an array of primitives" do
|
||||||
|
input = object_fixture("amf3-primitive-array.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output.should == [1,2,3,4,5]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize an associative array" do
|
||||||
|
input = object_fixture("amf3-associative-array.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output.should == {0=>"bar1", 1=>"bar2", 2=>"bar3", "asdf"=>"fdsa", "foo"=>"bar", "42"=>"bar"}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize an array of mixed objects" do
|
||||||
|
input = object_fixture("amf3-mixed-array.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
|
||||||
|
h1 = {'foo_one' => "bar_one"}
|
||||||
|
h2 = {'foo_two' => ""}
|
||||||
|
so1 = {'foo_three' => 42}
|
||||||
|
output.should == [h1, h2, so1, {}, [h1, h2, so1], [], 42, "", [], "", {}, "bar_one", so1]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize an array collection as an array" do
|
||||||
|
input = object_fixture("amf3-array-collection.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
|
||||||
|
output.class.should == Array
|
||||||
|
output.should == ["foo", "bar"]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize a complex set of array collections" do
|
||||||
|
RocketAMF::ClassMapper.define {|m| m.map :as => 'org.amf.ASClass', :ruby => 'RubyClass'}
|
||||||
|
input = object_fixture('amf3-complex-array-collection.bin')
|
||||||
|
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
|
||||||
|
output[0].should == ["foo", "bar"]
|
||||||
|
output[1][0].should be_a(RubyClass)
|
||||||
|
output[1][1].should be_a(RubyClass)
|
||||||
|
output[2].should === output[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize a byte array" do
|
||||||
|
input = object_fixture("amf3-byte-array.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
|
||||||
|
output.should be_a(StringIO)
|
||||||
|
expected = "\000\003これtest\100"
|
||||||
|
expected.force_encoding("ASCII-8BIT") if expected.respond_to?(:force_encoding)
|
||||||
|
output.string.should == expected
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize an empty dictionary" do
|
||||||
|
input = object_fixture("amf3-empty-dictionary.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output.should == {}
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize a dictionary" do
|
||||||
|
input = object_fixture("amf3-dictionary.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
|
||||||
|
keys = output.keys
|
||||||
|
keys.length.should == 2
|
||||||
|
obj_key, str_key = keys[0].is_a?(RocketAMF::Values::TypedHash) ? [keys[0], keys[1]] : [keys[1], keys[0]]
|
||||||
|
obj_key.type.should == 'org.amf.ASClass'
|
||||||
|
output[obj_key].should == "asdf2"
|
||||||
|
str_key.should == "bar"
|
||||||
|
output[str_key].should == "asdf1"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize Vector.<int>" do
|
||||||
|
input = object_fixture('amf3-vector-int.bin')
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output.should == [4, -20, 12]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize Vector.<uint>" do
|
||||||
|
input = object_fixture('amf3-vector-uint.bin')
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output.should == [4, 20, 12]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize Vector.<Number>" do
|
||||||
|
input = object_fixture('amf3-vector-double.bin')
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output.should == [4.3, -20.6]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize Vector.<Object>" do
|
||||||
|
input = object_fixture('amf3-vector-object.bin')
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output[0]['foo'].should == 'foo'
|
||||||
|
output[1].type.should == 'org.amf.ASClass'
|
||||||
|
output[2]['foo'].should == 'baz'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "and implementing the AMF Spec" do
|
||||||
|
it "should keep references of duplicate strings" do
|
||||||
|
input = object_fixture("amf3-string-ref.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
|
||||||
|
foo = "foo"
|
||||||
|
bar = "str"
|
||||||
|
output.should == [foo, bar, foo, bar, foo, {'str' => "foo"}]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should not reference the empty string" do
|
||||||
|
input = object_fixture("amf3-empty-string-ref.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output.should == ["",""]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should keep references of duplicate dates" do
|
||||||
|
input = object_fixture("amf3-date-ref.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
|
||||||
|
output[0].should == Time.at(0)
|
||||||
|
output[0].should equal(output[1])
|
||||||
|
# Expected object:
|
||||||
|
# [DateTime.parse "1/1/1970", DateTime.parse "1/1/1970"]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should keep reference of duplicate objects" do
|
||||||
|
input = object_fixture("amf3-object-ref.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
|
||||||
|
obj1 = {'foo' => "bar"}
|
||||||
|
obj2 = {'foo' => obj1['foo']}
|
||||||
|
output.should == [[obj1, obj2], "bar", [obj1, obj2]]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should keep reference of duplicate object traits" do
|
||||||
|
RocketAMF::ClassMapper.define {|m| m.map :as => 'org.amf.ASClass', :ruby => 'RubyClass'}
|
||||||
|
|
||||||
|
input = object_fixture("amf3-trait-ref.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
|
||||||
|
output[0].foo.should == "foo"
|
||||||
|
output[1].foo.should == "bar"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should keep references of duplicate arrays" do
|
||||||
|
input = object_fixture("amf3-array-ref.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
|
||||||
|
a = [1,2,3]
|
||||||
|
b = %w{ a b c }
|
||||||
|
output.should == [a, b, a, b]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should not keep references of duplicate empty arrays unless the object_id matches" do
|
||||||
|
input = object_fixture("amf3-empty-array-ref.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
|
||||||
|
a = []
|
||||||
|
b = []
|
||||||
|
output.should == [a,b,a,b]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should keep references of duplicate XML and XMLDocuments" do
|
||||||
|
input = object_fixture("amf3-xml-ref.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output.should == ['<parent><child prop="test"/></parent>', '<parent><child prop="test"/></parent>']
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should keep references of duplicate byte arrays" do
|
||||||
|
input = object_fixture("amf3-byte-array-ref.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
output[0].object_id.should == output[1].object_id
|
||||||
|
output[0].string.should == "ASDF"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should deserialize a deep object graph with circular references" do
|
||||||
|
input = object_fixture("amf3-graph-member.bin")
|
||||||
|
output = RocketAMF.deserialize(input, 3)
|
||||||
|
|
||||||
|
output['children'][0]['parent'].should === output
|
||||||
|
output['parent'].should === nil
|
||||||
|
output['children'].length.should == 2
|
||||||
|
# Expected object:
|
||||||
|
# parent = Hash.new
|
||||||
|
# child1 = Hash.new
|
||||||
|
# child1[:parent] = parent
|
||||||
|
# child1[:children] = []
|
||||||
|
# child2 = Hash.new
|
||||||
|
# child2[:parent] = parent
|
||||||
|
# child2[:children] = []
|
||||||
|
# parent[:parent] = nil
|
||||||
|
# parent[:children] = [child1, child2]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
144
vendor/gems/RocketAMF-1.0.0/spec/fast_class_mapping_spec.rb
vendored
Normal file
144
vendor/gems/RocketAMF-1.0.0/spec/fast_class_mapping_spec.rb
vendored
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
require "spec_helper.rb"
|
||||||
|
|
||||||
|
describe RocketAMF::Ext::FastClassMapping do
|
||||||
|
before :each do
|
||||||
|
RocketAMF::Ext::FastClassMapping.reset
|
||||||
|
RocketAMF::Ext::FastClassMapping.define do |m|
|
||||||
|
m.map :as => 'ASClass', :ruby => 'ClassMappingTest'
|
||||||
|
end
|
||||||
|
@mapper = RocketAMF::Ext::FastClassMapping.new
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "class name mapping" do
|
||||||
|
it "should allow resetting of mappings back to defaults" do
|
||||||
|
@mapper.get_as_class_name('ClassMappingTest').should_not be_nil
|
||||||
|
RocketAMF::Ext::FastClassMapping.reset
|
||||||
|
@mapper = RocketAMF::Ext::FastClassMapping.new
|
||||||
|
@mapper.get_as_class_name('ClassMappingTest').should be_nil
|
||||||
|
@mapper.get_as_class_name('RocketAMF::Values::AcknowledgeMessage').should_not be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return AS class name for ruby objects" do
|
||||||
|
@mapper.get_as_class_name(ClassMappingTest.new).should == 'ASClass'
|
||||||
|
@mapper.get_as_class_name('ClassMappingTest').should == 'ASClass'
|
||||||
|
@mapper.get_as_class_name(RocketAMF::Values::TypedHash.new('ClassMappingTest')).should == 'ASClass'
|
||||||
|
@mapper.get_as_class_name('BadClass').should be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should instantiate a ruby class" do
|
||||||
|
@mapper.get_ruby_obj('ASClass').should be_a(ClassMappingTest)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should properly instantiate namespaced classes" do
|
||||||
|
RocketAMF::Ext::FastClassMapping.mappings.map :as => 'ASClass', :ruby => 'ANamespace::TestRubyClass'
|
||||||
|
@mapper = RocketAMF::Ext::FastClassMapping.new
|
||||||
|
@mapper.get_ruby_obj('ASClass').should be_a(ANamespace::TestRubyClass)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return a hash with original type if not mapped" do
|
||||||
|
obj = @mapper.get_ruby_obj('UnmappedClass')
|
||||||
|
obj.should be_a(RocketAMF::Values::TypedHash)
|
||||||
|
obj.type.should == 'UnmappedClass'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should map special classes from AS by default" do
|
||||||
|
as_classes = [
|
||||||
|
'flex.messaging.messages.AcknowledgeMessage',
|
||||||
|
'flex.messaging.messages.CommandMessage',
|
||||||
|
'flex.messaging.messages.RemotingMessage'
|
||||||
|
]
|
||||||
|
|
||||||
|
as_classes.each do |as_class|
|
||||||
|
@mapper.get_ruby_obj(as_class).should_not be_a(RocketAMF::Values::TypedHash)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should map special classes from ruby by default" do
|
||||||
|
ruby_classes = [
|
||||||
|
'RocketAMF::Values::AcknowledgeMessage',
|
||||||
|
'RocketAMF::Values::ErrorMessage'
|
||||||
|
]
|
||||||
|
|
||||||
|
ruby_classes.each do |obj|
|
||||||
|
@mapper.get_as_class_name(obj).should_not be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should allow config modification" do
|
||||||
|
RocketAMF::Ext::FastClassMapping.mappings.map :as => 'SecondClass', :ruby => 'ClassMappingTest'
|
||||||
|
@mapper = RocketAMF::Ext::FastClassMapping.new
|
||||||
|
@mapper.get_as_class_name(ClassMappingTest.new).should == 'SecondClass'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "ruby object populator" do
|
||||||
|
it "should populate a ruby class" do
|
||||||
|
obj = @mapper.populate_ruby_obj ClassMappingTest.new, {:prop_a => 'Data'}
|
||||||
|
obj.prop_a.should == 'Data'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should populate a typed hash" do
|
||||||
|
obj = @mapper.populate_ruby_obj RocketAMF::Values::TypedHash.new('UnmappedClass'), {'prop_a' => 'Data'}
|
||||||
|
obj['prop_a'].should == 'Data'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "property extractor" do
|
||||||
|
# Use symbol keys for properties in Ruby >1.9
|
||||||
|
def prop_hash hash
|
||||||
|
out = {}
|
||||||
|
if RUBY_VERSION =~ /^1\.8/
|
||||||
|
hash.each {|k,v| out[k.to_s] = v}
|
||||||
|
else
|
||||||
|
hash.each {|k,v| out[k.to_sym] = v}
|
||||||
|
end
|
||||||
|
out
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return hash without modification" do
|
||||||
|
hash = {:a => 'test1', 'b' => 'test2'}
|
||||||
|
props = @mapper.props_for_serialization(hash)
|
||||||
|
props.should === hash
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should extract object properties" do
|
||||||
|
obj = ClassMappingTest.new
|
||||||
|
obj.prop_a = 'Test A'
|
||||||
|
|
||||||
|
hash = @mapper.props_for_serialization obj
|
||||||
|
hash.should == prop_hash({'prop_a' => 'Test A', 'prop_b' => nil})
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should extract inherited object properties" do
|
||||||
|
obj = ClassMappingTest2.new
|
||||||
|
obj.prop_a = 'Test A'
|
||||||
|
obj.prop_c = 'Test C'
|
||||||
|
|
||||||
|
hash = @mapper.props_for_serialization obj
|
||||||
|
hash.should == prop_hash({'prop_a' => 'Test A', 'prop_b' => nil, 'prop_c' => 'Test C'})
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should cache property lookups by instance" do
|
||||||
|
class ClassMappingTest3; attr_accessor :prop_a; end;
|
||||||
|
|
||||||
|
# Cache properties
|
||||||
|
obj = ClassMappingTest3.new
|
||||||
|
hash = @mapper.props_for_serialization obj
|
||||||
|
|
||||||
|
# Add a method to ClassMappingTest3
|
||||||
|
class ClassMappingTest3; attr_accessor :prop_b; end;
|
||||||
|
|
||||||
|
# Test property list does not have new property
|
||||||
|
obj = ClassMappingTest3.new
|
||||||
|
obj.prop_a = 'Test A'
|
||||||
|
obj.prop_b = 'Test B'
|
||||||
|
hash = @mapper.props_for_serialization obj
|
||||||
|
hash.should == prop_hash({'prop_a' => 'Test A'})
|
||||||
|
|
||||||
|
# Test that new class mapper *does* have new property (cache per instance)
|
||||||
|
@mapper = RocketAMF::Ext::FastClassMapping.new
|
||||||
|
hash = @mapper.props_for_serialization obj
|
||||||
|
hash.should == prop_hash({'prop_a' => 'Test A', 'prop_b' => 'Test B'})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-boolean.bin
vendored
Normal file
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-boolean.bin
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
|
||||||
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-complex-encoded-string.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-complex-encoded-string.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-date.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-date.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-ecma-ordinal-array.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-ecma-ordinal-array.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-empty-string-key-hash.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-empty-string-key-hash.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-hash.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-hash.bin
vendored
Normal file
Binary file not shown.
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-null.bin
vendored
Normal file
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-null.bin
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
|
||||||
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-number.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-number.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-object.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-object.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-ref-test.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-ref-test.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-strict-array.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-strict-array.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-string.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-string.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-time.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-time.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-typed-object.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-typed-object.bin
vendored
Normal file
Binary file not shown.
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-undefined.bin
vendored
Normal file
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-undefined.bin
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
|
||||||
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-untyped-object.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-untyped-object.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-xml-doc.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf0-xml-doc.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-0.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-0.bin
vendored
Normal file
Binary file not shown.
2
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-array-collection.bin
vendored
Normal file
2
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-array-collection.bin
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
Cflex.messaging.io.ArrayCollection foobar
|
||||||
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-array-ref.bin
vendored
Normal file
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-array-ref.bin
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
abc
|
||||||
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-associative-array.bin
vendored
Normal file
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-associative-array.bin
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
asdf fdsafoobar42 bar1 bar2 bar3
|
||||||
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-bigNum.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-bigNum.bin
vendored
Normal file
Binary file not shown.
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-byte-array-ref.bin
vendored
Normal file
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-byte-array-ref.bin
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
ASDF
|
||||||
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-byte-array.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-byte-array.bin
vendored
Normal file
Binary file not shown.
6
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-complex-array-collection.bin
vendored
Normal file
6
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-complex-array-collection.bin
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
Cflex.messaging.io.ArrayCollection foobar
|
||||||
|
|
||||||
|
#org.amf.ASClassbaz
|
||||||
|
asdf
|
||||||
|
|
||||||
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-complex-encoded-string-array.bin
vendored
Normal file
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-complex-encoded-string-array.bin
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Shift テストUTF テスト
|
||||||
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-date-ref.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-date-ref.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-date.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-date.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-dictionary.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-dictionary.bin
vendored
Normal file
Binary file not shown.
2
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-dynamic-object.bin
vendored
Normal file
2
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-dynamic-object.bin
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
/another_public_propertya_public_valuenil_propertyproperty_onefoo
|
||||||
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-empty-array-ref.bin
vendored
Normal file
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-empty-array-ref.bin
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
|
||||||
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-empty-array.bin
vendored
Normal file
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-empty-array.bin
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
|
||||||
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-empty-dictionary.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-empty-dictionary.bin
vendored
Normal file
Binary file not shown.
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-empty-string-ref.bin
vendored
Normal file
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-empty-string-ref.bin
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
|
||||||
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-encoded-string-ref.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-encoded-string-ref.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-externalizable.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-externalizable.bin
vendored
Normal file
Binary file not shown.
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-false.bin
vendored
Normal file
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-false.bin
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
|
||||||
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-float.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-float.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-graph-member.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-graph-member.bin
vendored
Normal file
Binary file not shown.
2
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-hash.bin
vendored
Normal file
2
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-hash.bin
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
answer*foobar
|
||||||
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-large-max.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-large-max.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-large-min.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-large-min.bin
vendored
Normal file
Binary file not shown.
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-max.bin
vendored
Normal file
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-max.bin
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<04><><EFBFBD><EFBFBD>
|
||||||
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-min.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-min.bin
vendored
Normal file
Binary file not shown.
10
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-mixed-array.bin
vendored
Normal file
10
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-mixed-array.bin
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
foo_onebar_one
|
||||||
|
foo_two
|
||||||
|
foo_three*
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*
|
||||||
|
|
||||||
|
|
||||||
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-null.bin
vendored
Normal file
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-null.bin
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
|
||||||
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-object-ref.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-object-ref.bin
vendored
Normal file
Binary file not shown.
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-primitive-array.bin
vendored
Normal file
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-primitive-array.bin
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
|
||||||
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-string-ref.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-string-ref.bin
vendored
Normal file
Binary file not shown.
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-string.bin
vendored
Normal file
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-string.bin
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
String . String
|
||||||
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-symbol.bin
vendored
Normal file
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-symbol.bin
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
foo
|
||||||
3
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-trait-ref.bin
vendored
Normal file
3
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-trait-ref.bin
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
|
||||||
|
#org.amf.ASClassbazfoo
|
||||||
|
bar
|
||||||
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-true.bin
vendored
Normal file
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-true.bin
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
|
||||||
2
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-typed-object.bin
vendored
Normal file
2
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-typed-object.bin
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
#org.amf.ASClassbazfoobar
|
||||||
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-vector-double.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-vector-double.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-vector-int.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-vector-int.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-vector-object.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-vector-object.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-vector-uint.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-vector-uint.bin
vendored
Normal file
Binary file not shown.
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-xml-doc.bin
vendored
Normal file
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-xml-doc.bin
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
M<parent><child prop="test" /></parent>
|
||||||
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-xml-ref.bin
vendored
Normal file
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-xml-ref.bin
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
K<parent><child prop="test"/></parent>
|
||||||
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-xml.bin
vendored
Normal file
1
vendor/gems/RocketAMF-1.0.0/spec/fixtures/objects/amf3-xml.bin
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
K<parent><child prop="test"/></parent>
|
||||||
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/request/acknowledge-response.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/request/acknowledge-response.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/request/amf0-error-response.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/request/amf0-error-response.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/request/blaze-response.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/request/blaze-response.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/request/commandMessage.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/request/commandMessage.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/request/flex-request.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/request/flex-request.bin
vendored
Normal file
Binary file not shown.
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/request/multiple-simple-request.bin
vendored
Normal file
BIN
vendor/gems/RocketAMF-1.0.0/spec/fixtures/request/multiple-simple-request.bin
vendored
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue