1/* 2** Copyright (C) 2008-2016 Erik de Castro Lopo <erikd@mega-nerd.com> 3** Copyright (C) 2012 IOhannes m zmoelnig, IEM <zmoelnig@iem.at> 4** 5** This program is free software; you can redistribute it and/or modify 6** it under the terms of the GNU Lesser General Public License as published by 7** the Free Software Foundation; either version 2.1 of the License, or 8** (at your option) any later version. 9** 10** This program is distributed in the hope that it will be useful, 11** but WITHOUT ANY WARRANTY; without even the implied warranty of 12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13** GNU Lesser General Public License for more details. 14** 15** You should have received a copy of the GNU Lesser General Public License 16** along with this program; if not, write to the Free Software 17** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18*/ 19 20#include "sfconfig.h" 21 22#include <stdlib.h> 23#include <string.h> 24 25#include "sndfile.h" 26#include "sfendian.h" 27#include "common.h" 28 29static int64_t 30hash_of_str (const char * str) 31{ int64_t marker = 0 ; 32 int k ; 33 34 for (k = 0 ; str [k] ; k++) 35 marker = marker * 0x7f + ((const uint8_t *) str) [k] ; 36 37 return marker ; 38} /* hash_of_str */ 39 40SF_CHUNK_ITERATOR * 41psf_get_chunk_iterator (SF_PRIVATE * psf, const char * marker_str) 42{ const READ_CHUNKS * pchk = &psf->rchunks ; 43 int idx ; 44 45 if (marker_str) 46 idx = psf_find_read_chunk_str (pchk, marker_str) ; 47 else 48 idx = pchk->used > 0 ? 0 : -1 ; 49 50 if (idx < 0) 51 return NULL ; 52 53 if (psf->iterator == NULL) 54 { psf->iterator = calloc (1, sizeof (SF_CHUNK_ITERATOR)) ; 55 if (psf->iterator == NULL) 56 return NULL ; 57 } ; 58 59 psf->iterator->sndfile = (SNDFILE *) psf ; 60 61 if (marker_str) 62 { int64_t hash ; 63 size_t marker_len ; 64 union 65 { uint32_t marker ; 66 char str [5] ; 67 } u ; 68 69 snprintf (u.str, sizeof (u.str), "%s", marker_str) ; 70 71 marker_len = strlen (marker_str) ; 72 if (marker_len > 64) 73 marker_len = 64 ; 74 75 hash = marker_len > 4 ? hash_of_str (marker_str) : u.marker ; 76 77 memcpy (psf->iterator->id, marker_str, marker_len) ; 78 psf->iterator->id_size = (unsigned) marker_len ; 79 psf->iterator->hash = hash ; 80 } 81 82 psf->iterator->current = idx ; 83 84 return psf->iterator ; 85} /* psf_get_chunk_iterator */ 86 87SF_CHUNK_ITERATOR * 88psf_next_chunk_iterator (const READ_CHUNKS * pchk , SF_CHUNK_ITERATOR * iterator) 89{ uint64_t hash = iterator->hash ; 90 uint32_t k ; 91 92 iterator->current++ ; 93 94 if (hash) 95 { for (k = iterator->current ; k < pchk->used ; k++) 96 if (pchk->chunks [k].hash == hash) 97 { iterator->current = k ; 98 return iterator ; 99 } 100 } 101 else if (iterator->current < pchk->used) 102 return iterator ; 103 104 /* No match, clear iterator and return NULL */ 105 memset (iterator, 0, sizeof (*iterator)) ; 106 return NULL ; 107} /* psf_next_chunk_iterator */ 108 109static int 110psf_store_read_chunk (READ_CHUNKS * pchk, const READ_CHUNK * rchunk) 111{ if (pchk->count == 0) 112 { pchk->used = 0 ; 113 pchk->count = 20 ; 114 pchk->chunks = calloc (pchk->count, sizeof (READ_CHUNK)) ; 115 if (!pchk->chunks) 116 { return SFE_MALLOC_FAILED ; 117 } ; 118 } 119 else if (pchk->used > pchk->count) 120 return SFE_INTERNAL ; 121 else if (pchk->used == pchk->count) 122 { READ_CHUNK * old_ptr = pchk->chunks ; 123 int new_count = 3 * (pchk->count + 1) / 2 ; 124 125 pchk->chunks = realloc (old_ptr, new_count * sizeof (READ_CHUNK)) ; 126 if (pchk->chunks == NULL) 127 { pchk->chunks = old_ptr ; 128 return SFE_MALLOC_FAILED ; 129 } ; 130 pchk->count = new_count ; 131 } ; 132 133 pchk->chunks [pchk->used] = *rchunk ; 134 135 pchk->used ++ ; 136 137 return SFE_NO_ERROR ; 138} /* psf_store_read_chunk */ 139 140int 141psf_store_read_chunk_u32 (READ_CHUNKS * pchk, uint32_t marker, sf_count_t offset, uint32_t len) 142{ READ_CHUNK rchunk ; 143 144 memset (&rchunk, 0, sizeof (rchunk)) ; 145 146 rchunk.hash = marker ; 147 rchunk.mark32 = marker ; 148 rchunk.offset = offset ; 149 rchunk.len = len ; 150 151 rchunk.id_size = 4 ; 152 memcpy (rchunk.id, &marker, rchunk.id_size) ; 153 154 return psf_store_read_chunk (pchk, &rchunk) ; 155} /* psf_store_read_chunk_u32 */ 156 157int 158psf_find_read_chunk_str (const READ_CHUNKS * pchk, const char * marker_str) 159{ uint64_t hash ; 160 uint32_t k ; 161 union 162 { uint32_t marker ; 163 char str [5] ; 164 } u ; 165 166 snprintf (u.str, sizeof (u.str), "%s", marker_str) ; 167 168 hash = strlen (marker_str) > 4 ? hash_of_str (marker_str) : u.marker ; 169 170 for (k = 0 ; k < pchk->used ; k++) 171 if (pchk->chunks [k].hash == hash) 172 return k ; 173 174 return -1 ; 175} /* psf_find_read_chunk_str */ 176 177int 178psf_find_read_chunk_m32 (const READ_CHUNKS * pchk, uint32_t marker) 179{ uint32_t k ; 180 181 for (k = 0 ; k < pchk->used ; k++) 182 if (pchk->chunks [k].mark32 == marker) 183 return k ; 184 185 return -1 ; 186} /* psf_find_read_chunk_m32 */ 187int 188psf_find_read_chunk_iterator (const READ_CHUNKS * pchk, const SF_CHUNK_ITERATOR * marker) 189{ if (marker->current < pchk->used) 190 return marker->current ; 191 192 return -1 ; 193} /* psf_find_read_chunk_iterator */ 194 195int 196psf_store_read_chunk_str (READ_CHUNKS * pchk, const char * marker_str, sf_count_t offset, uint32_t len) 197{ READ_CHUNK rchunk ; 198 union 199 { uint32_t marker ; 200 char str [5] ; 201 } u ; 202 size_t marker_len ; 203 204 memset (&rchunk, 0, sizeof (rchunk)) ; 205 snprintf (u.str, sizeof (u.str), "%s", marker_str) ; 206 207 marker_len = strlen (marker_str) ; 208 209 rchunk.hash = marker_len > 4 ? hash_of_str (marker_str) : u.marker ; 210 rchunk.mark32 = u.marker ; 211 rchunk.offset = offset ; 212 rchunk.len = len ; 213 214 rchunk.id_size = marker_len > 64 ? 64 : (unsigned) marker_len ; 215 memcpy (rchunk.id, marker_str, rchunk.id_size) ; 216 217 return psf_store_read_chunk (pchk, &rchunk) ; 218} /* psf_store_read_chunk_str */ 219 220int 221psf_save_write_chunk (WRITE_CHUNKS * pchk, const SF_CHUNK_INFO * chunk_info) 222{ union 223 { uint32_t marker ; 224 char str [5] ; 225 /* Update snprintf() format string below when changing this */ 226 } u ; 227 uint32_t len ; 228 229 if (pchk->count == 0) 230 { pchk->used = 0 ; 231 pchk->count = 20 ; 232 pchk->chunks = calloc (pchk->count, sizeof (WRITE_CHUNK)) ; 233 if (!pchk->chunks) 234 { return SFE_MALLOC_FAILED ; 235 } ; 236 } 237 else if (pchk->used >= pchk->count) 238 { WRITE_CHUNK * old_ptr = pchk->chunks ; 239 int new_count = 3 * (pchk->count + 1) / 2 ; 240 241 pchk->chunks = realloc (old_ptr, new_count * sizeof (WRITE_CHUNK)) ; 242 if (pchk->chunks == NULL) 243 { pchk->chunks = old_ptr ; 244 return SFE_MALLOC_FAILED ; 245 } ; 246 } ; 247 248 len = chunk_info->datalen ; 249 while (len & 3) len ++ ; 250 251 snprintf (u.str, sizeof (u.str), "%.4s", chunk_info->id) ; 252 253 pchk->chunks [pchk->used].hash = strlen (chunk_info->id) > 4 ? hash_of_str (chunk_info->id) : u.marker ; 254 pchk->chunks [pchk->used].mark32 = u.marker ; 255 pchk->chunks [pchk->used].len = len ; 256 pchk->chunks [pchk->used].data = psf_memdup (chunk_info->data, chunk_info->datalen) ; 257 258 pchk->used ++ ; 259 260 return SFE_NO_ERROR ; 261} /* psf_save_write_chunk */ 262 263