+static VALUE
+stream_serialize (VALUE self)
+{
+ VALUE ret;
+ struct RArray *stream;
+ long i;
+
+ ret = rb_str_new2 ("");
+
+ stream = RARRAY (self);
+ if (!stream->len)
+ return ret;
+
+ for (i = 0; i < stream->len; i++) {
+ VALUE str = rb_funcall (stream->ptr[i], id_serialize, 0, NULL);
+
+ rb_str_append (ret, str);
+ }
+
+ return ret;
+}
+
+static VALUE
+chunk_init (VALUE self, VALUE tag, VALUE data)
+{
+ unsigned long len;
+
+ StringValue (tag);
+ StringValue (data);
+
+ if (rb_funcall (tag, id_include, 1, INT2FIX (0)) == Qtrue) \
+ rb_raise (rb_eArgError, "tag must not contain binary zeroes");
+
+ /* libeet uses a signed 32bit integer to store the
+ * chunk size, so make sure we don't overflow it
+ */
+ len = RSTRING (tag)->len + 1 + RSTRING (data)->len;
+ if (len < 0 || len >= 2147483647L)
+ rb_raise (rb_eArgError, "tag or data too long");
+
+ rb_ivar_set (self, id_tag, rb_str_dup_frozen (tag));
+ rb_ivar_set (self, id_data, rb_str_dup_frozen (data));
+
+ return self;
+}
+
+static VALUE
+chunk_serialize (VALUE self)
+{
+ VALUE tmp, ret;
+ unsigned int size, buf_len;
+ unsigned char *buf;
+ struct RString *tag, *data;
+
+ tmp = rb_ivar_get (self, id_tag);
+ StringValue (tmp);
+ tag = RSTRING (tmp);
+
+ tmp = rb_ivar_get (self, id_data);
+ StringValue (tmp);
+ data = RSTRING (tmp);
+
+ buf_len = 9 + tag->len + data->len;
+ ret = rb_str_buf_new (buf_len);
+
+ buf = RSTRING (ret)->ptr;
+ RSTRING (ret)->len = buf_len;
+
+ memcpy (buf, "CHnK", 4);
+ buf += 4;
+
+ size = tag->len + data->len + 1;
+ size = BSWAP32 (size);
+ memcpy (buf, &size, 4);
+ buf += 4;
+
+ memcpy (buf, tag->ptr, tag->len);
+ buf += tag->len;
+
+ *buf++ = 0;
+
+ memcpy (buf, data->ptr, data->len);
+
+ return ret;
+}
+
+static int
+for_each_prop (VALUE tag, VALUE arg, VALUE stream)
+{
+ VALUE value, type, chunks;
+ long len, i;
+
+ if (rb_obj_is_kind_of (arg, rb_cArray) == Qfalse)
+ rb_raise (ePropError, "hash value is not an array");
+
+ value = rb_ary_entry (arg, 0);
+ if (NIL_P (value))
+ return ST_CONTINUE;
+
+ type = rb_ary_entry (arg, 1);
+ chunks = rb_funcall (value, id_to_eet_chunks, 2, tag, type);
+
+ len = RARRAY (chunks)->len;
+
+ for (i = 0; i < len; i++)
+ rb_funcall (stream, id_push, 1, rb_ary_entry (chunks, i));
+
+ return ST_CONTINUE;
+}
+
+/*
+ * :call-seq:
+ * object.to_eet -> string
+ *
+ * Serializes the receiver to EET format.
+ */
+static VALUE
+c_to_eet (VALUE self)
+{
+ VALUE props, name, stream, chunk, args[2];
+
+ props = rb_funcall (self, id_to_eet_properties, 0);
+
+ if (rb_obj_is_kind_of (props, rb_cHash) == Qfalse ||
+ !RHASH (props)->tbl->num_entries)
+ rb_raise (ePropError, "invalid EET properties");
+
+ name = rb_funcall (self, id_to_eet_name, 0);
+ StringValue (name);
+
+ if (!RSTRING (name)->len ||
+ rb_funcall (name, id_include, 1, INT2FIX (0)))
+ rb_raise (eNameError, "invalid EET name");
+
+ stream = rb_class_new_instance (0, NULL, cStream);
+
+ rb_hash_foreach (props, for_each_prop, stream);
+
+ args[0] = name;
+ args[1] = rb_funcall (stream, id_serialize, 0);
+ chunk = rb_class_new_instance (2, args, cChunk);
+
+ stream = rb_class_new_instance (1, &chunk, cStream);
+
+ return rb_funcall (stream, id_serialize, 0);
+}
+