Finish implementing the OpusTags API.
authorTimothy B. Terriberry <tterribe@xiph.org>
Tue, 18 Sep 2012 00:07:48 +0000 (17:07 -0700)
committerTimothy B. Terriberry <tterribe@xiph.org>
Tue, 18 Sep 2012 00:07:48 +0000 (17:07 -0700)
Apparently just declaring the functions in the header is not good
 enough.
Currently completely untested.

include/opusfile.h
src/info.c
src/internal.c
src/internal.h

index fa14030..5e88a2b 100644 (file)
@@ -281,7 +281,9 @@ void opus_tags_init(OpusTags *_tags) OP_ARG_NONNULL(1);
    \param _tags    The #OpusTags structure to add the (tag, value) pair to.
    \param _tag     A NUL-terminated, case-insensitive, ASCII string containing
                     the tag to add (without an '=' character).
-   \param _comment A NUL-terminated UTF-8 containing the corresponding value.*/
+   \param _comment A NUL-terminated UTF-8 containing the corresponding value.
+   \return 0 on success, or a negative value on failure.
+   \retval OP_EFAULT An internal memory allocation failed.*/
 int opus_tags_add(OpusTags *_tags,const char *_tag,const char *_value)
  OP_ARG_NONNULL(1) OP_ARG_NONNULL(2) OP_ARG_NONNULL(3);
 
@@ -292,7 +294,9 @@ int opus_tags_add(OpusTags *_tags,const char *_tag,const char *_value)
     directly.
    \param _tags    The #OpusTags structure to add the comment to.
    \param _comment A NUL-terminated UTF-8 string containing the comment in
-                    "TAG=value" form.*/
+                    "TAG=value" form.
+   \return 0 on success, or a negative value on failure.
+   \retval OP_EFAULT An internal memory allocation failed.*/
 int opus_tags_add_comment(OpusTags *_tags,const char *_comment)
  OP_ARG_NONNULL(1) OP_ARG_NONNULL(2);
 
index c3f9f8b..396ebfa 100644 (file)
@@ -118,13 +118,13 @@ int opus_tags_parse_impl(OpusTags *_tags,
   /*Check to make sure there's minimally sufficient data left in the packet.*/
   if(count>len>>2)return OP_EBADHEADER;
   /*Check for overflow (the API limits this to an int).*/
-  if(count>(opus_uint32)INT_MAX)return OP_EFAULT;
+  if(count>(opus_uint32)INT_MAX-1)return OP_EFAULT;
   if(_tags!=NULL){
-    size=sizeof(*_tags->comment_lengths)*count;
-    if(size/sizeof(*_tags->comment_lengths)!=count)return OP_EFAULT;
+    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;
-    if(size/sizeof(*_tags->user_comments)!=count)return OP_EFAULT;
+    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;
@@ -153,6 +153,8 @@ int opus_tags_parse_impl(OpusTags *_tags,
     _data+=count;
     len-=count;
   }
+  _tags->user_comments[ncomments]=NULL;
+  _tags->comment_lengths[ncomments]=0;
   return 0;
 }
 
@@ -168,3 +170,104 @@ int opus_tags_parse(OpusTags *_tags,const unsigned char *_data,size_t _len){
   }
   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=_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=_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;
+  return 0;
+}
+
+int opus_tags_add(OpusTags *_tags,const char *_tag,const char *_value){
+  char *comment;
+  int   tag_len;
+  int   value_len;
+  int   ncomments;
+  int   ret;
+  ret=op_tags_add_prepare(_tags);
+  if(OP_UNLIKELY(ret<0))return ret;
+  tag_len=strlen(_tag);
+  value_len=strlen(_value);
+  ncomments=_tags->comments;
+  /*+2 for '=' and '\0'.*/
+  _tags->user_comments[ncomments]=comment=
+   (char *)_ogg_malloc(sizeof(*comment)*(tag_len+value_len+2));
+  if(OP_UNLIKELY(comment==NULL))return OP_EFAULT;
+  _tags->comment_lengths[ncomments]=tag_len+value_len+1;
+  memcpy(comment,_tag,sizeof(*comment)*tag_len);
+  comment[tag_len]='=';
+  memcpy(comment+tag_len+1,_value,sizeof(*comment)*(value_len+1));
+  return 0;
+}
+
+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);
+  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;
+  _tags->comment_lengths[ncomments]=comment_len;
+  memcpy(comment,_comment,sizeof(*comment)*(comment_len+1));
+  return 0;
+}
+
+/*Is _a a "tag=value" comment whose tag matches _b?
+  0 if it is, a non-zero value otherwise.*/
+static int op_tagcompare(const char *_a,const char *_b,int _n){
+  return op_strncasecmp(_a,_b,_n)||_a[_n]!='=';
+}
+
+const char *opus_tags_query(const OpusTags *_tags,const char *_tag,int _count){
+  char **user_comments;
+  int    tag_len;
+  int    found;
+  int    ncomments;
+  int    ci;
+  tag_len=strlen(_tag);
+  ncomments=_tags->comments;
+  user_comments=_tags->user_comments;
+  found=0;
+  for(ci=0;ci<ncomments;ci++){
+    if(!op_tagcompare(user_comments[ci],_tag,tag_len)){
+      /*We return a pointer to the data, not a copy.*/
+      if(_count==found++)return user_comments[ci]+tag_len+1;
+    }
+  }
+  /*Didn't find anything.*/
+  return NULL;
+}
+
+int opus_tags_query_count(const OpusTags *_tags,const char *_tag){
+  char **user_comments;
+  int    tag_len;
+  int    found;
+  int    ncomments;
+  int    ci;
+  tag_len=strlen(_tag);
+  ncomments=_tags->comments;
+  user_comments=_tags->user_comments;
+  found=0;
+  for(ci=0;ci<ncomments;ci++){
+    if(!op_tagcompare(user_comments[ci],_tag,tag_len))found++;
+  }
+  return found;
+}
index f97f7ef..103f53b 100644 (file)
@@ -7,3 +7,21 @@ void op_fatal_impl(const char *_str,const char *_file,int _line){
   abort();
 }
 #endif
+
+/*A version of strncasecmp() that is guaranteed to only ignore the case of
+   ASCII characters.*/
+int op_strncasecmp(const char *_a,const char *_b,int _n){
+  int i;
+  for(i=0;i<_n;i++){
+    int a;
+    int b;
+    int d;
+    a=_a[i];
+    b=_b[i];
+    if(a>='a'&&a<='z')a-='a'-'A';
+    if(b>='a'&&b<='z')b-='a'-'A';
+    d=a-b;
+    if(d)return d;
+  }
+  return 0;
+}
index 778f8c6..2cffdbc 100644 (file)
@@ -182,4 +182,6 @@ struct OggOpusFile{
 #endif
 };
 
+int op_strncasecmp(const char *_a,const char *_b,int _n);
+
 #endif