split metadata.c into metadata_object.c and metadata_iterators.c
authorJosh Coalson <jcoalson@users.sourceforce.net>
Thu, 30 May 2002 05:23:38 +0000 (05:23 +0000)
committerJosh Coalson <jcoalson@users.sourceforce.net>
Thu, 30 May 2002 05:23:38 +0000 (05:23 +0000)
src/libFLAC/Makefile.am
src/libFLAC/Makefile.lite
src/libFLAC/Makefile.vc
src/libFLAC/metadata_iterators.c [moved from src/libFLAC/metadata.c with 82% similarity]
src/libFLAC/metadata_object.c [new file with mode: 0644]

index cbafa5c..e3b92fc 100644 (file)
@@ -48,7 +48,8 @@ libFLAC_la_SOURCES = \
        lpc.c \
        md5.c \
        memory.c \
-       metadata.c \
+       metadata_iterators.c \
+       metadata_object.c \
        seek_table.c \
        seekable_stream_decoder.c \
        stream_decoder.c \
index 2f92f7f..a5723d8 100644 (file)
@@ -55,7 +55,8 @@ OBJS = $(ASM_OBJS) \
        lpc.o \
        md5.o \
        memory.o \
-       metadata.o \
+       metadata_iterators.o \
+       metadata_object.o \
        seek_table.o \
        seekable_stream_decoder.o \
        stream_decoder.o \
index 588b4c7..b76733b 100644 (file)
@@ -39,7 +39,8 @@ C_FILES= \
        lpc.c \\r
        md5.c \\r
        memory.c \\r
-       metadata.c \\r
+       metadata_iterators.c \\r
+       metadata_object.c \\r
        seek_table.c \\r
        seekable_stream_decoder.c \\r
        stream_decoder.c \\r
similarity index 82%
rename from src/libFLAC/metadata.c
rename to src/libFLAC/metadata_iterators.c
index 01369ce..deeccc1 100644 (file)
@@ -32,8 +32,9 @@
 #endif
 #include <sys/stat.h> /* for stat(), maybe chmod() */
 
+#include "private/metadata.h"
+
 #include "FLAC/assert.h"
-#include "FLAC/metadata.h"
 #include "FLAC/file_decoder.h"
 
 #ifdef max
@@ -52,9 +53,6 @@
  *
  ***************************************************************************/
 
-/* this one should probably not go in the public interface: */
-static void FLAC__metadata_object_delete_data_(FLAC__StreamMetaData *object);
-
 static void pack_uint32_(FLAC__uint32 val, FLAC__byte *b, unsigned bytes);
 static void pack_uint32_little_endian_(FLAC__uint32 val, FLAC__byte *b, unsigned bytes);
 static void pack_uint64_(FLAC__uint64 val, FLAC__byte *b, unsigned bytes);
