shithub: opusfile

Download patch

ref: e96c5aa859f8ec04283a113d6b232fbd1ae0548f
parent: 046b089dfd1995bd97825c11d12a9755a039f931
author: Timothy B. Terriberry <tterribe@xiph.org>
date: Sat Aug 24 20:54:43 EDT 2013

Add an opus_tags_copy() API.

Also refactor the other tag functions to centralize the memory
 allocation logic, plus a few other minor cleanups.

--- a/include/opusfile.h
+++ b/include/opusfile.h
@@ -452,7 +452,7 @@
                       for validity.
    \param[in]  _data The contents of the 'comment' header packet.
    \param      _len  The number of bytes of data in the 'info' header packet.
-   \retval 0             Success.
+   \retval 0              Success.
    \retval #OP_ENOTFORMAT If the data does not start with the "OpusTags"
                            string.
    \retval #OP_EBADHEADER If the contents of the packet otherwise violate the
@@ -460,6 +460,15 @@
    \retval #OP_EFAULT     If there wasn't enough memory to store the tags.*/
 OP_WARN_UNUSED_RESULT int opus_tags_parse(OpusTags *_tags,
  const unsigned char *_data,size_t _len) OP_ARG_NONNULL(2);
+
+/**Performs a deep copy of an #OpusTags structure.
+   \param _dst The #OpusTags structure to copy into.
+               If this function fails, the contents of this structure remain
+                untouched.
+   \param _src The #OpusTags structure to copy from.
+   \retval 0          Success.
+   \retval #OP_EFAULT If there wasn't enough memory to copy the tags.*/
+int opus_tags_copy(OpusTags *_dst,const OpusTags *_src) OP_ARG_NONNULL(1);
 
 /**Initializes an #OpusTags structure.
    This should be called on a freshly allocated #OpusTags structure before
--- a/src/http.c
+++ b/src/http.c
@@ -60,7 +60,7 @@
   if(OP_UNLIKELY(len>=INT_MAX))return NULL;
   ret=(char *)_ogg_malloc(sizeof(*ret)*(len+1));
   if(OP_LIKELY(ret!=NULL)){
-    memcpy(ret,_start,sizeof(*ret)*(len));
+    ret=(char *)memcpy(ret,_start,sizeof(*ret)*(len));
     ret[len]='\0';
   }
   return ret;
--- a/src/info.c
+++ b/src/info.c
@@ -90,13 +90,48 @@
 }
 
 void opus_tags_clear(OpusTags *_tags){
-  int i;
-  for(i=_tags->comments;i-->0;)_ogg_free(_tags->user_comments[i]);
+  int ci;
+  for(ci=_tags->comments;ci-->0;)_ogg_free(_tags->user_comments[ci]);
   _ogg_free(_tags->user_comments);
   _ogg_free(_tags->comment_lengths);
   _ogg_free(_tags->vendor);
 }
 
+/*Ensure there's room for up to _ncomments comments.*/
+static int op_tags_ensure_capacity(OpusTags *_tags,size_t _ncomments){
+  char   **user_comments;
+  int     *comment_lengths;
+  size_t   size;
+  if(OP_UNLIKELY(_ncomments>=(size_t)INT_MAX))return OP_EFAULT;
+  size=sizeof(*_tags->comment_lengths)*(_ncomments+1);
+  if(size/sizeof(*_tags->comment_lengths)!=_ncomments+1)return OP_EFAULT;
+  comment_lengths=(int *)_ogg_realloc(_tags->comment_lengths,size);
+  if(OP_UNLIKELY(comment_lengths==NULL))return OP_EFAULT;
+  comment_lengths[_ncomments]=0;
+  _tags->comment_lengths=comment_lengths;
+  size=sizeof(*_tags->user_comments)*(_ncomments+1);
+  if(size/sizeof(*_tags->user_comments)!=_ncomments+1)return OP_EFAULT;
+  user_comments=(char **)_ogg_realloc(_tags->user_comments,size);
+  if(OP_UNLIKELY(user_comments==NULL))return OP_EFAULT;
+  user_comments[_ncomments]=NULL;
+  _tags->user_comments=user_comments;
+  return 0;
+}
+
+/*Duplicate a (possibly non-NUL terminated) string with a known length.*/
+static char *op_strdup_with_len(const char *_s,size_t _len){
+  size_t  size;
+  char   *ret;
+  size=sizeof(*ret)*(_len+1);
+  if(OP_UNLIKELY(size<_len))return NULL;
+  ret=(char *)_ogg_malloc(size);
+  if(OP_LIKELY(ret!=NULL)){
+    ret=(char *)memcpy(ret,_s,sizeof(*ret)*_len);
+    ret[_len]='\0';
+  }
+  return ret;
+}
+
 /*The actual implementation of opus_tags_parse().
   Unlike the public API, this function requires _tags to already be
    initialized, modifies its contents before success is guaranteed, and assumes
@@ -104,10 +139,9 @@
 static int opus_tags_parse_impl(OpusTags *_tags,
  const unsigned char *_data,size_t _len){
   opus_uint32 count;
-  size_t      size;
   size_t      len;
   int         ncomments;
-  int         i;
+  int         ci;
   len=_len;
   if(len<8)return OP_ENOTFORMAT;
   if(memcmp(_data,"OpusTags",8)!=0)return OP_ENOTFORMAT;
@@ -119,14 +153,8 @@
   len-=4;
   if(count>len)return OP_EBADHEADER;
   if(_tags!=NULL){
-    char *vendor;
-    size=count+1;
-    if(size<count)return OP_EFAULT;
-    vendor=(char *)_ogg_malloc(size);
-    if(vendor==NULL)return OP_EFAULT;
-    memcpy(vendor,_data,count);
-    vendor[count]='\0';
-    _tags->vendor=vendor;
+    _tags->vendor=op_strdup_with_len((char *)_data,count);
+    if(_tags->vendor==NULL)return OP_EFAULT;
   }
   _data+=count;
   len-=count;
@@ -139,20 +167,14 @@
   /*Check for overflow (the API limits this to an int).*/
   if(count>(opus_uint32)INT_MAX-1)return OP_EFAULT;
   if(_tags!=NULL){
-    size=sizeof(*_tags->comment_lengths)*(count+1);
-    if(size/sizeof(*_tags->comment_lengths)!=count+1)return OP_EFAULT;
-    _tags->comment_lengths=(int *)_ogg_malloc(size);
-    size=sizeof(*_tags->user_comments)*(count+1);
-    if(size/sizeof(*_tags->user_comments)!=count+1)return OP_EFAULT;
-    _tags->user_comments=(char **)_ogg_malloc(size);
-    if(_tags->comment_lengths==NULL||_tags->user_comments==NULL){
-      return OP_EFAULT;
-    }
+    int ret;
+    ret=op_tags_ensure_capacity(_tags,count);
+    if(ret<0)return ret;
   }
   ncomments=(int)count;
