summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/mruby/inline.h47
-rw-r--r--include/mruby/value.h3
-rw-r--r--mrbgems/default.gembox3
-rw-r--r--mrbgems/mruby-inline-struct/mrbgem.rake5
-rw-r--r--mrbgems/mruby-inline-struct/test/inline.c83
-rw-r--r--mrbgems/mruby-inline-struct/test/inline.rb151
-rw-r--r--src/class.c20
-rw-r--r--src/etc.c1
-rw-r--r--src/kernel.c4
-rw-r--r--src/object.c4
10 files changed, 318 insertions, 3 deletions
diff --git a/include/mruby/inline.h b/include/mruby/inline.h
new file mode 100644
index 000000000..e773aa118
--- /dev/null
+++ b/include/mruby/inline.h
@@ -0,0 +1,47 @@
+/*
+** mruby/inline.h - Inline structures
+**
+** See Copyright Notice in mruby.h
+*/
+
+#ifndef MRUBY_INLINE_H
+#define MRUBY_INLINE_H
+
+#include "common.h"
+#include <string.h>
+
+/**
+ * Inline structures that fit in RVALUE
+ *
+ * They cannot have finalizer, and cannot have instance variables.
+ */
+MRB_BEGIN_DECL
+
+#define INLINE_DATA_SIZE (sizeof(void*) * 3)
+
+struct RInline {
+ MRB_OBJECT_HEADER;
+ char inline_data[INLINE_DATA_SIZE];
+};
+
+#define RINLINE(obj) ((struct RInline*)(mrb_ptr(obj)))
+#define INLINE_PTR(obj) (RINLINE(obj)->inline_data)
+
+MRB_INLINE mrb_int mrb_inline_size()
+{
+ return INLINE_DATA_SIZE;
+}
+
+MRB_INLINE void* mrb_inline_ptr(mrb_value object)
+{
+ return INLINE_PTR(object);
+}
+
+MRB_INLINE void mrb_inline_copy(mrb_value dest, mrb_value src)
+{
+ memcpy(INLINE_PTR(dest), INLINE_PTR(src), INLINE_DATA_SIZE);
+}
+
+MRB_END_DECL
+
+#endif /* MRUBY_INLINE_H */
diff --git a/include/mruby/value.h b/include/mruby/value.h
index 4330b9441..eb3f931e1 100644
--- a/include/mruby/value.h
+++ b/include/mruby/value.h
@@ -116,7 +116,8 @@ enum mrb_vtype {
MRB_TT_ENV, /* 20 */
MRB_TT_DATA, /* 21 */
MRB_TT_FIBER, /* 22 */
- MRB_TT_MAXDEFINE /* 23 */
+ MRB_TT_INLINE, /* 23 */
+ MRB_TT_MAXDEFINE /* 24 */
};
#include <mruby/object.h>
diff --git a/mrbgems/default.gembox b/mrbgems/default.gembox
index dab7230aa..e3bd114f3 100644
--- a/mrbgems/default.gembox
+++ b/mrbgems/default.gembox
@@ -74,6 +74,9 @@ MRuby::GemBox.new do |conf|
# Use class/module extension
conf.gem :core => "mruby-class-ext"
+ # Use inline struct
+ conf.gem :core => "mruby-inline-struct"
+
# Use mruby-compiler to build other mrbgems
conf.gem :core => "mruby-compiler"
end
diff --git a/mrbgems/mruby-inline-struct/mrbgem.rake b/mrbgems/mruby-inline-struct/mrbgem.rake
new file mode 100644
index 000000000..91ad9f44b
--- /dev/null
+++ b/mrbgems/mruby-inline-struct/mrbgem.rake
@@ -0,0 +1,5 @@
+MRuby::Gem::Specification.new('mruby-inline-struct') do |spec|
+ spec.license = 'MIT'
+ spec.author = 'mruby developers'
+ spec.summary = 'inline structure'
+end
diff --git a/mrbgems/mruby-inline-struct/test/inline.c b/mrbgems/mruby-inline-struct/test/inline.c
new file mode 100644
index 000000000..903c08aca
--- /dev/null
+++ b/mrbgems/mruby-inline-struct/test/inline.c
@@ -0,0 +1,83 @@
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/string.h>
+#include <mruby/inline.h>
+
+static mrb_value
+inline_test_initialize(mrb_state *mrb, mrb_value self)
+{
+ char *string = mrb_inline_ptr(self);
+ mrb_int size = mrb_inline_size();
+ mrb_value object;
+ mrb_get_args(mrb, "o", &object);
+
+ if (mrb_float_p(object))
+ {
+ snprintf(string, size, "float(%.3f)", mrb_float(object));
+ }
+ else if (mrb_fixnum_p(object))
+ {
+ snprintf(string, size, "fixnum(%d)", mrb_fixnum(object));
+ }
+ else if (mrb_string_p(object))
+ {
+ snprintf(string, size, "string(%s)", mrb_string_value_cstr(mrb, &object));
+ }
+
+ string[size - 1] = 0; // force NULL at the end
+ return self;
+}
+
+static mrb_value
+inline_test_to_s(mrb_state *mrb, mrb_value self)
+{
+ return mrb_str_new_cstr(mrb, mrb_inline_ptr(self));
+}
+
+static mrb_value
+inline_test_length(mrb_state *mrb, mrb_value self)
+{
+ return mrb_fixnum_value(mrb_inline_size());
+}
+
+static mrb_value
+inline_test_test_receive(mrb_state *mrb, mrb_value self)
+{
+ mrb_value object;
+ mrb_get_args(mrb, "o", &object);
+ if (mrb_obj_class(mrb, object) != mrb_class_get(mrb, "InlineStructTest"))
+ {
+ mrb_raisef(mrb, E_TYPE_ERROR, "Expected InlineStructTest");
+ }
+ return mrb_bool_value(((char*)mrb_inline_ptr(object))[0] == 's');
+}
+
+static mrb_value
+inline_test_test_receive_direct(mrb_state *mrb, mrb_value self)
+{
+ char *ptr;
+ mrb_get_args(mrb, "I", &ptr);
+ return mrb_bool_value(ptr[0] == 's');
+}
+
+static mrb_value
+inline_test_mutate(mrb_state *mrb, mrb_value self)
+{
+ char *ptr = mrb_inline_ptr(self);
+ memcpy(ptr, "mutate", 6);
+ return mrb_nil_value();
+}
+
+void mrb_mruby_inline_struct_gem_test(mrb_state *mrb)
+{
+ struct RClass *cls;
+
+ cls = mrb_define_class(mrb, "InlineStructTest", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cls, MRB_TT_INLINE);
+ mrb_define_method(mrb, cls, "initialize", inline_test_initialize, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, cls, "to_s", inline_test_to_s, MRB_ARGS_NONE());
+ mrb_define_method(mrb, cls, "mutate", inline_test_mutate, MRB_ARGS_NONE());
+ mrb_define_class_method(mrb, cls, "length", inline_test_length, MRB_ARGS_NONE());
+ mrb_define_class_method(mrb, cls, "test_receive", inline_test_test_receive, MRB_ARGS_REQ(1));
+ mrb_define_class_method(mrb, cls, "test_receive_direct", inline_test_test_receive_direct, MRB_ARGS_REQ(1));
+}
diff --git a/mrbgems/mruby-inline-struct/test/inline.rb b/mrbgems/mruby-inline-struct/test/inline.rb
new file mode 100644
index 000000000..495859232
--- /dev/null
+++ b/mrbgems/mruby-inline-struct/test/inline.rb
@@ -0,0 +1,151 @@
+##
+# InlineStruct Test
+
+class InlineStructTest
+ def extra_method
+ :ok
+ end
+
+ def test_ivar_set
+ @var = :ivar
+ end
+
+ def test_ivar_get
+ @vat
+ end
+end
+
+assert('InlineStructTest#dup') do
+ obj = InlineStructTest.new(1)
+ assert_equal obj.to_s, 'fixnum(1)'
+ assert_equal obj.dup.to_s, 'fixnum(1)'
+end
+
+assert('InlineStructTest#clone') do
+ obj = InlineStructTest.new(1)
+ assert_equal obj.to_s, 'fixnum(1)'
+ assert_equal obj.clone.to_s, 'fixnum(1)'
+end
+
+assert('InlineStruct#object_id') do
+ obj1 = InlineStructTest.new(1)
+ obj2 = InlineStructTest.new(1)
+ assert_not_equal obj1, obj2
+ assert_not_equal obj1.object_id, obj2.object_id
+ assert_not_equal obj1.object_id, obj1.dup.object_id
+ assert_not_equal obj1.object_id, obj1.clone.object_id
+end
+
+assert('InlineStructTest#mutate (dup)') do
+ obj1 = InlineStructTest.new("foo")
+ assert_equal obj1.to_s, "string(foo)"
+ obj2 = obj1.dup
+ assert_equal obj2.to_s, "string(foo)"
+ obj1.mutate
+ assert_equal obj1.to_s, "mutate(foo)"
+ assert_equal obj2.to_s, "string(foo)"
+end
+
+assert('InlineStructTest#mutate (clone)') do
+ obj1 = InlineStructTest.new("foo")
+ assert_equal obj1.to_s, "string(foo)"
+ obj2 = obj1.clone
+ assert_equal obj2.to_s, "string(foo)"
+ obj1.mutate
+ assert_equal obj1.to_s, "mutate(foo)"
+ assert_equal obj2.to_s, "string(foo)"
+end
+
+assert('InlineStructTest#test_receive(string)') do
+ assert_equal InlineStructTest.test_receive(InlineStructTest.new('a')), true
+end
+
+assert('InlineStructTest#test_receive(float)') do
+ assert_equal InlineStructTest.test_receive(InlineStructTest.new(1.25)), false
+end
+
+assert('InlineStructTest#test_receive(invalid object)') do
+ assert_raise(TypeError) do
+ InlineStructTest.test_receive([])
+ end
+end
+
+assert('InlineStructTest#test_receive(string)') do
+ assert_equal InlineStructTest.test_receive_direct(InlineStructTest.new('a')), true
+end
+
+assert('InlineStructTest#test_receive(float)') do
+ assert_equal InlineStructTest.test_receive_direct(InlineStructTest.new(1.25)), false
+end
+
+assert('InlineStructTest#test_receive(invalid object)') do
+ assert_raise(TypeError) do
+ InlineStructTest.test_receive_direct([])
+ end
+end
+
+assert('InlineStructTest#extra_method') do
+ assert_equal InlineStructTest.new(1).extra_method, :ok
+end
+
+assert('InlineStructTest instance variable') do
+ obj = InlineStructTest.new(1)
+ assert_raise(ArgumentError) do
+ obj.test_ivar_set
+ end
+ assert_equal obj.test_ivar_get, nil
+end
+
+# 64-bit mode
+if InlineStructTest.length == 24
+ assert('InlineStructTest length [64 bit]') do
+ assert_equal InlineStructTest.length, 3 * 8
+ end
+
+ assert('InlineStructTest w/float [64 bit]') do
+ obj = InlineStructTest.new(1.25)
+ assert_equal obj.to_s, "float(1.250)"
+ end
+
+ assert('InlineStructTest w/fixnum [64 bit]') do
+ obj = InlineStructTest.new(42)
+ assert_equal obj.to_s, "fixnum(42)"
+ end
+
+ assert('InlineStructTest w/string [64 bit]') do
+ obj = InlineStructTest.new("hello")
+ assert_equal obj.to_s, "string(hello)"
+ end
+
+ assert('InlineStructTest w/long string [64 bit]') do
+ obj = InlineStructTest.new("this won't fit in 3 * 8 bytes available for the structure")
+ assert_equal obj.to_s, "string(this won't fit i"
+ end
+end
+
+# 32-bit mode
+if InlineStructTest.length == 12
+ assert('InlineStructTest length [32 bit]') do
+ assert_equal InlineStructTest.length, 3 * 4
+ end
+
+ assert('InlineStructTest w/float [32 bit]') do
+ obj = InlineStructTest.new(1.25)
+ assert_equal obj.to_s, "float(1.250"
+ end
+
+ assert('InlineStructTest w/fixnum [32 bit]') do
+ obj = InlineStructTest.new(42)
+ assert_equal obj.to_s, "fixnum(42)"
+ end
+
+ assert('InlineStructTest w/string [32 bit]') do
+ obj = InlineStructTest.new("hello")
+ assert_equal obj.to_s, "string(hell"
+ end
+
+ assert('InlineStructTest w/long string [32 bit]') do
+ obj = InlineStructTest.new("this won't fit in 3 * 4 bytes available for the structure")
+ assert_equal obj.to_s, "string(this"
+ end
+end
diff --git a/src/class.c b/src/class.c
index 4fc81689b..53354d02a 100644
--- a/src/class.c
+++ b/src/class.c
@@ -14,6 +14,7 @@
#include <mruby/variable.h>
#include <mruby/error.h>
#include <mruby/data.h>
+#include <mruby/inline.h>
KHASH_DEFINE(mt, mrb_sym, struct RProc*, TRUE, kh_int_hash_func, kh_int_hash_equal)
@@ -491,6 +492,7 @@ to_sym(mrb_state *mrb, mrb_value ss)
b: Boolean [mrb_bool]
n: Symbol [mrb_sym]
d: Data [void*,mrb_data_type const] 2nd argument will be used to check data type so it won't be modified
+ I: Inline struct [void*]
&: Block [mrb_value]
*: rest argument [mrb_value*,mrb_int] Receive the rest of the arguments as an array.
|: optional Next argument of '|' and later are optional.
@@ -702,6 +704,24 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
}
}
break;
+ case 'I':
+ {
+ void* *p;
+ mrb_value ss;
+
+ p = va_arg(ap, void**);
+ if (i < argc) {
+ ss = ARGV[arg_i];
+ if (mrb_type(ss) != MRB_TT_INLINE)
+ {
+ mrb_raisef(mrb, E_TYPE_ERROR, "%S is not inline struct", ss);
+ }
+ *p = mrb_inline_ptr(ss);
+ arg_i++;
+ i++;
+ }
+ }
+ break;
case 'f':
{
mrb_float *p;
diff --git a/src/etc.c b/src/etc.c
index 183e2f070..c89549da6 100644
--- a/src/etc.c
+++ b/src/etc.c
@@ -139,6 +139,7 @@ mrb_obj_id(mrb_value obj)
case MRB_TT_EXCEPTION:
case MRB_TT_FILE:
case MRB_TT_DATA:
+ case MRB_TT_INLINE:
default:
return MakeID(mrb_ptr(obj));
}
diff --git a/src/kernel.c b/src/kernel.c
index df237cd46..4a4b6b414 100644
--- a/src/kernel.c
+++ b/src/kernel.c
@@ -11,6 +11,7 @@
#include <mruby/string.h>
#include <mruby/variable.h>
#include <mruby/error.h>
+#include <mruby/inline.h>
typedef enum {
NOEX_PUBLIC = 0x00,
@@ -301,6 +302,9 @@ init_copy(mrb_state *mrb, mrb_value dest, mrb_value obj)
case MRB_TT_EXCEPTION:
mrb_iv_copy(mrb, dest, obj);
break;
+ case MRB_TT_INLINE:
+ mrb_inline_copy(dest, obj);
+ break;
default:
break;
diff --git a/src/object.c b/src/object.c
index bb1a4ebc4..af66d93d0 100644
--- a/src/object.c
+++ b/src/object.c
@@ -348,7 +348,7 @@ mrb_check_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const
{
mrb_value v;
- if (mrb_type(val) == type && type != MRB_TT_DATA) return val;
+ if (mrb_type(val) == type && type != MRB_TT_DATA && type != MRB_TT_INLINE) return val;
v = convert_type(mrb, val, tname, method, FALSE);
if (mrb_nil_p(v) || mrb_type(v) != type) return mrb_nil_value();
return v;
@@ -390,7 +390,7 @@ mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t)
enum mrb_vtype xt;
xt = mrb_type(x);
- if ((xt != t) || (xt == MRB_TT_DATA)) {
+ if ((xt != t) || (xt == MRB_TT_DATA) || (xt == MRB_TT_INLINE)) {
while (type->type < MRB_TT_MAXDEFINE) {
if (type->type == t) {
const char *etype;