@@ -1148,7 +1146,7 @@ FLAC__bool FLAC__metadata_iterator_delete_block(FLAC__MetaData_Iterator *iterato
        save = iterator->current->prev;
 
        if(replace_with_padding) {
-               FLAC__metadata_object_delete_data_(iterator->current->data);
+               FLAC__metadata_object_delete_data(iterator->current->data);
                iterator->current->data->type = FLAC__METADATA_TYPE_PADDING;
        }
        else {
@@ -1205,479 +1203,6 @@ FLAC__bool FLAC__metadata_iterator_insert_block_after(FLAC__MetaData_Iterator *i
 
 /****************************************************************************
  *
- * Metadata object routines
- *
- ***************************************************************************/
-
-/*@@@@move
-will return pointer to new empty object of type 'type', or 0 if malloc failed
-type is valid type
-*/
-FLAC__StreamMetaData *FLAC__metadata_object_new(FLAC__MetaDataType type)
-{
-       FLAC__StreamMetaData *object = malloc(sizeof(FLAC__StreamMetaData));
-       if(0 != object) {
-               memset(object, 0, sizeof(FLAC__StreamMetaData));
-               object->is_last = false;
-               object->type = type;
-               switch(type) {
-                       case FLAC__METADATA_TYPE_STREAMINFO:
-                               object->length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH;
-                               break;
-                       case FLAC__METADATA_TYPE_PADDING:
-                               break;
-                       case FLAC__METADATA_TYPE_APPLICATION:
-                               object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8;
-                               break;
-                       case FLAC__METADATA_TYPE_SEEKTABLE:
-                               break;
-                       case FLAC__METADATA_TYPE_VORBIS_COMMENT:
-                               object->length = (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN + FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN) / 8;
-                               break;
-                       default:
-                               /* double protection: */
-                               FLAC__ASSERT(0);
-                               free(object);
-                               return 0;
-               }
-       }
-
-       return object;
-}
-
-static FLAC__bool copy_bytes_(FLAC__byte **to, const FLAC__byte *from, unsigned bytes)
-{
-       if(bytes > 0 && 0 != from) {
-               if(0 == (*to = malloc(bytes)))
-                       return false;
-               memcpy(*to, from, bytes);
-       }
-       else {
-               FLAC__ASSERT(0 == from);
-               FLAC__ASSERT(bytes == 0);
-               *to = 0;
-       }
-       return true;
-}
-
-static FLAC__bool copy_vcentry_(FLAC__StreamMetaData_VorbisComment_Entry *to, const FLAC__StreamMetaData_VorbisComment_Entry *from)
-{
-       to->length = from->length;
-       if(0 == from->entry) {
-               FLAC__ASSERT(from->length == 0);
-               to->entry = 0;
-       }
-       else {
-               FLAC__ASSERT(from->length > 0);
-               if(0 == (to->entry = malloc(from->length)))
-                       return false;
-               memcpy(to->entry, from->entry, from->length);
-       }
-       return true;
-}
-
-/*@@@@move
-return a pointer to a copy of 'object', or 0 if any malloc failed.  does a deep copy.  user gets ownership of object.
-    FLAC__ASSERT(0 != object);
-*/
-FLAC__StreamMetaData *FLAC__metadata_object_copy(const FLAC__StreamMetaData *object)
-{
-       FLAC__StreamMetaData *to;
-       unsigned i;
-
-       FLAC__ASSERT(0 != object);
-
-       if(0 != (to = FLAC__metadata_object_new(object->type))) {
-               to->is_last = object->is_last;
-               to->type = object->type;
-               to->length = object->length;
-               switch(to->type) {
-                       case FLAC__METADATA_TYPE_STREAMINFO:
-                               memcpy(&to->data.stream_info, &object->data.stream_info, sizeof(FLAC__StreamMetaData_StreamInfo));
-                               break;
-                       case FLAC__METADATA_TYPE_PADDING:
-                               break;
-                       case FLAC__METADATA_TYPE_APPLICATION:
-                               memcpy(&to->data.application.id, &object->data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8);
-                               if(!copy_bytes_(&to->data.application.data, object->data.application.data, object->length - FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8)) {
-                                       FLAC__metadata_object_delete(to);
-                                       return 0;
-                               }
-                               break;
-                       case FLAC__METADATA_TYPE_SEEKTABLE:
-                               to->data.seek_table.num_points = object->data.seek_table.num_points;
-                               if(!copy_bytes_((FLAC__byte**)&to->data.seek_table.points, (FLAC__byte*)object->data.seek_table.points, object->data.seek_table.num_points * sizeof(FLAC__StreamMetaData_SeekPoint))) {
-                                       FLAC__metadata_object_delete(to);
-                                       return 0;
-                               }
-                               break;
-                       case FLAC__METADATA_TYPE_VORBIS_COMMENT:
-                               if(!copy_vcentry_(&to->data.vorbis_comment.vendor_string, &object->data.vorbis_comment.vendor_string)) {
-                                       FLAC__metadata_object_delete(to);
-                                       return 0;
-                               }
-                               if(object->data.vorbis_comment.num_comments == 0) {
-                                       FLAC__ASSERT(0 == object->data.vorbis_comment.comments);
-                                       to->data.vorbis_comment.comments = 0;
-                               }
-                               else {
-                                       FLAC__ASSERT(0 != object->data.vorbis_comment.comments);
-                                       to->data.vorbis_comment.comments = FLAC__metadata_object_vorbiscomment_entry_array_copy(object->data.vorbis_comment.comments);
-                                       if(0 == to->data.vorbis_comment.comments) {
-                                               FLAC__metadata_object_delete(to);
-                                               return 0;
-                                       }
-                               }
-                               to->data.vorbis_comment.num_comments = object->data.vorbis_comment.num_comments;
-                               break;
-                       default:
-                               /* double protection: */
-                               FLAC__ASSERT(0);
-                               free(to);
-                               return 0;
-               }
-       }
-
-       return to;
-}
-
-void FLAC__metadata_object_delete_data_(FLAC__StreamMetaData *object)
-{
-       unsigned i;
-
-       FLAC__ASSERT(0 != object);
-
-       switch(object->type) {
-               case FLAC__METADATA_TYPE_STREAMINFO:
-               case FLAC__METADATA_TYPE_PADDING:
-                       break;
-               case FLAC__METADATA_TYPE_APPLICATION:
-                       if(0 != object->data.application.data)
-                               free(object->data.application.data);
-                       break;
-               case FLAC__METADATA_TYPE_SEEKTABLE:
-                       if(0 != object->data.seek_table.points)
-                               free(object->data.seek_table.points);
-                       break;
-               case FLAC__METADATA_TYPE_VORBIS_COMMENT:
-                       if(0 != object->data.vorbis_comment.vendor_string.entry)
-                               free(object->data.vorbis_comment.vendor_string.entry);
-                       if(0 != object->data.vorbis_comment.comments) {
-                               FLAC__ASSERT(object->data.vorbis_comment.num_comments > 0);
-                               FLAC__metadata_object_vorbiscomment_entry_array_delete(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments);
-                       }
-                       break;
-               default:
-                       FLAC__ASSERT(0);
-       }
-}
-
-/*@@@@move
-frees 'object'.  does a deep delete.
-*/
-void FLAC__metadata_object_delete(FLAC__StreamMetaData *object)
-{
-       FLAC__metadata_object_delete_data_(object);
-       free(object);
-}
-
-/*@@@@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);
-    FLAC__ASSERT((0 != data && length > 0) || (0 == data && length == 0 && copy == false));
-*/
-FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetaData *object, FLAC__byte *data, unsigned length, FLAC__bool copy)
-{
-       FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_APPLICATION);
-       FLAC__ASSERT((0 != data && length > 0) || (0 == data && length == 0 && copy == false));
-
-       if(0 != object->data.application.data)
-               free(object->data.application.data);
-
-       if(copy) {
-               if(!copy_bytes_(&object->data.application.data, data, length))
-                       return false;
-       }
-       else {
-               object->data.application.data = data;
-       }
-       object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8 + length;
-       return true;
-}
-
-/*@@@@move
-creates an array of seekpoints (FLAC__StreamMetaData_SeekPoint).  returns pointer to new array, or NULL if malloc fails.
-num_points > 0.
-*/
-FLAC__StreamMetaData_SeekPoint *FLAC__metadata_object_seekpoint_array_new(unsigned num_points)
-{
-       FLAC__StreamMetaData_SeekPoint *object_array;
-
-       FLAC__ASSERT(num_points > 0);
-
-       object_array = malloc(num_points * sizeof(FLAC__StreamMetaData_SeekPoint));
-
-       if(0 != object_array)
-               memset(object_array, 0, num_points * sizeof(FLAC__StreamMetaData_SeekPoint));
-
-       return object_array;
-}
-
-/*@@@@move
-return pointer to copy of seekpoint array, or NULL if malloc fails.
-num_points > 0
-object_array != NULL
-*/
-FLAC__StreamMetaData_SeekPoint *FLAC__metadata_object_seekpoint_array_copy(const FLAC__StreamMetaData_SeekPoint *object_array, unsigned num_points)
-{
-       FLAC__StreamMetaData_SeekPoint *return_array;
-
-       FLAC__ASSERT(0 != object_array);
-       FLAC__ASSERT(num_points > 0);
-
-       return_array = FLAC__metadata_object_seekpoint_array_new(num_points);
-
-       if(0 != return_array)
-               memcpy(return_array, object_array, num_points * sizeof(FLAC__StreamMetaData_SeekPoint));
-
-       return return_array;
-}
-
-/*@@@@move
-frees object array.
-object_array != NULL
-*/
-void FLAC__metadata_object_seekpoint_array_delete(FLAC__StreamMetaData_SeekPoint *object_array)
-{
-       FLAC__ASSERT(0 != object_array);
-
-       free(object_array);
-}
-
-FLAC__bool FLAC__metadata_object_seekpoint_array_resize(FLAC__StreamMetaData_SeekPoint **object_array, unsigned old_num_points, unsigned new_num_points)
-{
-       FLAC__ASSERT(0 != object_array);
-
-       if(0 == *object_array) {
-               FLAC__ASSERT(old_num_points == 0);
-               if(0 == new_num_points)
-                       return true;
-               else
-                       return 0 != (*object_array = FLAC__metadata_object_seekpoint_array_new(new_num_points));
-       }
-       else {
-               const unsigned old_size = old_num_points * sizeof(FLAC__StreamMetaData_SeekPoint);
-               const unsigned new_size = new_num_points * sizeof(FLAC__StreamMetaData_SeekPoint);
-
-               FLAC__ASSERT(old_num_points > 0);
-
-               if(new_size == 0) {
-                       free(*object_array);
-                       *object_array = 0;
-               }
-               else if(0 == (*object_array = realloc(*object_array, new_size)))
-                       return false;
-
-               if(new_size > old_size)
-                       memset(*object_array + old_num_points, 0, new_size - old_size);
-
-               return true;
-       }
-}
-
-FLAC__bool FLAC__metadata_object_seektable_set_points(FLAC__StreamMetaData *object, FLAC__StreamMetaData_SeekPoint *points, unsigned num_points, FLAC__bool copy)
-{
-       FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
-       FLAC__ASSERT((0 != points && num_points > 0) || (0 == points && num_points == 0 && copy == false));
-
-       object->data.seek_table.num_points = num_points;
-
-       if(0 != object->data.seek_table.points)
-               FLAC__metadata_object_seekpoint_array_delete(object->data.seek_table.points);
-
-       if(copy) {
-               if(0 == (object->data.seek_table.points = FLAC__metadata_object_seekpoint_array_copy(points, num_points)))
-                       return false;
-       }
-       else {
-               object->data.seek_table.points = points;
-       }
-       object->length = num_points * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH;
-       return true;
-}
-
-FLAC__StreamMetaData_VorbisComment_Entry *FLAC__metadata_object_vorbiscomment_entry_array_new(unsigned num_comments)
-{
-       FLAC__StreamMetaData_VorbisComment_Entry *object_array;
-
-       FLAC__ASSERT(num_comments > 0);
-
-       object_array = malloc(num_comments * sizeof(FLAC__StreamMetaData_VorbisComment_Entry));
-
-       if(0 != object_array)
-               memset(object_array, 0, num_comments * sizeof(FLAC__StreamMetaData_VorbisComment_Entry));
-
-       return object_array;
-}
-
-FLAC__StreamMetaData_VorbisComment_Entry *FLAC__metadata_object_vorbiscomment_entry_array_copy(const FLAC__StreamMetaData_VorbisComment_Entry *object_array, unsigned num_comments)
-{
-       FLAC__StreamMetaData_VorbisComment_Entry *return_array;
-
-       FLAC__ASSERT(0 != object_array);
-       FLAC__ASSERT(num_comments > 0);
-
-       return_array = FLAC__metadata_object_vorbiscomment_entry_array_new(num_comments);
-
-       if(0 != return_array) {
-               unsigned i;
-
-               /* Need to do this to set the pointers inside the comments to 0.
-                * In case of an error in the following loop, the object will be
-                * deleted and we don't want the destructor freeing uninitialized
-                * pointers.
-                */
-               memset(return_array, 0, num_comments * sizeof(FLAC__StreamMetaData_VorbisComment_Entry));
-
-               for(i = 0; i < num_comments; i++) {
-                       if(!copy_vcentry_(return_array+i, object_array+i)) {
-                               FLAC__metadata_object_vorbiscomment_entry_array_delete(return_array, num_comments);
-                               return 0;
-                       }
-               }
-       }
-
-       return return_array;
-}
-
-void FLAC__metadata_object_vorbiscomment_entry_array_delete(FLAC__StreamMetaData_VorbisComment_Entry *object_array, unsigned num_comments)
-{
-       unsigned i;
-
-       FLAC__ASSERT(0 != object_array && num_comments > 0);
-
-       for(i = 0; i < num_comments; i++)
-               if(0 != object_array[i].entry)
-                       free(object_array[i].entry);
-
-       if(0 != object_array)
-               free(object_array);
-}
-
-FLAC__bool FLAC__metadata_object_vorbiscomment_entry_array_resize(FLAC__StreamMetaData_VorbisComment_Entry **object_array, unsigned old_num_comments, unsigned new_num_comments)
-{
-       FLAC__ASSERT(0 != object_array);
-
-       if(0 == *object_array) {
-               FLAC__ASSERT(old_num_comments == 0);
-               if(0 == new_num_comments)
-                       return true;
-               else
-                       return 0 != (*object_array = FLAC__metadata_object_vorbiscomment_entry_array_new(new_num_comments));
-       }
-       else {
-               const unsigned old_size = old_num_comments * sizeof(FLAC__StreamMetaData_VorbisComment_Entry);
-               const unsigned new_size = new_num_comments * sizeof(FLAC__StreamMetaData_VorbisComment_Entry);
-
-               FLAC__ASSERT(old_num_comments > 0);
-
-               /* if shrinking, free the truncated entries */
-               if(new_num_comments < old_num_comments) {
-                       unsigned i;
-                       for(i = new_num_comments; i < old_num_comments; i++)
-                               if(0 != (*object_array)[i].entry)
-                                       free((*object_array)[i].entry);
-               }
-
-               if(new_size == 0) {
-                       free(*object_array);
-                       *object_array = 0;
-               }
-               else if(0 == (*object_array = realloc(*object_array, new_size)))
-                       return false;
-
-               /* if growing, zero all the length/pointers of new elements */
-               if(new_size > old_size)
-                       memset(*object_array + old_num_comments, 0, new_size - old_size);
-
-               return true;
-       }
-}
-
-FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetaData *object, FLAC__byte *entry, unsigned length, FLAC__bool copy)
-{
-       FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
-       FLAC__ASSERT((0 != entry && length > 0) || (0 == entry && length == 0 && copy == false));
-
-       if(0 != object->data.vorbis_comment.vendor_string.entry)
-               free(object->data.vorbis_comment.vendor_string.entry);
-
-       object->length -= object->data.vorbis_comment.vendor_string.length;
-
-       if(copy) {
-               if(!copy_bytes_(&object->data.vorbis_comment.vendor_string.entry, entry, length)) {
-                       object->data.vorbis_comment.vendor_string.length = 0;
-                       return false;
-               }
-       }
-       else {
-               object->data.vorbis_comment.vendor_string.entry = entry;
-       }
-
-       object->data.vorbis_comment.vendor_string.length = length;
-       object->length += object->data.vorbis_comment.vendor_string.length;
-
-       return true;
-}
-
-FLAC__bool FLAC__metadata_object_vorbiscomment_set_comments(FLAC__StreamMetaData *object, FLAC__StreamMetaData_VorbisComment_Entry *comments, unsigned num_comments, FLAC__bool copy)
-{
-       unsigned i;
-
-       FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
-       FLAC__ASSERT((0 != comments && num_comments > 0) || (0 == comments && num_comments == 0 && copy == false));
-
-       object->data.vorbis_comment.num_comments = num_comments;
-
-       if(0 != object->data.vorbis_comment.comments)
-               FLAC__metadata_object_vorbiscomment_entry_array_delete(object->data.vorbis_comment.comments, num_comments);
-
-       if(copy) {
-               if(0 == (object->data.vorbis_comment.comments = FLAC__metadata_object_vorbiscomment_entry_array_copy(comments, num_comments)))
-                       return false;
-       }
-       else {
-               object->data.vorbis_comment.comments = comments;
-       }
-       object->length = num_comments * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH;
-
-       /* calculate the new length */
-       object->length = (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN) / 8;
-       object->length += object->data.vorbis_comment.vendor_string.length;
-       object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN) / 8;
-       for(i = 0; i < object->data.vorbis_comment.num_comments; i++) {
-               object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8);
-               object->length += object->data.vorbis_comment.comments[i].length;
-       }
-
-       return true;
-}
-
-FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__StreamMetaData *object, unsigned comment_num, FLAC__byte *entry, unsigned length, FLAC__bool copy)
-{
-}
-
-FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__StreamMetaData *object, unsigned comment_num, FLAC__byte *entry, unsigned length, FLAC__bool copy)
-{
-}
-
-FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetaData *object, unsigned comment_num)
-{
-}
-
-
-/****************************************************************************
- *
  * Local function definitions
  *
  ***************************************************************************/