-  for(i=0;i<ncomments;i++){
+  for(ci=0;ci<ncomments;ci++){
     /*Check to make sure there's minimally sufficient data left in the packet.*/
-    if((size_t)(ncomments-i)>len>>2)return OP_EBADHEADER;
+    if((size_t)(ncomments-ci)>len>>2)return OP_EBADHEADER;
     count=op_parse_uint32le(_data);
     _data+=4;
     len-=4;
@@ -160,22 +182,14 @@
     /*Check for overflow (the API limits this to an int).*/
     if(count>(opus_uint32)INT_MAX)return OP_EFAULT;
     if(_tags!=NULL){
-      _tags->comment_lengths[i]=(int)count;
-      size=count+1;
-      if(size<count)return OP_EFAULT;
-      _tags->user_comments[i]=(char *)_ogg_malloc(size);
-      if(_tags->user_comments[i]==NULL)return OP_EFAULT;
-      _tags->comments=i+1;
-      memcpy(_tags->user_comments[i],_data,count);
-      _tags->user_comments[i][count]='\0';
+      _tags->user_comments[ci]=op_strdup_with_len((char *)_data,count);
+      if(_tags->user_comments[ci]==NULL)return OP_EFAULT;
+      _tags->comment_lengths[ci]=(int)count;
+      _tags->comments=ci+1;
     }
     _data+=count;
     len-=count;
   }
-  if(_tags!=NULL){
-    _tags->user_comments[ncomments]=NULL;
-    _tags->comment_lengths[ncomments]=0;
-  }
   return 0;
 }
 
@@ -192,26 +206,43 @@
   else return opus_tags_parse_impl(NULL,_data,_len);
 }
 
