massive glob of checkins: improved tests, more tests, bugfixes
diff --git a/src/libFLAC/metadata_object.c b/src/libFLAC/metadata_object.c
index aa379fa..21f81c4 100644
--- a/src/libFLAC/metadata_object.c
+++ b/src/libFLAC/metadata_object.c
@@ -340,6 +340,124 @@
 	free(object);
 }
 
+static FLAC__bool compare_block_data_streaminfo_(const FLAC__StreamMetaData_StreamInfo *block1, const FLAC__StreamMetaData_StreamInfo *block2)
+{
+	if(block1->min_blocksize != block2->min_blocksize)
+		return false;
+	if(block1->max_blocksize != block2->max_blocksize)
+		return false;
+	if(block1->min_framesize != block2->min_framesize)
+		return false;
+	if(block1->max_framesize != block2->max_framesize)
+		return false;
+	if(block1->sample_rate != block2->sample_rate)
+		return false;
+	if(block1->channels != block2->channels)
+		return false;
+	if(block1->bits_per_sample != block2->bits_per_sample)
+		return false;
+	if(block1->total_samples != block2->total_samples)
+		return false;
+	if(0 != memcmp(block1->md5sum, block2->md5sum, 16))
+		return false;
+	return true;
+}
+
+static FLAC__bool compare_block_data_application_(const FLAC__StreamMetaData_Application *block1, const FLAC__StreamMetaData_Application *block2, unsigned block_length)
+{
+	FLAC__ASSERT(0 != block1);
+	FLAC__ASSERT(0 != block2);
+	FLAC__ASSERT(block_length >= sizeof(block1->id));
+
+	if(0 != memcmp(block1->id, block2->id, sizeof(block1->id)))
+		return false;
+	if(0 != block1->data && 0 != block2->data)
+		return 0 == memcmp(block1->data, block2->data, block_length - sizeof(block1->id));
+	else
+		return block1->data == block2->data;
+}
+
+static FLAC__bool compare_block_data_seektable_(const FLAC__StreamMetaData_SeekTable *block1, const FLAC__StreamMetaData_SeekTable *block2)
+{
+	unsigned i;
+
+	FLAC__ASSERT(0 != block1);
+	FLAC__ASSERT(0 != block2);
+
+	if(block1->num_points != block2->num_points)
+		return false;
+
+	if(0 != block1->points && 0 != block2->points) {
+		for(i = 0; i < block1->num_points; i++) {
+			if(block1->points[i].sample_number != block2->points[i].sample_number)
+				return false;
+			if(block1->points[i].stream_offset != block2->points[i].stream_offset)
+				return false;
+			if(block1->points[i].frame_samples != block2->points[i].frame_samples)
+				return false;
+		}
+		return true;
+	}
+	else
+		return block1->points == block2->points;
+}
+
+static FLAC__bool compare_block_data_vorbiscomment_(const FLAC__StreamMetaData_VorbisComment *block1, const FLAC__StreamMetaData_VorbisComment *block2)
+{
+	unsigned i;
+
+	if(block1->vendor_string.length != block2->vendor_string.length)
+		return false;
+
+	if(0 != block1->vendor_string.entry && 0 != block2->vendor_string.entry) {
+		if(0 != memcmp(block1->vendor_string.entry, block2->vendor_string.entry, block1->vendor_string.length))
+			return false;
+	}
+	else if(block1->vendor_string.entry != block2->vendor_string.entry)
+		return false;
+
+	if(block1->num_comments != block2->num_comments)
+		return false;
+
+	for(i = 0; i < block1->num_comments; i++) {
+		if(0 != block1->comments[i].entry && 0 != block2->comments[i].entry) {
+			if(0 != memcmp(block1->comments[i].entry, block2->comments[i].entry, block1->comments[i].length))
+				return false;
+		}
+		else if(block1->comments[i].entry != block2->comments[i].entry)
+			return false;
+	}
+	return true;
+}
+
+FLAC__bool FLAC__metadata_object_is_equal(const FLAC__StreamMetaData *block1, const FLAC__StreamMetaData *block2)
+{
+	if(block1->type != block2->type) {
+		return false;
+    }
+	if(block1->is_last != block2->is_last) {
+		return false;
+    }
+	if(block1->length != block2->length) {
+		return false;
+    }
+	switch(block1->type) {
+		case FLAC__METADATA_TYPE_STREAMINFO:
+			return compare_block_data_streaminfo_(&block1->data.stream_info, &block2->data.stream_info);
+		case FLAC__METADATA_TYPE_PADDING:
+			return true; /* we don't compare the padding guts */
+		case FLAC__METADATA_TYPE_APPLICATION:
+			return compare_block_data_application_(&block1->data.application, &block2->data.application, block1->length);
+		case FLAC__METADATA_TYPE_SEEKTABLE:
+			return compare_block_data_seektable_(&block1->data.seek_table, &block2->data.seek_table);
+		case FLAC__METADATA_TYPE_VORBIS_COMMENT:
+			return compare_block_data_vorbiscomment_(&block1->data.vorbis_comment, &block2->data.vorbis_comment);
+		default:
+			FLAC__ASSERT(0);
+			return false;
+	}
+}
+
 /*@@@@move
 sets the application data to 'data'.  if 'copy' is true, makes, copy, else takes ownership of pointer.  returns false if copy==true and malloc fails.
     FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_APPLICATION);