diff --git a/src/libFLAC/metadata_object.c b/src/libFLAC/metadata_object.c
new file mode 100644 (file)
index 0000000..a1b6989
--- /dev/null
@@ -0,0 +1,553 @@
+/* libFLAC - Free Lossless Audio Codec library
+ * Copyright (C) 2001,2002  Josh Coalson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA  02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "private/metadata.h"
+
+#include "FLAC/assert.h"
+
+
+/****************************************************************************
+ *
+ * Local routines
+ *
+ ***************************************************************************/
+
+static FLAC__bool copy_bytes_(FLAC__byte **to, const FLAC__byte *from, unsigned bytes)
+{
+       if(bytes > 0 && 0 != from) {
+               FLAC__byte *x;
+               if(0 == (x = malloc(bytes)))
+                       return false;
+               memcpy(x, from, bytes);
+               *to = x;
+       }
+       else {
+               FLAC__ASSERT(0 == from);
+               FLAC__ASSERT(bytes == 0);
+               *to = 0;
+       }
+       return true;
+}
+
+static FLAC__bool copy_vcentry_(FLAC__StreamMetaData_VorbisComment_Entry *to, const FLAC__StreamMetaData_VorbisComment_Entry *from)
+{
+       to->length = from->length;
+       if(0 == from->entry) {
+               FLAC__ASSERT(from->length == 0);
+               to->entry = 0;
+       }
+       else {
+               FLAC__byte *x;
+               FLAC__ASSERT(from->length > 0);
+               if(0 == (x = malloc(from->length)))
+                       return false;
+               memcpy(x, from->entry, from->length);
+               to->entry = x;
+       }
+       return true;
+}
+
+static void seektable_calculate_length_(FLAC__StreamMetaData *object)
+{
+       FLAC__ASSERT(0 != object);
+       FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
+
+       object->length = object->data.seek_table.num_points * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH;
+}
+
+static FLAC__StreamMetaData_SeekPoint *seekpoint_array_new_(unsigned num_points)
+{
+       FLAC__StreamMetaData_SeekPoint *object_array;
+
+       FLAC__ASSERT(num_points > 0);
+
+       object_array = malloc(num_points * sizeof(FLAC__StreamMetaData_SeekPoint));
+
+       if(0 != object_array) {
+               unsigned i;
+               for(i = 0; i < num_points; i++) {
+                       object_array[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER;
+                       object_array[i].stream_offset = 0;
+                       object_array[i].frame_samples = 0;
+               }
+       }
+
+       return object_array;
+}
+
+static void vorbiscomment_calculate_length_(FLAC__StreamMetaData *object)
+{
+       unsigned i;
+
+       FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
+
+       object->length = (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN) / 8;
+       object->length += object->data.vorbis_comment.vendor_string.length;
+       object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN) / 8;
+       for(i = 0; i < object->data.vorbis_comment.num_comments; i++) {
+               object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8);
+               object->length += object->data.vorbis_comment.comments[i].length;
+       }
+}
+
+static FLAC__StreamMetaData_VorbisComment_Entry *vorbiscomment_entry_array_new_(unsigned num_comments)
+{
+       FLAC__StreamMetaData_VorbisComment_Entry *object_array;
+
+       FLAC__ASSERT(num_comments > 0);
+
+       object_array = malloc(num_comments * sizeof(FLAC__StreamMetaData_VorbisComment_Entry));
+
+       if(0 != object_array)
+               memset(object_array, 0, num_comments * sizeof(FLAC__StreamMetaData_VorbisComment_Entry));
+
+       return object_array;
+}
+
+static void vorbiscomment_entry_array_delete_(FLAC__StreamMetaData_VorbisComment_Entry *object_array, unsigned num_comments)
+{
+       unsigned i;
+
+       FLAC__ASSERT(0 != object_array && num_comments > 0);
+
+       for(i = 0; i < num_comments; i++)
+               if(0 != object_array[i].entry)
+                       free(object_array[i].entry);
+
+       if(0 != object_array)
+               free(object_array);
+}
+
+static FLAC__StreamMetaData_VorbisComment_Entry *vorbiscomment_entry_array_copy_(const FLAC__StreamMetaData_VorbisComment_Entry *object_array, unsigned num_comments)
+{
+       FLAC__StreamMetaData_VorbisComment_Entry *return_array;
+
+       FLAC__ASSERT(0 != object_array);
+       FLAC__ASSERT(num_comments > 0);
+
+       return_array = vorbiscomment_entry_array_new_(num_comments);
+
+       if(0 != return_array) {
+               unsigned i;
+
+               /* Need to do this to set the pointers inside the comments to 0.
+                * In case of an error in the following loop, the object will be
+                * deleted and we don't want the destructor freeing uninitialized
+                * pointers.
+                */
+               memset(return_array, 0, num_comments * sizeof(FLAC__StreamMetaData_VorbisComment_Entry));
+
+               for(i = 0; i < num_comments; i++) {
+                       if(!copy_vcentry_(return_array+i, object_array+i)) {
+                               vorbiscomment_entry_array_delete_(return_array, num_comments);
+                               return 0;
+                       }
+               }
+       }
+
+       return return_array;
+}
+
+static FLAC__bool vorbiscomment_set_entry_(FLAC__StreamMetaData *object, FLAC__StreamMetaData_VorbisComment_Entry *dest, FLAC__StreamMetaData_VorbisComment_Entry *src, FLAC__bool copy)
+{
+       FLAC__byte *save;
+
+       FLAC__ASSERT(0 != object);
+       FLAC__ASSERT(0 != dest);
+       FLAC__ASSERT(0 != src);
+       FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
+       FLAC__ASSERT((0 != src->entry && src->length > 0) || (0 == src->entry && src->length == 0 && copy == false));
+
+       save = src->entry;
+
+       /* do the copy first so that if we fail we leave the object untouched */
+       if(copy) {
+               if(!copy_vcentry_(dest, src))
+                       return false;
+       }
+       else {
+               *dest = *src;
+       }
+
+       if(0 != save)
+               free(save);
+
+       vorbiscomment_calculate_length_(object);
+       return true;
+}
+
+
+/****************************************************************************
+ *
+ * Metadata object routines
+ *
+ ***************************************************************************/
+
+/*@@@@move
+will return pointer to new empty object of type 'type', or 0 if malloc failed
+type is valid type
+*/
+FLAC__StreamMetaData *FLAC__metadata_object_new(FLAC__MetaDataType type)
+{
+       FLAC__StreamMetaData *object = malloc(sizeof(FLAC__StreamMetaData));
+       if(0 != object) {
+               memset(object, 0, sizeof(FLAC__StreamMetaData));
+               object->is_last = false;
+               object->type = type;
+               switch(type) {
+                       case FLAC__METADATA_TYPE_STREAMINFO:
+                               object->length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH;
+                               break;
+                       case FLAC__METADATA_TYPE_PADDING:
+                               break;
+                       case FLAC__METADATA_TYPE_APPLICATION:
+                               object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8;
+                               break;
+                       case FLAC__METADATA_TYPE_SEEKTABLE:
+                               break;
+                       case FLAC__METADATA_TYPE_VORBIS_COMMENT:
+                               object->length = (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN + FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN) / 8;
+                               break;
+                       default:
+                               /* double protection: */
+                               FLAC__ASSERT(0);
+                               free(object);
+                               return 0;
+               }
+       }
+
+       return object;
+}
+
+/*@@@@move
+return a pointer to a copy of 'object', or 0 if any malloc failed.  does a deep copy.  user gets ownership of object.
+    FLAC__ASSERT(0 != object);
+*/
+FLAC__StreamMetaData *FLAC__metadata_object_copy(const FLAC__StreamMetaData *object)
+{
+       FLAC__StreamMetaData *to;
+
+       FLAC__ASSERT(0 != object);
+
+       if(0 != (to = FLAC__metadata_object_new(object->type))) {
+               to->is_last = object->is_last;
+               to->type = object->type;
+               to->length = object->length;
+               switch(to->type) {
+                       case FLAC__METADATA_TYPE_STREAMINFO:
+                               memcpy(&to->data.stream_info, &object->data.stream_info, sizeof(FLAC__StreamMetaData_StreamInfo));
+                               break;
+                       case FLAC__METADATA_TYPE_PADDING:
+                               break;
+                       case FLAC__METADATA_TYPE_APPLICATION:
+                               memcpy(&to->data.application.id, &object->data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8);
+                               if(!copy_bytes_(&to->data.application.data, object->data.application.data, object->length - FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8)) {
+                                       FLAC__metadata_object_delete(to);
+                                       return 0;
+                               }
+                               break;
+                       case FLAC__METADATA_TYPE_SEEKTABLE:
+                               to->data.seek_table.num_points = object->data.seek_table.num_points;
+                               if(!copy_bytes_((FLAC__byte**)&to->data.seek_table.points, (FLAC__byte*)object->data.seek_table.points, object->data.seek_table.num_points * sizeof(FLAC__StreamMetaData_SeekPoint))) {
+                                       FLAC__metadata_object_delete(to);
+                                       return 0;
+                               }
+                               break;
+                       case FLAC__METADATA_TYPE_VORBIS_COMMENT:
+                               if(!copy_vcentry_(&to->data.vorbis_comment.vendor_string, &object->data.vorbis_comment.vendor_string)) {
+                                       FLAC__metadata_object_delete(to);
+                                       return 0;
+                               }
+                               if(object->data.vorbis_comment.num_comments == 0) {
+                                       FLAC__ASSERT(0 == object->data.vorbis_comment.comments);
+                                       to->data.vorbis_comment.comments = 0;
+                               }
+                               else {
+                                       FLAC__ASSERT(0 != object->data.vorbis_comment.comments);
+                                       to->data.vorbis_comment.comments = vorbiscomment_entry_array_copy_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments);
+                                       if(0 == to->data.vorbis_comment.comments) {
+                                               FLAC__metadata_object_delete(to);
+                                               return 0;
+                                       }
+                               }
+                               to->data.vorbis_comment.num_comments = object->data.vorbis_comment.num_comments;
+                               break;
+                       default:
+                               /* double protection: */
+                               FLAC__ASSERT(0);
+                               free(to);
+                               return 0;
+               }
+       }
+
+       return to;
+}
+
+void FLAC__metadata_object_delete_data(FLAC__StreamMetaData *object)
+{
+       FLAC__ASSERT(0 != object);
+
+       switch(object->type) {
+               case FLAC__METADATA_TYPE_STREAMINFO:
+               case FLAC__METADATA_TYPE_PADDING:
+                       break;
+               case FLAC__METADATA_TYPE_APPLICATION:
+                       if(0 != object->data.application.data)
+                               free(object->data.application.data);
+                       break;
+               case FLAC__METADATA_TYPE_SEEKTABLE:
+                       if(0 != object->data.seek_table.points)
+                               free(object->data.seek_table.points);
+                       break;
+               case FLAC__METADATA_TYPE_VORBIS_COMMENT:
+                       if(0 != object->data.vorbis_comment.vendor_string.entry)
+                               free(object->data.vorbis_comment.vendor_string.entry);
+                       if(0 != object->data.vorbis_comment.comments) {
+                               FLAC__ASSERT(object->data.vorbis_comment.num_comments > 0);
+                               vorbiscomment_entry_array_delete_(object->data.vorbis_comment.comments, object->data.vorbis_comment.num_comments);
+                       }
+                       break;
+               default:
+                       FLAC__ASSERT(0);
+       }
+}
+
+/*@@@@move
+frees 'object'.  does a deep delete.
+*/
+void FLAC__metadata_object_delete(FLAC__StreamMetaData *object)
+{
+       FLAC__metadata_object_delete_data(object);
+       free(object);
+}
+
+/*@@@@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);
+    FLAC__ASSERT((0 != data && length > 0) || (0 == data && length == 0 && copy == false));
+*/
+FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetaData *object, FLAC__byte *data, unsigned length, FLAC__bool copy)
+{
+       FLAC__byte *save;
+
+       FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_APPLICATION);
+       FLAC__ASSERT((0 != data && length > 0) || (0 == data && length == 0 && copy == false));
+
+       save = object->data.application.data;
+
+       /* do the copy first so that if we fail we leave the object untouched */
+       if(copy) {
+               if(!copy_bytes_(&object->data.application.data, data, length))
+                       return false;
+       }
+       else {
+               object->data.application.data = data;
+       }
+
+       if(0 != save)
+               free(save);
+
+       object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8 + length;
+       return true;
+}
+
+FLAC__bool FLAC__metadata_object_seektable_resize_points(FLAC__StreamMetaData *object, unsigned new_num_points)
+{
+       FLAC__ASSERT(0 != object);
+       FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
+
+       if(0 == object->data.seek_table.points) {
+               FLAC__ASSERT(object->data.seek_table.num_points == 0);
+               if(0 == new_num_points)
+                       return true;
+               else if(0 == (object->data.seek_table.points = seekpoint_array_new_(new_num_points)))
+                       return false;
+       }
+       else {
+               const unsigned old_size = object->data.seek_table.num_points * sizeof(FLAC__StreamMetaData_SeekPoint);
+               const unsigned new_size = new_num_points * sizeof(FLAC__StreamMetaData_SeekPoint);
+
+               FLAC__ASSERT(object->data.seek_table.num_points > 0);
+
+               if(new_size == 0) {
+                       free(object->data.seek_table.points);
+                       object->data.seek_table.points = 0;
+               }
+               else if(0 == (object->data.seek_table.points = realloc(object->data.seek_table.points, new_size)))
+                       return false;
+
+               /* if growing, set new elements to placeholders */
+               if(new_size > old_size) {
+                       unsigned i;
+                       for(i = object->data.seek_table.num_points; i < new_num_points; i++) {
+                               object->data.seek_table.points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER;
+                               object->data.seek_table.points[i].stream_offset = 0;
+                               object->data.seek_table.points[i].frame_samples = 0;
+                       }
+               }
+       }
+
+       object->data.seek_table.num_points = new_num_points;
+
+       seektable_calculate_length_(object);
+       return true;
+}
+
+void FLAC__metadata_object_seektable_set_point(FLAC__StreamMetaData *object, unsigned point_num, FLAC__StreamMetaData_SeekPoint point)
+{
+       FLAC__ASSERT(0 != object);
+       FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
+       FLAC__ASSERT(object->data.seek_table.num_points > point_num);
+
+       object->data.seek_table.points[point_num] = point;
+}
+
+FLAC__bool FLAC__metadata_object_seektable_insert_point(FLAC__StreamMetaData *object, unsigned point_num, FLAC__StreamMetaData_SeekPoint point)
+{
+       int i;
+
+       FLAC__ASSERT(0 != object);
+       FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
+       FLAC__ASSERT(object->data.seek_table.num_points >= point_num);
+
+       if(!FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points+1))
+               return false;
+
+       /* move all points >= point_num forward one space */
+       for(i = (int)object->data.seek_table.num_points-1; i > (int)point_num; i--)
+               object->data.seek_table.points[i] = object->data.seek_table.points[i-1];
+
+       FLAC__metadata_object_seektable_set_point(object, point_num, point);
+       seektable_calculate_length_(object);
+       return true;
+}
+
+FLAC__bool FLAC__metadata_object_seektable_delete_point(FLAC__StreamMetaData *object, unsigned point_num)
+{
+       unsigned i;
+
+       FLAC__ASSERT(0 != object);
+       FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
+       FLAC__ASSERT(object->data.seek_table.num_points > point_num);
+
+       /* move all points > point_num backward one space */
+       for(i = point_num; i < object->data.seek_table.num_points-1; i++)
+               object->data.seek_table.points[i] = object->data.seek_table.points[i+1];
+
+       return FLAC__metadata_object_seektable_resize_points(object, object->data.seek_table.num_points-1);
+}
+
+FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetaData *object, FLAC__StreamMetaData_VorbisComment_Entry *entry, FLAC__bool copy)
+{
+       return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.vendor_string, entry, copy);
+}
+
+FLAC__bool FLAC__metadata_object_vorbiscomment_resize_comments(FLAC__StreamMetaData *object, unsigned new_num_comments)
+{
+       FLAC__ASSERT(0 != object);
+       FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
+
+       if(0 == object->data.vorbis_comment.comments) {
+               FLAC__ASSERT(object->data.vorbis_comment.num_comments == 0);
+               if(0 == new_num_comments)
+                       return true;
+               else if(0 == (object->data.vorbis_comment.comments = vorbiscomment_entry_array_new_(new_num_comments)))
+                       return false;
+       }
+       else {
+               const unsigned old_size = object->data.vorbis_comment.num_comments * sizeof(FLAC__StreamMetaData_VorbisComment_Entry);
+               const unsigned new_size = new_num_comments * sizeof(FLAC__StreamMetaData_VorbisComment_Entry);
+
+               FLAC__ASSERT(object->data.vorbis_comment.num_comments > 0);
+
+               /* if shrinking, free the truncated entries */
+               if(new_num_comments < object->data.vorbis_comment.num_comments) {
+                       unsigned i;
+                       for(i = new_num_comments; i < object->data.vorbis_comment.num_comments; i++)
+                               if(0 != object->data.vorbis_comment.comments[i].entry)
+                                       free(object->data.vorbis_comment.comments[i].entry);
+               }
+
+               if(new_size == 0) {
+                       free(object->data.vorbis_comment.comments);
+                       object->data.vorbis_comment.comments = 0;
+               }
+               else if(0 == (object->data.vorbis_comment.comments = realloc(object->data.vorbis_comment.comments, new_size)))
+                       return false;
+
+               /* if growing, zero all the length/pointers of new elements */
+               if(new_size > old_size)
+                       memset(object->data.vorbis_comment.comments + object->data.vorbis_comment.num_comments, 0, new_size - old_size);
+       }
+
+       object->data.vorbis_comment.num_comments = new_num_comments;
+
+       vorbiscomment_calculate_length_(object);
+       return true;
+}
+
+FLAC__bool FLAC__metadata_object_vorbiscomment_set_comment(FLAC__StreamMetaData *object, unsigned comment_num, FLAC__StreamMetaData_VorbisComment_Entry *entry, FLAC__bool copy)
+{
+       return vorbiscomment_set_entry_(object, &object->data.vorbis_comment.comments[comment_num], entry, copy);
+}
+
+FLAC__bool FLAC__metadata_object_vorbiscomment_insert_comment(FLAC__StreamMetaData *object, unsigned comment_num, FLAC__StreamMetaData_VorbisComment_Entry *entry, FLAC__bool copy)
+{
+       int i;
+
+       FLAC__ASSERT(0 != object);
+       FLAC__ASSERT(0 != entry);
+       FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
+       FLAC__ASSERT(object->data.vorbis_comment.num_comments >= comment_num);
+
+       if(!FLAC__metadata_object_vorbiscomment_resize_comments(object, object->data.vorbis_comment.num_comments+1))
+               return false;
+
+       /* move all comments >= comment_num forward one space */
+       for(i = (int)object->data.vorbis_comment.num_comments-1; i > (int)comment_num; i--)
+               object->data.vorbis_comment.comments[i] = object->data.vorbis_comment.comments[i-1];
+       object->data.vorbis_comment.comments[i].length = 0;
+       object->data.vorbis_comment.comments[i].entry = 0;
+
+       return FLAC__metadata_object_vorbiscomment_set_comment(object, comment_num, entry, copy);
+}
+
+FLAC__bool FLAC__metadata_object_vorbiscomment_delete_comment(FLAC__StreamMetaData *object, unsigned comment_num)
+{
+       unsigned i;
+
+       FLAC__ASSERT(0 != object);
+       FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
+       FLAC__ASSERT(object->data.vorbis_comment.num_comments > comment_num);
+
+       /* free the comment at comment_num */
+       if(0 != object->data.vorbis_comment.comments[comment_num].entry)
+               free(object->data.vorbis_comment.comments[comment_num].entry);
+
+       /* move all comments > comment_num backward one space */
+       for(i = comment_num; i < object->data.vorbis_comment.num_comments-1; i++)
+               object->data.vorbis_comment.comments[i] = object->data.vorbis_comment.comments[i+1];
+       object->data.vorbis_comment.comments[i].length = 0;
+       object->data.vorbis_comment.comments[i].entry = 0;
+
+       return FLAC__metadata_object_vorbiscomment_resize_comments(object, object->data.vorbis_comment.num_comments-1);
+}