-/*Add room for a new comment.*/
-static int op_tags_add_prepare(OpusTags *_tags){
-  char **user_comments;
-  int   *comment_lengths;
-  int    ncomments;
-  ncomments=_tags->comments;
-  user_comments=(char **)_ogg_realloc(_tags->user_comments,
-   sizeof(*_tags->user_comments)*(ncomments+2));
-  if(OP_UNLIKELY(user_comments==NULL))return OP_EFAULT;
-  _tags->user_comments=user_comments;
-  comment_lengths=(int *)_ogg_realloc(_tags->comment_lengths,
-   sizeof(*_tags->comment_lengths)*(ncomments+2));
-  if(OP_UNLIKELY(comment_lengths==NULL))return OP_EFAULT;
-  _tags->comment_lengths=comment_lengths;
-  comment_lengths[ncomments]=comment_lengths[ncomments+1]=0;
-  /*Our caller will always set user_comments[ncomments].*/
-  user_comments[ncomments+1]=NULL;
+/*The actual implementation of opus_tags_copy().
+  Unlike the public API, this function requires _dst to already be
+   initialized, modifies its contents before success is guaranteed, and assumes
+   the caller will clear it on error.*/
+static int opus_tags_copy_impl(OpusTags *_dst,const OpusTags *_src){
+  char *vendor;
+  int   ncomments;
+  int   ret;
+  int   ci;
+  vendor=_src->vendor;
+  _dst->vendor=op_strdup_with_len(vendor,strlen(vendor));
+  if(OP_UNLIKELY(_dst->vendor==NULL))return OP_EFAULT;
+  ncomments=_src->comments;
+  ret=op_tags_ensure_capacity(_dst,ncomments);
+  if(OP_UNLIKELY(ret<0))return ret;
+  for(ci=0;ci<ncomments;ci++){
+    int len;
+    len=_src->comment_lengths[ci];
+    OP_ASSERT(len>=0);
+    _dst->user_comments[ci]=op_strdup_with_len(_src->user_comments[ci],len);
+    if(OP_UNLIKELY(_dst->user_comments[ci]==NULL))return OP_EFAULT;
+    _dst->comment_lengths[ci]=len;
+    _dst->comments=ci+1;
+  }
   return 0;
 }
 
+int opus_tags_copy(OpusTags *_dst,const OpusTags *_src){
+  OpusTags dst;
+  int      ret;
+  opus_tags_init(&dst);
+  ret=opus_tags_copy_impl(&dst,_src);
+  if(OP_UNLIKELY(ret<0))opus_tags_clear(&dst);
+  else *_dst=*&dst;
+  return 0;
+}
+
 int opus_tags_add(OpusTags *_tags,const char *_tag,const char *_value){
   char *comment;
   int   tag_len;
@@ -218,12 +249,13 @@
   int   value_len;
   int   ncomments;
   int   ret;
-  ret=op_tags_add_prepare(_tags);
+  ncomments=_tags->comments;
+  ret=op_tags_ensure_capacity(_tags,ncomments+1);
   if(OP_UNLIKELY(ret<0))return ret;
   tag_len=strlen(_tag);
   value_len=strlen(_value);
-  ncomments=_tags->comments;
   /*+2 for '=' and '\0'.*/
+  _tags->comment_lengths[ncomments]=0;
   _tags->user_comments[ncomments]=comment=
    (char *)_ogg_malloc(sizeof(*comment)*(tag_len+value_len+2));
   if(OP_UNLIKELY(comment==NULL))return OP_EFAULT;
@@ -235,19 +267,17 @@
 }
 
 int opus_tags_add_comment(OpusTags *_tags,const char *_comment){
-  char *comment;
-  int   ncomments;
-  int   comment_len;
-  int   ret;
-  ret=op_tags_add_prepare(_tags);
-  if(OP_UNLIKELY(ret<0))return ret;
-  comment_len=strlen(_comment);
+  int comment_len;
+  int ncomments;
+  int ret;
   ncomments=_tags->comments;
-  _tags->user_comments[ncomments]=comment=(char *)
-   _ogg_malloc(sizeof(*_tags->user_comments[ncomments])*(comment_len+1));
-  if(OP_UNLIKELY(comment==NULL))return OP_EFAULT;
+  ret=op_tags_ensure_capacity(_tags,ncomments+1);
+  if(OP_UNLIKELY(ret<0))return ret;
+  comment_len=(int)strlen(_comment);
+  _tags->comment_lengths[ncomments]=0;
+  _tags->user_comments[ncomments]=op_strdup_with_len(_comment,comment_len);
+  if(OP_UNLIKELY(_tags->user_comments[ncomments]==NULL))return OP_EFAULT;
   _tags->comment_lengths[ncomments]=comment_len;
-  memcpy(comment,_comment,sizeof(*comment)*(comment_len+1));
   return 0;
 }
 
@@ -300,16 +330,13 @@
 
 int opus_tags_get_track_gain(const OpusTags *_tags,int *_gain_q8){
   char **comments;
-  int   *comment_lengths;
   int    ncomments;
   int    ci;
   comments=_tags->user_comments;
-  comment_lengths=_tags->comment_lengths;
   ncomments=_tags->comments;
   /*Look for the first valid R128_TRACK_GAIN tag and use that.*/
   for(ci=0;ci<ncomments;ci++){
-    if(comment_lengths[ci]>16
-     &&op_strncasecmp(comments[ci],"R128_TRACK_GAIN=",16)==0){
+    if(opus_tagncompare("R128_TRACK_GAIN",15,comments[ci])==0){
       char       *p;
       opus_int32  gain_q8;
       int         negative;
@@ -622,7 +649,7 @@
   size_t          buf_sz;
   size_t          tag_length;
   int             ret;
-  if(op_strncasecmp(_tag,"METADATA_BLOCK_PICTURE=",23)==0)_tag+=23;
+  if(opus_tagncompare("METADATA_BLOCK_PICTURE",22,_tag)==0)_tag+=23;
   /*Figure out how much BASE64-encoded data we have.*/
   tag_length=strlen(_tag);
   if(tag_length&3)return OP_ENOTFORMAT;