Prefer rb_funcall2() over rb_funcall().
[ruby-vorbistagger.git] / ext / comments.c
index a2569866a62766062a292d9719f0233fd75a1185..6e6efd58460b787f541c82a1cbda9ccda9b5a7b9 100644 (file)
  */
 
 #include <ruby.h>
+#include <st.h>
 #include <stdbool.h>
 #include <ctype.h>
 #include <assert.h>
 
 #include "vcedit.h"
 
-static ID id_casecmp, id_replace, id_compare;
+static ID id_casecmp, id_replace, id_each, id_compare;
 
 void
 comments_init (VALUE self, vcedit_state *state)
@@ -34,20 +35,23 @@ comments_init (VALUE self, vcedit_state *state)
 
        vc = vcedit_comments (state);
 
-       items = rb_ary_new2 (vc->comments);
-       rb_iv_set (self, "items", items);
+       items = rb_iv_get (self, "items");
+       rb_ary_clear (items);
 
        for (i = 0; i < vc->comments; i++) {
                VALUE k, v, pair;
                char *ptr, *content = vc->user_comments[i];
+               int k_len, v_len;
 
                ptr = strchr (content, '=');
                assert (ptr);
 
-               k = rb_str_new (content, ptr - content);
+               k_len = ptr - content;
+               k = rb_str_new (content, k_len);
                OBJ_FREEZE (k);
 
-               v = rb_str_new2 (ptr + 1);
+               v_len = vc->comment_lengths[i] - k_len - 1;
+               v = rb_str_new (ptr + 1, v_len);
 
                pair = rb_ary_new3 (2, k, v);
                OBJ_FREEZE (pair);
@@ -78,6 +82,19 @@ comments_sync (VALUE self, vcedit_state *state)
                                        StringValuePtr (pair->ptr[1]));
        }
 }
+/*
+ * call-seq:
+ *  Ogg::Vorbis::Comments.new -> object
+ *
+ * Creates an Ogg::Vorbis::Comments object.
+ */
+static VALUE
+c_init (VALUE self)
+{
+       rb_iv_set (self, "items", rb_ary_new ());
+
+       return self;
+}
 
 /*
  * call-seq:
@@ -147,7 +164,7 @@ c_delete (VALUE self, VALUE key)
                struct RArray *pair = RARRAY (items->ptr[i]);
                VALUE tmp;
 
-               tmp = rb_funcall (pair->ptr[0], id_casecmp, 1, key);
+               tmp = rb_funcall2 (pair->ptr[0], id_casecmp, 1, &key);
                if (tmp == INT2FIX (0)) {
                        ret = pair->ptr[1];
                        pos = i;
@@ -262,7 +279,7 @@ c_aref (VALUE self, VALUE key)
                struct RArray *pair = RARRAY (items->ptr[i]);
                VALUE tmp;
 
-               tmp = rb_funcall (pair->ptr[0], id_casecmp, 1, key);
+               tmp = rb_funcall2 (pair->ptr[0], id_casecmp, 1, &key);
                if (tmp == INT2FIX (0))
                        return pair->ptr[1];
        }
@@ -289,9 +306,9 @@ c_aset (VALUE self, VALUE key, VALUE value)
                struct RArray *pair = RARRAY (items->ptr[i]);
                VALUE tmp;
 
-               tmp = rb_funcall (pair->ptr[0], id_casecmp, 1, key);
+               tmp = rb_funcall2 (pair->ptr[0], id_casecmp, 1, &key);
                if (tmp == INT2FIX (0)) {
-                       rb_funcall (pair->ptr[1], id_replace, 1, value);
+                       rb_funcall2 (pair->ptr[1], id_replace, 1, &value);
                        return pair->ptr[1];
                }
        }
@@ -323,7 +340,7 @@ c_has_key (VALUE self, VALUE key)
                struct RArray *pair = RARRAY (items->ptr[i]);
                VALUE tmp;
 
-               tmp = rb_funcall (pair->ptr[0], id_casecmp, 1, key);
+               tmp = rb_funcall2 (pair->ptr[0], id_casecmp, 1, &key);
                if (tmp == INT2FIX (0))
                        return Qtrue;
        }
@@ -331,6 +348,52 @@ c_has_key (VALUE self, VALUE key)
        return Qfalse;
 }
 
+static VALUE
+merge_cb (VALUE ar, VALUE self)
+{
+       struct RArray *pair = RARRAY (ar);
+
+       c_aset (self, pair->ptr[0], pair->ptr[1]);
+
+       return Qnil;
+}
+
+/*
+ * call-seq:
+ *  object.merge!(arg) -> object
+ *
+ * Adds the key-value pairs from *arg* to *object*, overwriting existing
+ * values if a key already existed in *object*.
+ *
+ * Note that *arg*'s each method needs to yield key-value pairs for this
+ * to work. This means that e.g. hashes and Ogg::Vorbis::Comments objects
+ * are supported as arguments.
+ */
+static VALUE
+c_merge (VALUE self, VALUE arg)
+{
+       if (!rb_respond_to (arg, id_each))
+               rb_raise (rb_eArgError, "invalid argument");
+
+       rb_iterate (rb_each, arg, merge_cb, self);
+
+       return self;
+}
+
+/*
+ * call-seq:
+ *  object.shift(hash) -> array or nil
+ *
+ * Removes the first key-value pair from *object* and returns it
+ * as the two-item array [key, value].
+ * If *object* is empty, +nil+ is returned.
+ */
+static VALUE
+c_shift (VALUE self)
+{
+       return rb_ary_shift (rb_iv_get (self, "items"));
+}
+
 /*
  * call-seq:
  *  object <=> other -> -1, 0 or 1
@@ -363,7 +426,7 @@ c_compare (VALUE self, VALUE other)
                for (j = 0; j < 2; j++) {
                        VALUE tmp;
 
-                       tmp = rb_funcall (aa->ptr[j], id_compare, 1, bb->ptr[j]);
+                       tmp = rb_funcall2 (aa->ptr[j], id_compare, 1, &bb->ptr[j]);
                        if (FIX2INT (tmp) != 0)
                                return tmp;
                }
@@ -453,6 +516,7 @@ Init_Comments (VALUE mVorbis)
 
        c = rb_define_class_under (mVorbis, "Comments", rb_cObject);
 
+       rb_define_method (c, "initialize", c_init, 0);
        rb_define_method (c, "inspect", c_inspect, 0);
        rb_define_method (c, "clear", c_clear, 0);
        rb_define_method (c, "delete", c_delete, 1);
@@ -463,6 +527,8 @@ Init_Comments (VALUE mVorbis)
        rb_define_method (c, "empty?", c_get_empty, 0);
        rb_define_method (c, "keys", c_keys, 0);
        rb_define_method (c, "values", c_values, 0);
+       rb_define_method (c, "merge!", c_merge, 1);
+       rb_define_method (c, "shift", c_shift, 0);
 
        rb_include_module (c, rb_mComparable);
        rb_define_method (c, "<=>", c_compare, 1);
@@ -480,6 +546,7 @@ Init_Comments (VALUE mVorbis)
 
        id_casecmp = rb_intern ("casecmp");
        id_replace = rb_intern ("replace");
+       id_each = rb_intern ("each");
        id_compare = rb_intern ("<=>");
 
        return c;