summaryrefslogtreecommitdiff
path: root/ruby
diff options
context:
space:
mode:
authorINADA Naoki <songofacandy@gmail.com>2010-09-02 01:30:32 +0900
committerINADA Naoki <songofacandy@gmail.com>2010-09-02 01:30:32 +0900
commit623df2357025a860578db54683aa10ddea7d261d (patch)
tree697b5401dc7ca2108600464ae905aa688ae96fce /ruby
parent4a15d8b6d2b69bdc1de0b0a7f643b02e00100e66 (diff)
parent558e9c21edf3cee5813aaa0e7797509eec5d43fb (diff)
downloadmsgpack-python-623df2357025a860578db54683aa10ddea7d261d.tar.gz
Merge branch 'master' of github.com:msgpack/msgpack
Diffstat (limited to 'ruby')
-rw-r--r--ruby/ChangeLog6
-rw-r--r--ruby/README26
-rw-r--r--ruby/compat.h77
-rw-r--r--ruby/extconf.rb3
-rwxr-xr-xruby/makegem.sh7
-rw-r--r--ruby/msgpack.gemspec7
-rw-r--r--ruby/pack.c156
-rw-r--r--ruby/pack.h2
-rw-r--r--ruby/rbinit.c41
-rw-r--r--ruby/test/test_cases.rb46
-rw-r--r--ruby/test/test_encoding.rb68
-rw-r--r--ruby/test/test_helper.rb7
-rw-r--r--ruby/test/test_pack_unpack.rb (renamed from ruby/msgpack_test.rb)72
-rw-r--r--ruby/unpack.c435
-rw-r--r--ruby/unpack.h2
-rw-r--r--ruby/version.rb3
16 files changed, 851 insertions, 107 deletions
diff --git a/ruby/ChangeLog b/ruby/ChangeLog
index e69de29..d3a7282 100644
--- a/ruby/ChangeLog
+++ b/ruby/ChangeLog
@@ -0,0 +1,6 @@
+
+2010-06-29 version 0.4.3:
+
+ * Adds MessagePack::VERSION constant
+ * Fixes SEGV problem caused by GC bug at MessagePack_Unpacker_mark
+
diff --git a/ruby/README b/ruby/README
index 859ae2b..051a769 100644
--- a/ruby/README
+++ b/ruby/README
@@ -1,29 +1,37 @@
= MessagePack
-
== Description
+MessagePack is a binary-based efficient object serialization library.
+It enables to exchange structured objects between many languages like JSON.
+But unlike JSON, it is very fast and small.
-== Installation
+Simple usage is as follows:
-=== Archive Installation
+ require 'msgpack'
+ msg = [1,2,3].to_msgpack #=> "\x93\x01\x02\x03"
+ MessagePack.unpack(msg) #=> [1,2,3]
- rake install
+Use MessagePack::Unpacker for streaming deserialization.
-=== Gem Installation
- gem install msgpack
+== Installation
+=== Archive Installation
-== Features/Problems
+ ruby extconf.rb
+ make
+ make install
+=== Gem Installation
-== Synopsis
+ gem install msgpack
== Copyright
Author:: frsyuki <frsyuki@users.sourceforge.jp>
-Copyright:: Copyright (c) 2008-2009 frsyuki
+Copyright:: Copyright (c) 2008-2010 FURUHASHI Sadayuki
License:: Apache License, Version 2.0
+
diff --git a/ruby/compat.h b/ruby/compat.h
new file mode 100644
index 0000000..d7a2ca7
--- /dev/null
+++ b/ruby/compat.h
@@ -0,0 +1,77 @@
+/*
+ * MessagePack for Ruby
+ *
+ * Copyright (C) 2008-2010 FURUHASHI Sadayuki
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef COMPAT_H__
+#define COMPAT_H__
+
+
+#ifdef HAVE_RUBY_ENCODING_H
+#include "ruby/encoding.h"
+#define COMPAT_HAVE_ENCODING
+extern int s_enc_utf8;
+extern int s_enc_ascii8bit;
+extern int s_enc_usascii;
+extern VALUE s_enc_utf8_value;
+#endif
+
+#ifdef RUBY_VM
+#define COMPAT_RERAISE rb_exc_raise(rb_errinfo())
+#else
+#define COMPAT_RERAISE rb_exc_raise(ruby_errinfo)
+#endif
+
+
+/* ruby 1.8 and Rubinius */
+#ifndef RBIGNUM_POSITIVE_P
+# ifdef RUBINIUS
+# define RBIGNUM_POSITIVE_P(b) (rb_funcall(b, rb_intern(">="), 1, INT2FIX(0)) == Qtrue)
+# else
+# define RBIGNUM_POSITIVE_P(b) (RBIGNUM(b)->sign)
+# endif
+#endif
+
+
+/* Rubinius */
+#ifdef RUBINIUS
+static inline void rb_gc_enable() { return; }
+static inline void rb_gc_disable() { return; }
+#endif
+
+
+/* ruby 1.8.5 */
+#ifndef RSTRING_PTR
+#define RSTRING_PTR(s) (RSTRING(s)->ptr)
+#endif
+
+/* ruby 1.8.5 */
+#ifndef RSTRING_LEN
+#define RSTRING_LEN(s) (RSTRING(s)->len)
+#endif
+
+/* ruby 1.8.5 */
+#ifndef RARRAY_PTR
+#define RARRAY_PTR(s) (RARRAY(s)->ptr)
+#endif
+
+/* ruby 1.8.5 */
+#ifndef RARRAY_LEN
+#define RARRAY_LEN(s) (RARRAY(s)->len)
+#endif
+
+
+#endif /* compat.h */
+
diff --git a/ruby/extconf.rb b/ruby/extconf.rb
index e6d4bd6..f1d44ec 100644
--- a/ruby/extconf.rb
+++ b/ruby/extconf.rb
@@ -1,4 +1,5 @@
require 'mkmf'
-$CFLAGS << " -I.. -Wall -O4"
+require './version.rb'
+$CFLAGS << %[ -I.. -Wall -O4 -DMESSAGEPACK_VERSION=\\"#{MessagePack::VERSION}\\" -g]
create_makefile('msgpack')
diff --git a/ruby/makegem.sh b/ruby/makegem.sh
index 5ea66f1..bf30cd4 100755
--- a/ruby/makegem.sh
+++ b/ruby/makegem.sh
@@ -8,15 +8,20 @@ cp pack.h ext/
cp rbinit.c ext/
cp unpack.c ext/
cp unpack.h ext/
+cp version.rb ext/
cp ../msgpack/pack_define.h msgpack/
cp ../msgpack/pack_template.h msgpack/
cp ../msgpack/unpack_define.h msgpack/
cp ../msgpack/unpack_template.h msgpack/
cp ../msgpack/sysdep.h msgpack/
-cat msgpack_test.rb | sed "s/require ['\"]msgpack['\"]/require File.dirname(__FILE__) + '\/test_helper.rb'/" > test/msgpack_test.rb
+cp ../test/cases.mpac test/
+cp ../test/cases_compact.mpac test/
+cp ../test/cases.json test/
gem build msgpack.gemspec
+rdoc rbinit.c pack.c unpack.c
+
if [ $? -eq 0 ]; then
rm -rf ext msgpack test/msgpack_test.rb
fi
diff --git a/ruby/msgpack.gemspec b/ruby/msgpack.gemspec
index c5e8c8c..95a2bd0 100644
--- a/ruby/msgpack.gemspec
+++ b/ruby/msgpack.gemspec
@@ -1,14 +1,15 @@
+require './version.rb'
Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.name = "msgpack"
- s.version = "0.3.9"
+ s.version = MessagePack::VERSION
s.summary = "MessagePack, a binary-based efficient data interchange format."
s.author = "FURUHASHI Sadayuki"
s.email = "frsyuki@users.sourceforge.jp"
s.homepage = "http://msgpack.sourceforge.net/"
s.rubyforge_project = "msgpack"
- s.has_rdoc = false
- s.extra_rdoc_files = ["README", "ChangeLog", "AUTHORS"]
+ s.has_rdoc = true
+ s.rdoc_options = ["ext"]
s.require_paths = ["lib"]
s.files = Dir["ext/**/*", "msgpack/**/*", "test/**/*"]
s.test_files = Dir["test/test_*.rb"]
diff --git a/ruby/pack.c b/ruby/pack.c
index 387bab6..8ce46aa 100644
--- a/ruby/pack.c
+++ b/ruby/pack.c
@@ -16,6 +16,8 @@
* limitations under the License.
*/
#include "ruby.h"
+#include "compat.h"
+
#include "msgpack/pack_define.h"
static ID s_to_msgpack;
@@ -51,6 +53,15 @@ static ID s_append;
rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)", argc); \
}
+
+/*
+ * Document-method: NilClass#to_msgpack
+ *
+ * call-seq:
+ * nil.to_msgpack(out = '') -> String
+ *
+ * Serializes the nil into raw bytes.
+ */
static VALUE MessagePack_NilClass_to_msgpack(int argc, VALUE *argv, VALUE self)
{
ARG_BUFFER(out, argc, argv);
@@ -58,6 +69,15 @@ static VALUE MessagePack_NilClass_to_msgpack(int argc, VALUE *argv, VALUE self)
return out;
}
+
+/*
+ * Document-method: TrueClass#to_msgpack
+ *
+ * call-seq:
+ * true.to_msgpack(out = '') -> String
+ *
+ * Serializes the true into raw bytes.
+ */
static VALUE MessagePack_TrueClass_to_msgpack(int argc, VALUE *argv, VALUE self)
{
ARG_BUFFER(out, argc, argv);
@@ -65,6 +85,15 @@ static VALUE MessagePack_TrueClass_to_msgpack(int argc, VALUE *argv, VALUE self)
return out;
}
+
+/*
+ * Document-method: FalseClass#to_msgpack
+ *
+ * call-seq:
+ * false.to_msgpack(out = '') -> String
+ *
+ * Serializes false into raw bytes.
+ */
static VALUE MessagePack_FalseClass_to_msgpack(int argc, VALUE *argv, VALUE self)
{
ARG_BUFFER(out, argc, argv);
@@ -73,6 +102,14 @@ static VALUE MessagePack_FalseClass_to_msgpack(int argc, VALUE *argv, VALUE self
}
+/*
+ * Document-method: Fixnum#to_msgpack
+ *
+ * call-seq:
+ * fixnum.to_msgpack(out = '') -> String
+ *
+ * Serializes the Fixnum into raw bytes.
+ */
static VALUE MessagePack_Fixnum_to_msgpack(int argc, VALUE *argv, VALUE self)
{
ARG_BUFFER(out, argc, argv);
@@ -81,22 +118,34 @@ static VALUE MessagePack_Fixnum_to_msgpack(int argc, VALUE *argv, VALUE self)
}
-#ifndef RBIGNUM_SIGN // Ruby 1.8
-#define RBIGNUM_SIGN(b) (RBIGNUM(b)->sign)
-#endif
-
+/*
+ * Document-method: Bignum#to_msgpack
+ *
+ * call-seq:
+ * bignum.to_msgpack(out = '') -> String
+ *
+ * Serializes the Bignum into raw bytes.
+ */
static VALUE MessagePack_Bignum_to_msgpack(int argc, VALUE *argv, VALUE self)
{
ARG_BUFFER(out, argc, argv);
- // FIXME bignum
- if(RBIGNUM_SIGN(self)) { // positive
+ if(RBIGNUM_POSITIVE_P(self)) {
msgpack_pack_uint64(out, rb_big2ull(self));
- } else { // negative
+ } else {
msgpack_pack_int64(out, rb_big2ll(self));
}
return out;
}
+
+/*
+ * Document-method: Float#to_msgpack
+ *
+ * call-seq:
+ * float.to_msgpack(out = '') -> String
+ *
+ * Serializes the Float into raw bytes.
+ */
static VALUE MessagePack_Float_to_msgpack(int argc, VALUE *argv, VALUE self)
{
ARG_BUFFER(out, argc, argv);
@@ -104,28 +153,69 @@ static VALUE MessagePack_Float_to_msgpack(int argc, VALUE *argv, VALUE self)
return out;
}
+
+/*
+ * Document-method: String#to_msgpack
+ *
+ * call-seq:
+ * string.to_msgpack(out = '') -> String
+ *
+ * Serializes the String into raw bytes.
+ */
static VALUE MessagePack_String_to_msgpack(int argc, VALUE *argv, VALUE self)
{
ARG_BUFFER(out, argc, argv);
+#ifdef COMPAT_HAVE_ENCODING
+ int enc = ENCODING_GET(self);
+ if(enc != s_enc_utf8 && enc != s_enc_ascii8bit && enc != s_enc_usascii) {
+ if(!ENC_CODERANGE_ASCIIONLY(self)) {
+ self = rb_str_encode(self, s_enc_utf8_value, 0, Qnil);
+ }
+ }
+#endif
msgpack_pack_raw(out, RSTRING_LEN(self));
msgpack_pack_raw_body(out, RSTRING_PTR(self), RSTRING_LEN(self));
return out;
}
+
+/*
+ * Document-method: Symbol#to_msgpack
+ *
+ * call-seq:
+ * symbol.to_msgpack(out = '') -> String
+ *
+ * Serializes the Symbol into raw bytes.
+ */
static VALUE MessagePack_Symbol_to_msgpack(int argc, VALUE *argv, VALUE self)
{
+#ifdef COMPAT_HAVE_ENCODING
+ return MessagePack_String_to_msgpack(argc, argv, rb_id2str(SYM2ID(self)));
+#else
ARG_BUFFER(out, argc, argv);
const char* name = rb_id2name(SYM2ID(self));
size_t len = strlen(name);
msgpack_pack_raw(out, len);
msgpack_pack_raw_body(out, name, len);
return out;
+#endif
}
+
+/*
+ * Document-method: Array#to_msgpack
+ *
+ * call-seq:
+ * array.to_msgpack(out = '') -> String
+ *
+ * Serializes the Array into raw bytes.
+ * This calls to_msgpack method reflectively for internal elements.
+ */
static VALUE MessagePack_Array_to_msgpack(int argc, VALUE *argv, VALUE self)
{
ARG_BUFFER(out, argc, argv);
- msgpack_pack_array(out, RARRAY_LEN(self));
+ // FIXME check sizeof(long) > sizeof(unsigned int) && RARRAY_LEN(self) > UINT_MAX
+ msgpack_pack_array(out, (unsigned int)RARRAY_LEN(self));
VALUE* p = RARRAY_PTR(self);
VALUE* const pend = p + RARRAY_LEN(self);
for(;p != pend; ++p) {
@@ -134,6 +224,7 @@ static VALUE MessagePack_Array_to_msgpack(int argc, VALUE *argv, VALUE self)
return out;
}
+
#ifndef RHASH_SIZE // Ruby 1.8
#define RHASH_SIZE(h) (RHASH(h)->tbl ? RHASH(h)->tbl->num_entries : 0)
#endif
@@ -146,15 +237,36 @@ static int MessagePack_Hash_to_msgpack_foreach(VALUE key, VALUE value, VALUE out
return ST_CONTINUE;
}
+/*
+ * Document-method: Hash#to_msgpack
+ *
+ * call-seq:
+ * hash.to_msgpack(out = '') -> String
+ *
+ * Serializes the Hash into raw bytes.
+ * This calls to_msgpack method reflectively for internal keys and values.
+ */
static VALUE MessagePack_Hash_to_msgpack(int argc, VALUE *argv, VALUE self)
{
ARG_BUFFER(out, argc, argv);
- msgpack_pack_map(out, RHASH_SIZE(self));
+ // FIXME check sizeof(st_index_t) > sizeof(unsigned int) && RARRAY_LEN(self) > UINT_MAX
+ msgpack_pack_map(out, (unsigned int)RHASH_SIZE(self));
rb_hash_foreach(self, MessagePack_Hash_to_msgpack_foreach, out);
return out;
}
+/**
+ * Document-method: MessagePack.pack
+ *
+ * call-seq:
+ * MessagePack.pack(object, out = '') -> String
+ *
+ * Serializes the object into raw bytes. The encoding of the string is ASCII-8BIT on Ruby 1.9.
+ * This method is same as object.to_msgpack(out = '').
+ *
+ * _out_ is an object that implements *<<* method like String or IO.
+ */
static VALUE MessagePack_pack(int argc, VALUE* argv, VALUE self)
{
VALUE out;
@@ -173,16 +285,22 @@ void Init_msgpack_pack(VALUE mMessagePack)
{
s_to_msgpack = rb_intern("to_msgpack");
s_append = rb_intern("<<");
- rb_define_method_id(rb_cNilClass, s_to_msgpack, MessagePack_NilClass_to_msgpack, -1);
- rb_define_method_id(rb_cTrueClass, s_to_msgpack, MessagePack_TrueClass_to_msgpack, -1);
- rb_define_method_id(rb_cFalseClass, s_to_msgpack, MessagePack_FalseClass_to_msgpack, -1);
- rb_define_method_id(rb_cFixnum, s_to_msgpack, MessagePack_Fixnum_to_msgpack, -1);
- rb_define_method_id(rb_cBignum, s_to_msgpack, MessagePack_Bignum_to_msgpack, -1);
- rb_define_method_id(rb_cFloat, s_to_msgpack, MessagePack_Float_to_msgpack, -1);
- rb_define_method_id(rb_cString, s_to_msgpack, MessagePack_String_to_msgpack, -1);
- rb_define_method_id(rb_cArray, s_to_msgpack, MessagePack_Array_to_msgpack, -1);
- rb_define_method_id(rb_cHash, s_to_msgpack, MessagePack_Hash_to_msgpack, -1);
- rb_define_method_id(rb_cSymbol, s_to_msgpack, MessagePack_Symbol_to_msgpack, -1);
+
+ rb_define_method(rb_cNilClass, "to_msgpack", MessagePack_NilClass_to_msgpack, -1);
+ rb_define_method(rb_cTrueClass, "to_msgpack", MessagePack_TrueClass_to_msgpack, -1);
+ rb_define_method(rb_cFalseClass, "to_msgpack", MessagePack_FalseClass_to_msgpack, -1);
+ rb_define_method(rb_cFixnum, "to_msgpack", MessagePack_Fixnum_to_msgpack, -1);
+ rb_define_method(rb_cBignum, "to_msgpack", MessagePack_Bignum_to_msgpack, -1);
+ rb_define_method(rb_cFloat, "to_msgpack", MessagePack_Float_to_msgpack, -1);
+ rb_define_method(rb_cString, "to_msgpack", MessagePack_String_to_msgpack, -1);
+ rb_define_method(rb_cArray, "to_msgpack", MessagePack_Array_to_msgpack, -1);
+ rb_define_method(rb_cHash, "to_msgpack", MessagePack_Hash_to_msgpack, -1);
+ rb_define_method(rb_cSymbol, "to_msgpack", MessagePack_Symbol_to_msgpack, -1);
+
+ /**
+ * MessagePack module is defined in rbinit.c file.
+ * mMessagePack = rb_define_module("MessagePack");
+ */
rb_define_module_function(mMessagePack, "pack", MessagePack_pack, -1);
}
diff --git a/ruby/pack.h b/ruby/pack.h
index c9b08a4..f162a86 100644
--- a/ruby/pack.h
+++ b/ruby/pack.h
@@ -1,7 +1,7 @@
/*
* MessagePack for Ruby packing routine
*
- * Copyright (C) 2008-2009 FURUHASHI Sadayuki
+ * Copyright (C) 2008-2010 FURUHASHI Sadayuki
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/ruby/rbinit.c b/ruby/rbinit.c
index 80d1d8c..1d1cbc6 100644
--- a/ruby/rbinit.c
+++ b/ruby/rbinit.c
@@ -1,7 +1,7 @@
/*
* MessagePack for Ruby
*
- * Copyright (C) 2008-2009 FURUHASHI Sadayuki
+ * Copyright (C) 2008-2010 FURUHASHI Sadayuki
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,13 +17,50 @@
*/
#include "pack.h"
#include "unpack.h"
+#include "compat.h"
static VALUE mMessagePack;
+#ifdef COMPAT_HAVE_ENCODING
+int s_enc_utf8;
+int s_enc_ascii8bit;
+int s_enc_usascii;
+VALUE s_enc_utf8_value;
+#endif
+
+/**
+ * Document-module: MessagePack
+ *
+ * MessagePack is a binary-based efficient object serialization library.
+ * It enables to exchange structured objects between many languages like JSON.
+ * But unlike JSON, it is very fast and small.
+ *
+ * You can install MessagePack with rubygems.
+ *
+ * gem install msgpack
+ *
+ * Simple usage is as follows:
+ *
+ * require 'msgpack'
+ * msg = [1,2,3].to_msgpack #=> "\x93\x01\x02\x03"
+ * MessagePack.unpack(msg) #=> [1,2,3]
+ *
+ * Use Unpacker class for streaming deserialization.
+ *
+ */
void Init_msgpack(void)
{
mMessagePack = rb_define_module("MessagePack");
+
+ rb_define_const(mMessagePack, "VERSION", rb_str_new2(MESSAGEPACK_VERSION));
+
+#ifdef COMPAT_HAVE_ENCODING
+ s_enc_ascii8bit = rb_ascii8bit_encindex();
+ s_enc_utf8 = rb_utf8_encindex();
+ s_enc_usascii = rb_usascii_encindex();
+ s_enc_utf8_value = rb_enc_from_encoding(rb_utf8_encoding());
+#endif
+
Init_msgpack_unpack(mMessagePack);
Init_msgpack_pack(mMessagePack);
}
-
diff --git a/ruby/test/test_cases.rb b/ruby/test/test_cases.rb
new file mode 100644
index 0000000..bfb752e
--- /dev/null
+++ b/ruby/test/test_cases.rb
@@ -0,0 +1,46 @@
+#!/usr/bin/env ruby
+here = File.dirname(__FILE__)
+require "#{here}/test_helper"
+
+begin
+require 'json'
+rescue LoadError
+require 'rubygems'
+require 'json'
+end
+
+CASES_PATH = "#{here}/cases.mpac"
+CASES_COMPACT_PATH = "#{here}/cases_compact.mpac"
+CASES_JSON_PATH = "#{here}/cases.json"
+
+class MessagePackTestCases < Test::Unit::TestCase
+ def feed_file(path)
+ pac = MessagePack::Unpacker.new
+ pac.feed File.read(path)
+ pac
+ end
+
+ def test_compare_compact
+ pac = feed_file(CASES_PATH)
+ cpac = feed_file(CASES_COMPACT_PATH)
+
+ objs = []; pac.each {| obj| objs << obj }
+ cobjs = []; cpac.each {|cobj| cobjs << cobj }
+
+ objs.zip(cobjs).each {|obj, cobj|
+ assert_equal(obj, cobj)
+ }
+ end
+
+ def test_compare_json
+ pac = feed_file(CASES_PATH)
+
+ objs = []; pac.each {|obj| objs << obj }
+ jobjs = JSON.load File.read(CASES_JSON_PATH)
+
+ objs.zip(jobjs) {|obj, jobj|
+ assert_equal(obj, jobj)
+ }
+ end
+end
+
diff --git a/ruby/test/test_encoding.rb b/ruby/test/test_encoding.rb
new file mode 100644
index 0000000..2cf0767
--- /dev/null
+++ b/ruby/test/test_encoding.rb
@@ -0,0 +1,68 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__)+'/test_helper'
+
+if RUBY_VERSION < "1.9"
+ exit
+end
+
+class MessagePackTestEncoding < Test::Unit::TestCase
+ def self.it(name, &block)
+ define_method("test_#{name}", &block)
+ end
+
+ it "US-ASCII" do
+ check_unpack "abc".force_encoding("US-ASCII")
+ end
+
+ it "UTF-8 ascii" do
+ check_unpack "abc".force_encoding("UTF-8")
+ end
+
+ it "UTF-8 mbstr" do
+ check_unpack "\xE3\x81\x82".force_encoding("UTF-8")
+ end
+
+ it "UTF-8 invalid" do
+ check_unpack "\xD0".force_encoding("UTF-8")
+ end
+
+ it "ASCII-8BIT" do
+ check_unpack "\xD0".force_encoding("ASCII-8BIT")
+ end
+
+ it "EUC-JP" do
+ x = "\xA4\xA2".force_encoding("EUC-JP")
+ check_unpack(x)
+ end
+
+ it "EUC-JP invalid" do
+ begin
+ "\xD0".force_encoding("EUC-JP").to_msgpack
+ assert(false)
+ rescue Encoding::InvalidByteSequenceError
+ assert(true)
+ end
+ end
+
+ private
+ def check_unpack(str)
+ if str.encoding.to_s == "ASCII-8BIT"
+ should_str = str.dup.force_encoding("UTF-8")
+ else
+ should_str = str.encode("UTF-8")
+ end
+
+ raw = str.to_msgpack
+ r = MessagePack.unpack(str.to_msgpack)
+ assert_equal(r.encoding.to_s, "UTF-8")
+ assert_equal(r, should_str.force_encoding("UTF-8"))
+
+ if str.valid_encoding?
+ sym = str.to_sym
+ r = MessagePack.unpack(sym.to_msgpack)
+ assert_equal(r.encoding.to_s, "UTF-8")
+ assert_equal(r, should_str.force_encoding("UTF-8"))
+ end
+ end
+end
+
diff --git a/ruby/test/test_helper.rb b/ruby/test/test_helper.rb
index 6a63489..4def861 100644
--- a/ruby/test/test_helper.rb
+++ b/ruby/test/test_helper.rb
@@ -1,3 +1,10 @@
require 'test/unit'
+begin
+require File.dirname(__FILE__) + '/../msgpack'
+rescue LoadError
require File.dirname(__FILE__) + '/../lib/msgpack'
+end
+if ENV["GC_STRESS"]
+ GC.stress = true
+end
diff --git a/ruby/msgpack_test.rb b/ruby/test/test_pack_unpack.rb
index 8cbb586..545e593 100644
--- a/ruby/msgpack_test.rb
+++ b/ruby/test/test_pack_unpack.rb
@@ -1,8 +1,7 @@
#!/usr/bin/env ruby
-require 'msgpack'
-require 'test/unit'
+require File.dirname(__FILE__)+'/test_helper'
-class MessagePackTestFormat < Test::Unit::TestCase
+class MessagePackTestPackUnpack < Test::Unit::TestCase
def self.it(name, &block)
define_method("test_#{name}", &block)
end
@@ -154,7 +153,8 @@ class MessagePackTestFormat < Test::Unit::TestCase
end
it "{1=>1}" do
- match ({1=>1}), "\x81\x01\x01"
+ obj = {1=>1}
+ match obj, "\x81\x01\x01"
end
it "1.0" do
@@ -166,27 +166,32 @@ class MessagePackTestFormat < Test::Unit::TestCase
end
it "[0, 1, ..., 14]" do
- match (0..14).to_a, "\x9f\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e"
+ obj = (0..14).to_a
+ match obj, "\x9f\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e"
end
it "[0, 1, ..., 15]" do
- match (0..15).to_a, "\xdc\x00\x10\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+ obj = (0..15).to_a
+ match obj, "\xdc\x00\x10\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
end
it "{}" do
- match ({}), "\x80"
+ obj = {}
+ match obj, "\x80"
end
- it "{0=>0, 1=>1, ..., 14=>14}" do
- a = (0..14).to_a;
- match Hash[*a.zip(a).flatten], "\x8f\x05\x05\x0b\x0b\x00\x00\x06\x06\x0c\x0c\x01\x01\x07\x07\x0d\x0d\x02\x02\x08\x08\x0e\x0e\x03\x03\x09\x09\x04\x04\x0a\x0a"
- end
-
- it "{0=>0, 1=>1, ..., 15=>15}" do
- a = (0..15).to_a;
- match Hash[*a.zip(a).flatten], "\xde\x00\x10\x05\x05\x0b\x0b\x00\x00\x06\x06\x0c\x0c\x01\x01\x07\x07\x0d\x0d\x02\x02\x08\x08\x0e\x0e\x03\x03\x09\x09\x0f\x0f\x04\x04\x0a\x0a"
- end
+## FIXME
+# it "{0=>0, 1=>1, ..., 14=>14}" do
+# a = (0..14).to_a;
+# match Hash[*a.zip(a).flatten], "\x8f\x05\x05\x0b\x0b\x00\x00\x06\x06\x0c\x0c\x01\x01\x07\x07\x0d\x0d\x02\x02\x08\x08\x0e\x0e\x03\x03\x09\x09\x04\x04\x0a\x0a"
+# end
+#
+# it "{0=>0, 1=>1, ..., 15=>15}" do
+# a = (0..15).to_a;
+# match Hash[*a.zip(a).flatten], "\xde\x00\x10\x05\x05\x0b\x0b\x00\x00\x06\x06\x0c\x0c\x01\x01\x07\x07\x0d\x0d\x02\x02\x08\x08\x0e\x0e\x03\x03\x09\x09\x0f\x0f\x04\x04\x0a\x0a"
+# end
+## FIXME
# it "fixmap" do
# check_map 1, 0
# check_map 1, (1<<4)-1
@@ -202,6 +207,37 @@ class MessagePackTestFormat < Test::Unit::TestCase
# #check_map 5, (1<<32)-1 # memory error
# end
+ it "buffer" do
+ str = "a"*32*1024*4
+ raw = str.to_msgpack
+ pac = MessagePack::Unpacker.new
+
+ len = 0
+ parsed = false
+
+ n = 655
+ time = raw.size / n
+ time += 1 unless raw.size % n == 0
+ off = 0
+
+ time.times do
+ assert(!parsed)
+
+ fe = raw[off, n]
+ assert(fe.length > 0)
+ off += fe.length
+
+ pac.feed fe
+ pac.each {|obj|
+ assert(!parsed)
+ assert_equal(obj, str)
+ parsed = true
+ }
+ end
+
+ assert(parsed)
+ end
+
it "gc mark" do
obj = [{["a","b"]=>["c","d"]}, ["e","f"], "d"]
num = 4
@@ -244,6 +280,10 @@ class MessagePackTestFormat < Test::Unit::TestCase
assert_equal(parsed, num)
end
+ it "MessagePack::VERSION constant" do
+ p MessagePack::VERSION
+ end
+
private
def check(len, obj)
v = obj.to_msgpack
diff --git a/ruby/unpack.c b/ruby/unpack.c
index 3a95e5a..2d10e75 100644
--- a/ruby/unpack.c
+++ b/ruby/unpack.c
@@ -16,22 +16,24 @@
* limitations under the License.
*/
#include "ruby.h"
+#include "compat.h"
#include "msgpack/unpack_define.h"
static ID s_sysread;
static ID s_readpartial;
-#ifdef HAVE_RUBY_ENCODING_H
-#include "ruby/encoding.h"
-int s_ascii_8bit;
-#endif
+struct unpack_buffer {
+ size_t size;
+ size_t free;
+ char* ptr;
+};
typedef struct {
int finished;
VALUE source;
size_t offset;
- VALUE buffer;
+ struct unpack_buffer buffer;
VALUE stream;
VALUE streambuf;
ID stream_append_method;
@@ -124,7 +126,17 @@ static inline int template_callback_map_item(unpack_user* u, VALUE* c, VALUE k,
#endif
static inline int template_callback_raw(unpack_user* u, const char* b, const char* p, unsigned int l, VALUE* o)
-{ *o = (l <= COW_MIN_SIZE) ? rb_str_new(p, l) : rb_str_substr(u->source, p - b, l); return 0; }
+{
+ if(u->source == Qnil || l <= COW_MIN_SIZE) {
+ *o = rb_str_new(p, l);
+ } else {
+ *o = rb_str_substr(u->source, p - b, l);
+ }
+#ifdef COMPAT_HAVE_ENCODING
+ ENCODING_SET(*o, s_enc_utf8);
+#endif
+ return 0;
+}
#include "msgpack/unpack_template.h"
@@ -147,11 +159,7 @@ static inline int template_callback_raw(unpack_user* u, const char* b, const cha
static VALUE template_execute_rescue(VALUE nouse)
{
rb_gc_enable();
-#ifdef RUBY_VM
- rb_exc_raise(rb_errinfo());
-#else
- rb_exc_raise(ruby_errinfo);
-#endif
+ COMPAT_RERAISE;
}
static VALUE template_execute_do(VALUE argv)
@@ -178,12 +186,6 @@ static int template_execute_wrap(msgpack_unpack_t* mp,
(VALUE)from,
};
-#ifdef HAVE_RUBY_ENCODING_H
- // FIXME encodingをASCII-8BITにする
- int enc_orig = rb_enc_get_index(str);
- rb_enc_set_index(str, s_ascii_8bit);
-#endif
-
// FIXME execute実行中はmp->topが更新されないのでGC markが機能しない
rb_gc_disable();
@@ -192,33 +194,68 @@ static int template_execute_wrap(msgpack_unpack_t* mp,
int ret = (int)rb_rescue(template_execute_do, (VALUE)args,
template_execute_rescue, Qnil);
+ rb_gc_enable();
+
+ return ret;
+}
+
+static int template_execute_wrap_each(msgpack_unpack_t* mp,
+ const char* ptr, size_t dlen, size_t* from)
+{
+ VALUE args[4] = {
+ (VALUE)mp,
+ (VALUE)ptr,
+ (VALUE)dlen,
+ (VALUE)from,
+ };
+
+ // FIXME execute実行中はmp->topが更新されないのでGC markが機能しない
+ rb_gc_disable();
+
mp->user.source = Qnil;
- rb_gc_enable();
+ int ret = (int)rb_rescue(template_execute_do, (VALUE)args,
+ template_execute_rescue, Qnil);
-#ifdef HAVE_RUBY_ENCODING_H
- rb_enc_set_index(str, enc_orig);
-#endif
+ rb_gc_enable();
return ret;
}
static VALUE cUnpacker;
+
+
+/**
+ * Document-module: MessagePack::UnpackerError
+ *
+ */
static VALUE eUnpackError;
+#ifndef MSGPACK_UNPACKER_BUFFER_INIT_SIZE
+#define MSGPACK_UNPACKER_BUFFER_INIT_SIZE (32*1024)
+#endif
+
+#ifndef MSGPACK_UNPACKER_BUFFER_RESERVE_SIZE
+#define MSGPACK_UNPACKER_BUFFER_RESERVE_SIZE (8*1024)
+#endif
+
static void MessagePack_Unpacker_free(void* data)
{
- if(data) { free(data); }
+ if(data) {
+ msgpack_unpack_t* mp = (msgpack_unpack_t*)data;
+ free(mp->user.buffer.ptr);
+ free(mp);
+ }
}
static void MessagePack_Unpacker_mark(msgpack_unpack_t *mp)
{
unsigned int i;
- rb_gc_mark(mp->user.buffer);
rb_gc_mark(mp->user.stream);
rb_gc_mark(mp->user.streambuf);
+ rb_gc_mark_maybe(template_data(mp));
for(i=0; i < mp->top; ++i) {
rb_gc_mark(mp->stack[i].obj);
rb_gc_mark_maybe(mp->stack[i].map_key);
@@ -229,6 +266,17 @@ static VALUE MessagePack_Unpacker_alloc(VALUE klass)
{
VALUE obj;
msgpack_unpack_t* mp = ALLOC_N(msgpack_unpack_t, 1);
+
+ // rb_gc_mark (not _maybe) is used for following member objects.
+ mp->user.stream = Qnil;
+ mp->user.streambuf = Qnil;
+
+ mp->user.finished = 0;
+ mp->user.offset = 0;
+ mp->user.buffer.size = 0;
+ mp->user.buffer.free = 0;
+ mp->user.buffer.ptr = NULL;
+
obj = Data_Wrap_Struct(klass, MessagePack_Unpacker_mark,
MessagePack_Unpacker_free, mp);
return obj;
@@ -243,6 +291,22 @@ static ID append_method_of(VALUE stream)
}
}
+/**
+ * Document-method: MessagePack::Unpacker#initialize
+ *
+ * call-seq:
+ * MessagePack::Unpacker.new(stream = nil)
+ *
+ * Creates instance of MessagePack::Unpacker.
+ *
+ * You can specify a _stream_ for input stream.
+ * It is required to implement *sysread* or *readpartial* method.
+ *
+ * With the input stream, buffers will be feeded into the deserializer automatically.
+ *
+ * Without the input stream, use *feed* method manually. Or you can manage the buffer manually
+ * with *execute*, *finished?*, *data* and *reset* methods.
+ */
static VALUE MessagePack_Unpacker_initialize(int argc, VALUE *argv, VALUE self)
{
VALUE stream;
@@ -259,21 +323,36 @@ static VALUE MessagePack_Unpacker_initialize(int argc, VALUE *argv, VALUE self)
UNPACKER(self, mp);
template_init(mp);
- mp->user.finished = 0;
- mp->user.offset = 0;
- mp->user.buffer = rb_str_new("",0);
mp->user.stream = stream;
- mp->user.streambuf = rb_str_new("",0);
+ mp->user.streambuf = rb_str_buf_new(MSGPACK_UNPACKER_BUFFER_RESERVE_SIZE);
mp->user.stream_append_method = append_method_of(stream);
+
return self;
}
+
+/**
+ * Document-method: MessagePack::Unpacker#stream
+ *
+ * call-seq:
+ * unpacker.stream
+ *
+ * Gets the input stream.
+ */
static VALUE MessagePack_Unpacker_stream_get(VALUE self)
{
UNPACKER(self, mp);
return mp->user.stream;
}
+/**
+ * Document-method: MessagePack::Unpacker#stream=
+ *
+ * call-seq:
+ * unpacker.stream = stream
+ *
+ * Resets the input stream. You can set nil not to use input stream.
+ */
static VALUE MessagePack_Unpacker_stream_set(VALUE self, VALUE val)
{
UNPACKER(self, mp);
@@ -282,14 +361,103 @@ static VALUE MessagePack_Unpacker_stream_set(VALUE self, VALUE val)
return val;
}
+
+static void reserve_buffer(msgpack_unpack_t* mp, size_t require)
+{
+ struct unpack_buffer* buffer = &mp->user.buffer;
+
+ if(buffer->size == 0) {
+ size_t nsize = MSGPACK_UNPACKER_BUFFER_INIT_SIZE;
+ while(nsize < require) {
+ nsize *= 2;
+ }
+ char* tmp = ALLOC_N(char, nsize);
+ buffer->ptr = tmp;
+ buffer->free = nsize;
+ buffer->size = 0;
+ return;
+ }
+
+ if(buffer->size <= mp->user.offset) {
+ /* clear buffer and rewind offset */
+ buffer->free += buffer->size;
+ buffer->size = 0;
+ mp->user.offset = 0;
+ }
+
+ if(require <= buffer->free) {
+ /* enough free space */
+ return;
+ }
+
+ size_t nsize = (buffer->size + buffer->free) * 2;
+
+ if(mp->user.offset <= buffer->size / 2) {
+ /* parsed less than half: realloc only */
+ while(nsize < buffer->size + require) {
+ nsize *= 2;
+ }
+ char* tmp = REALLOC_N(buffer->ptr, char, nsize);
+ buffer->free = nsize - buffer->size;
+ buffer->ptr = tmp;
+
+ } else {
+ /* parsed more than half: realloc and move */
+ size_t not_parsed = buffer->size - mp->user.offset;
+ while(nsize < not_parsed + require) {
+ nsize *= 2;
+ }
+ char* tmp = REALLOC_N(buffer->ptr, char, nsize);
+ memcpy(tmp, tmp + mp->user.offset, not_parsed);
+ buffer->free = nsize - buffer->size;
+ buffer->size = not_parsed;
+ buffer->ptr = tmp;
+ mp->user.offset = 0;
+ }
+}
+
+static inline void feed_buffer(msgpack_unpack_t* mp, const char* ptr, size_t len)
+{
+ struct unpack_buffer* buffer = &mp->user.buffer;
+
+ if(buffer->free < len) {
+ reserve_buffer(mp, len);
+ }
+ memcpy(buffer->ptr + buffer->size, ptr, len);
+ buffer->size += len;
+ buffer->free -= len;
+}
+
+/**
+ * Document-method: MessagePack::Unpacker#feed
+ *
+ * call-seq:
+ * unpacker.feed(data)
+ *
+ * Fills the internal buffer with the specified buffer.
+ */
static VALUE MessagePack_Unpacker_feed(VALUE self, VALUE data)
{
UNPACKER(self, mp);
StringValue(data);
- rb_str_cat(mp->user.buffer, RSTRING_PTR(data), RSTRING_LEN(data));
+ feed_buffer(mp, RSTRING_PTR(data), RSTRING_LEN(data));
return Qnil;
}
+/**
+ * Document-method: MessagePack::Unpacker#fill
+ *
+ * call-seq:
+ * unpacker.fill -> length of read data
+ *
+ * Fills the internal buffer using the input stream.
+ *
+ * If the input stream is not specified, it returns nil.
+ * You can set it on *initialize* or *stream=* methods.
+ *
+ * This methods raises exceptions that _stream.sysread_ or
+ * _stream.readpartial_ method raises.
+ */
static VALUE MessagePack_Unpacker_fill(VALUE self)
{
UNPACKER(self, mp);
@@ -298,21 +466,28 @@ static VALUE MessagePack_Unpacker_fill(VALUE self)
return Qnil;
}
- long len;
- if(RSTRING_LEN(mp->user.buffer) == 0) {
- rb_funcall(mp->user.stream, mp->user.stream_append_method, 2,
- LONG2FIX(64*1024), mp->user.buffer);
- len = RSTRING_LEN(mp->user.buffer);
- } else {
- rb_funcall(mp->user.stream, mp->user.stream_append_method, 2,
- LONG2FIX(64*1024), mp->user.streambuf);
- len = RSTRING_LEN(mp->user.streambuf);
- rb_str_cat(mp->user.buffer, RSTRING_PTR(mp->user.streambuf), RSTRING_LEN(mp->user.streambuf));
- }
+ rb_funcall(mp->user.stream, mp->user.stream_append_method, 2,
+ LONG2FIX(MSGPACK_UNPACKER_BUFFER_RESERVE_SIZE),
+ mp->user.streambuf);
+
+ size_t len = RSTRING_LEN(mp->user.streambuf);
+ feed_buffer(mp, RSTRING_PTR(mp->user.streambuf), len);
return LONG2FIX(len);
}
+
+/**
+ * Document-method: MessagePack::Unpacker#each
+ *
+ * call-seq:
+ * unpacker.each {|object| }
+ *
+ * Deserializes objects repeatedly. This calls *fill* method automatically.
+ *
+ * UnpackError is throw when parse error is occured.
+ * This method raises exceptions that *fill* method raises.
+ */
static VALUE MessagePack_Unpacker_each(VALUE self)
{
UNPACKER(self, mp);
@@ -323,7 +498,7 @@ static VALUE MessagePack_Unpacker_each(VALUE self)
#endif
while(1) {
- if(RSTRING_LEN(mp->user.buffer) <= mp->user.offset) {
+ if(mp->user.buffer.size <= mp->user.offset) {
do_fill:
{
VALUE len = MessagePack_Unpacker_fill(self);
@@ -333,8 +508,9 @@ static VALUE MessagePack_Unpacker_each(VALUE self)
}
}
- ret = template_execute_wrap(mp, mp->user.buffer,
- RSTRING_LEN(mp->user.buffer), &mp->user.offset);
+ ret = template_execute_wrap_each(mp,
+ mp->user.buffer.ptr, mp->user.buffer.size,
+ &mp->user.offset);
if(ret < 0) {
rb_raise(eUnpackError, "parse error.");
@@ -352,6 +528,7 @@ static VALUE MessagePack_Unpacker_each(VALUE self)
return Qnil;
}
+
static inline VALUE MessagePack_unpack_impl(VALUE self, VALUE data, unsigned long dlen)
{
msgpack_unpack_t mp;
@@ -376,12 +553,34 @@ static inline VALUE MessagePack_unpack_impl(VALUE self, VALUE data, unsigned lon
}
}
+/**
+ * Document-method: MessagePack::Unpacker.unpack_limit
+ *
+ * call-seq:
+ * MessagePack::Unpacker.unpack_limit(data, limit) -> object
+ *
+ * Deserializes one object over the specified buffer upto _limit_ bytes.
+ *
+ * UnpackError is throw when parse error is occured, the buffer is insufficient
+ * to deserialize one object or there are extra bytes.
+ */
static VALUE MessagePack_unpack_limit(VALUE self, VALUE data, VALUE limit)
{
CHECK_STRING_TYPE(data);
return MessagePack_unpack_impl(self, data, NUM2ULONG(limit));
}
+/**
+ * Document-method: MessagePack::Unpacker.unpack
+ *
+ * call-seq:
+ * MessagePack::Unpacker.unpack(data) -> object
+ *
+ * Deserializes one object over the specified buffer.
+ *
+ * UnpackError is throw when parse error is occured, the buffer is insufficient
+ * to deserialize one object or there are extra bytes.
+ */
static VALUE MessagePack_unpack(VALUE self, VALUE data)
{
CHECK_STRING_TYPE(data);
@@ -411,7 +610,20 @@ static VALUE MessagePack_Unpacker_execute_impl(VALUE self, VALUE data,
}
}
-/* compat */
+/**
+ * Document-method: MessagePack::Unpacker#execute_limit
+ *
+ * call-seq:
+ * unpacker.execute_limit(data, offset, limit) -> next offset
+ *
+ * Deserializes one object over the specified buffer from _offset_ bytes upto _limit_ bytes.
+ *
+ * This method doesn't use the internal buffer.
+ *
+ * Call *reset()* method before calling this method again.
+ *
+ * UnpackError is throw when parse error is occured.
+ */
static VALUE MessagePack_Unpacker_execute_limit(VALUE self, VALUE data,
VALUE off, VALUE limit)
{
@@ -420,7 +632,24 @@ static VALUE MessagePack_Unpacker_execute_limit(VALUE self, VALUE data,
(size_t)NUM2ULONG(off), (size_t)NUM2ULONG(limit));
}
-/* compat */
+/**
+ * Document-method: MessagePack::Unpacker#execute
+ *
+ * call-seq:
+ * unpacker.execute(data, offset) -> next offset
+ *
+ * Deserializes one object over the specified buffer from _offset_ bytes.
+ *
+ * This method doesn't use the internal buffer.
+ *
+ * Call *reset()* method before calling this method again.
+ *
+ * This returns offset that was parsed to.
+ * Use *finished?* method to check an object is deserialized and call *data*
+ * method if it returns true.
+ *
+ * UnpackError is throw when parse error is occured.
+ */
static VALUE MessagePack_Unpacker_execute(VALUE self, VALUE data, VALUE off)
{
CHECK_STRING_TYPE(data);
@@ -428,7 +657,16 @@ static VALUE MessagePack_Unpacker_execute(VALUE self, VALUE data, VALUE off)
(size_t)NUM2ULONG(off), (size_t)RSTRING_LEN(data));
}
-/* compat */
+/**
+ * Document-method: MessagePack::Unpacker#finished?
+ *
+ * call-seq:
+ * unpacker.finished?
+ *
+ * Returns true if an object is ready to get with data method.
+ *
+ * Use this method with execute method.
+ */
static VALUE MessagePack_Unpacker_finished_p(VALUE self)
{
UNPACKER(self, mp);
@@ -438,14 +676,30 @@ static VALUE MessagePack_Unpacker_finished_p(VALUE self)
return Qfalse;
}
-/* compat */
+/**
+ * Document-method: MessagePack::Unpacker#data
+ *
+ * call-seq:
+ * unpacker.data
+ *
+ * Gets the object deserialized by execute method.
+ *
+ * Use this method with execute method.
+ */
static VALUE MessagePack_Unpacker_data(VALUE self)
{
UNPACKER(self, mp);
return template_data(mp);
}
-/* compat */
+/**
+ * Document-method: MessagePack::Unpacker#reset
+ *
+ * call-seq:
+ * unpacker.reset
+ *
+ * Resets the internal state of the unpacker.
+ */
static VALUE MessagePack_Unpacker_reset(VALUE self)
{
UNPACKER(self, mp);
@@ -460,27 +714,100 @@ void Init_msgpack_unpack(VALUE mMessagePack)
s_sysread = rb_intern("sysread");
s_readpartial = rb_intern("readpartial");
-#ifdef HAVE_RUBY_ENCODING_H
- s_ascii_8bit = rb_enc_find_index("ASCII-8BIT");
-#endif
-
eUnpackError = rb_define_class_under(mMessagePack, "UnpackError", rb_eStandardError);
cUnpacker = rb_define_class_under(mMessagePack, "Unpacker", rb_cObject);
rb_define_alloc_func(cUnpacker, MessagePack_Unpacker_alloc);
+
rb_define_method(cUnpacker, "initialize", MessagePack_Unpacker_initialize, -1);
+
+ /* Buffered API */
rb_define_method(cUnpacker, "feed", MessagePack_Unpacker_feed, 1);
rb_define_method(cUnpacker, "fill", MessagePack_Unpacker_fill, 0);
rb_define_method(cUnpacker, "each", MessagePack_Unpacker_each, 0);
rb_define_method(cUnpacker, "stream", MessagePack_Unpacker_stream_get, 0);
rb_define_method(cUnpacker, "stream=", MessagePack_Unpacker_stream_set, 1);
- rb_define_module_function(mMessagePack, "unpack", MessagePack_unpack, 1);
- rb_define_module_function(mMessagePack, "unpack_limit", MessagePack_unpack_limit, 2);
- /* backward compatibility */
+ /* Unbuffered API */
rb_define_method(cUnpacker, "execute", MessagePack_Unpacker_execute, 2);
rb_define_method(cUnpacker, "execute_limit", MessagePack_Unpacker_execute_limit, 3);
rb_define_method(cUnpacker, "finished?", MessagePack_Unpacker_finished_p, 0);
rb_define_method(cUnpacker, "data", MessagePack_Unpacker_data, 0);
rb_define_method(cUnpacker, "reset", MessagePack_Unpacker_reset, 0);
+
+ /**
+ * MessagePack module is defined in rbinit.c file.
+ * mMessagePack = rb_define_module("MessagePack");
+ */
+ rb_define_module_function(mMessagePack, "unpack", MessagePack_unpack, 1);
+ rb_define_module_function(mMessagePack, "unpack_limit", MessagePack_unpack_limit, 2);
}
+/**
+ * Document-module: MessagePack::Unpacker
+ *
+ * Deserializer class that includes Buffered API and Unbuffered API.
+ *
+ *
+ * Buffered API uses the internal buffer of the Unpacker.
+ * Following code uses Buffered API with an input stream:
+ *
+ * # create an unpacker with input stream.
+ * pac = MessagePack::Unpacker.new(STDIN)
+ *
+ * # deserialize object one after another.
+ * pac.each {|obj|
+ * # ...
+ * }
+ *
+ *
+ * Following code doesn't use the input stream and feeds buffer
+ * manually. This is useful to use special stream or with
+ * event-driven I/O library.
+ *
+ * # create an unpacker without input stream.
+ * pac = MessagePack::Unpacker.new()
+ *
+ * # feed buffer to the internal buffer.
+ * pac.feed(input_bytes)
+ *
+ * # deserialize object one after another.
+ * pac.each {|obj|
+ * # ...
+ * }
+ *
+ *
+ * You can manage the buffer manually with the combination of
+ * *execute*, *finished?*, *data* and *reset* method.
+ *
+ * # create an unpacker.
+ * pac = MessagePack::Unpacker.new()
+ *
+ * # manage buffer and offset manually.
+ * offset = 0
+ * buffer = ''
+ *
+ * # read some data into the buffer.
+ * buffer << [1,2,3].to_msgpack
+ * buffer << [4,5,6].to_msgpack
+ *
+ * while true
+ * offset = pac.execute(buffer, offset)
+ *
+ * if pac.finished?
+ * obj = pac.data
+ *
+ * buffer.slice!(0, offset)
+ * offset = 0
+ * pac.reset
+ *
+ * # do something with the object
+ * # ...
+ *
+ * # repeat execution if there are more data.
+ * next unless buffer.empty?
+ * end
+ *
+ * break
+ * end
+ */
+
diff --git a/ruby/unpack.h b/ruby/unpack.h
index ce2a8de..91d3eb7 100644
--- a/ruby/unpack.h
+++ b/ruby/unpack.h
@@ -1,7 +1,7 @@
/*
* MessagePack for Ruby unpacking routine
*
- * Copyright (C) 2008-2009 FURUHASHI Sadayuki
+ * Copyright (C) 2008-2010 FURUHASHI Sadayuki
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/ruby/version.rb b/ruby/version.rb
new file mode 100644
index 0000000..b156620
--- /dev/null
+++ b/ruby/version.rb
@@ -0,0 +1,3 @@
+module MessagePack
+ VERSION = "0.4.3"
+end