| /* | |
| ** Copyright (C) 2008-2016 Erik de Castro Lopo <erikd@mega-nerd.com> | |
| ** Copyright (C) 2012 IOhannes m zmoelnig, IEM <zmoelnig@iem.at> | |
| ** | |
| ** This program is free software; you can redistribute it and/or modify | |
| ** it under the terms of the GNU Lesser General Public License as published by | |
| ** the Free Software Foundation; either version 2.1 of the License, or | |
| ** (at your option) any later version. | |
| ** | |
| ** This program 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 Lesser General Public License for more details. | |
| ** | |
| ** You should have received a copy of the GNU Lesser General Public License | |
| ** along with this program; if not, write to the Free Software | |
| ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
| */ | |
| #include "sfconfig.h" | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include "sndfile.h" | |
| #include "sfendian.h" | |
| #include "common.h" | |
| static int64_t | |
| hash_of_str (const char * str) | |
| { int64_t marker = 0 ; | |
| int k ; | |
| for (k = 0 ; str [k] ; k++) | |
| marker = marker * 0x7f + ((const uint8_t *) str) [k] ; | |
| return marker ; | |
| } /* hash_of_str */ | |
| SF_CHUNK_ITERATOR * | |
| psf_get_chunk_iterator (SF_PRIVATE * psf, const char * marker_str) | |
| { const READ_CHUNKS * pchk = &psf->rchunks ; | |
| int idx ; | |
| if (marker_str) | |
| idx = psf_find_read_chunk_str (pchk, marker_str) ; | |
| else | |
| idx = pchk->used > 0 ? 0 : -1 ; | |
| if (idx < 0) | |
| return NULL ; | |
| if (psf->iterator == NULL) | |
| { psf->iterator = calloc (1, sizeof (SF_CHUNK_ITERATOR)) ; | |
| if (psf->iterator == NULL) | |
| return NULL ; | |
| } ; | |
| psf->iterator->sndfile = (SNDFILE *) psf ; | |
| if (marker_str) | |
| { int64_t hash ; | |
| size_t marker_len ; | |
| union | |
| { uint32_t marker ; | |
| char str [5] ; | |
| } u ; | |
| snprintf (u.str, sizeof (u.str), "%s", marker_str) ; | |
| marker_len = strlen (marker_str) ; | |
| if (marker_len > 64) | |
| marker_len = 64 ; | |
| hash = marker_len > 4 ? hash_of_str (marker_str) : u.marker ; | |
| memcpy (psf->iterator->id, marker_str, marker_len) ; | |
| psf->iterator->id_size = (unsigned) marker_len ; | |
| psf->iterator->hash = hash ; | |
| } | |
| psf->iterator->current = idx ; | |
| return psf->iterator ; | |
| } /* psf_get_chunk_iterator */ | |
| SF_CHUNK_ITERATOR * | |
| psf_next_chunk_iterator (const READ_CHUNKS * pchk , SF_CHUNK_ITERATOR * iterator) | |
| { uint64_t hash = iterator->hash ; | |
| uint32_t k ; | |
| iterator->current++ ; | |
| if (hash) | |
| { for (k = iterator->current ; k < pchk->used ; k++) | |
| if (pchk->chunks [k].hash == hash) | |
| { iterator->current = k ; | |
| return iterator ; | |
| } | |
| } | |
| else if (iterator->current < pchk->used) | |
| return iterator ; | |
| /* No match, clear iterator and return NULL */ | |
| memset (iterator, 0, sizeof (*iterator)) ; | |
| return NULL ; | |
| } /* psf_next_chunk_iterator */ | |
| static int | |
| psf_store_read_chunk (READ_CHUNKS * pchk, const READ_CHUNK * rchunk) | |
| { if (pchk->count == 0) | |
| { pchk->used = 0 ; | |
| pchk->count = 20 ; | |
| pchk->chunks = calloc (pchk->count, sizeof (READ_CHUNK)) ; | |
| if (!pchk->chunks) | |
| { return SFE_MALLOC_FAILED ; | |
| } ; | |
| } | |
| else if (pchk->used > pchk->count) | |
| return SFE_INTERNAL ; | |
| else if (pchk->used == pchk->count) | |
| { READ_CHUNK * old_ptr = pchk->chunks ; | |
| int new_count = 3 * (pchk->count + 1) / 2 ; | |
| READ_CHUNK * new_chunks = realloc (old_ptr, new_count * sizeof (READ_CHUNK)) ; | |
| if (new_chunks == NULL) | |
| { | |
| return SFE_MALLOC_FAILED ; | |
| } else { | |
| pchk->chunks = new_chunks; | |
| } ; | |
| pchk->count = new_count ; | |
| } ; | |
| pchk->chunks [pchk->used] = *rchunk ; | |
| pchk->used ++ ; | |
| return SFE_NO_ERROR ; | |
| } /* psf_store_read_chunk */ | |
| int | |
| psf_store_read_chunk_u32 (READ_CHUNKS * pchk, uint32_t marker, sf_count_t offset, uint32_t len) | |
| { READ_CHUNK rchunk ; | |
| memset (&rchunk, 0, sizeof (rchunk)) ; | |
| rchunk.hash = marker ; | |
| rchunk.mark32 = marker ; | |
| rchunk.offset = offset ; | |
| rchunk.len = len ; | |
| rchunk.id_size = 4 ; | |
| memcpy (rchunk.id, &marker, rchunk.id_size) ; | |
| return psf_store_read_chunk (pchk, &rchunk) ; | |
| } /* psf_store_read_chunk_u32 */ | |
| int | |
| psf_find_read_chunk_str (const READ_CHUNKS * pchk, const char * marker_str) | |
| { uint64_t hash ; | |
| uint32_t k ; | |
| union | |
| { uint32_t marker ; | |
| char str [5] ; | |
| } u ; | |
| snprintf (u.str, sizeof (u.str), "%s", marker_str) ; | |
| hash = strlen (marker_str) > 4 ? hash_of_str (marker_str) : u.marker ; | |
| for (k = 0 ; k < pchk->used ; k++) | |
| if (pchk->chunks [k].hash == hash) | |
| return k ; | |
| return -1 ; | |
| } /* psf_find_read_chunk_str */ | |
| int | |
| psf_find_read_chunk_m32 (const READ_CHUNKS * pchk, uint32_t marker) | |
| { uint32_t k ; | |
| for (k = 0 ; k < pchk->used ; k++) | |
| if (pchk->chunks [k].mark32 == marker) | |
| return k ; | |
| return -1 ; | |
| } /* psf_find_read_chunk_m32 */ | |
| int | |
| psf_find_read_chunk_iterator (const READ_CHUNKS * pchk, const SF_CHUNK_ITERATOR * marker) | |
| { if (marker->current < pchk->used) | |
| return marker->current ; | |
| return -1 ; | |
| } /* psf_find_read_chunk_iterator */ | |
| int | |
| psf_store_read_chunk_str (READ_CHUNKS * pchk, const char * marker_str, sf_count_t offset, uint32_t len) | |
| { READ_CHUNK rchunk ; | |
| union | |
| { uint32_t marker ; | |
| char str [5] ; | |
| } u ; | |
| size_t marker_len ; | |
| memset (&rchunk, 0, sizeof (rchunk)) ; | |
| snprintf (u.str, sizeof (u.str), "%s", marker_str) ; | |
| marker_len = strlen (marker_str) ; | |
| rchunk.hash = marker_len > 4 ? hash_of_str (marker_str) : u.marker ; | |
| rchunk.mark32 = u.marker ; | |
| rchunk.offset = offset ; | |
| rchunk.len = len ; | |
| rchunk.id_size = marker_len > 64 ? 64 : (unsigned) marker_len ; | |
| memcpy (rchunk.id, marker_str, rchunk.id_size) ; | |
| return psf_store_read_chunk (pchk, &rchunk) ; | |
| } /* psf_store_read_chunk_str */ | |
| int | |
| psf_save_write_chunk (WRITE_CHUNKS * pchk, const SF_CHUNK_INFO * chunk_info) | |
| { union | |
| { uint32_t marker ; | |
| char str [5] ; | |
| /* Update snprintf() format string below when changing this */ | |
| } u ; | |
| uint32_t len ; | |
| if (pchk->count == 0) | |
| { pchk->used = 0 ; | |
| pchk->count = 20 ; | |
| pchk->chunks = calloc (pchk->count, sizeof (WRITE_CHUNK)) ; | |
| if (!pchk->chunks) | |
| { return SFE_MALLOC_FAILED ; | |
| } ; | |
| } | |
| else if (pchk->used >= pchk->count) | |
| { WRITE_CHUNK * old_ptr = pchk->chunks ; | |
| int new_count = 3 * (pchk->count + 1) / 2 ; | |
| WRITE_CHUNK * new_chunks = realloc (old_ptr, new_count * sizeof (WRITE_CHUNK)) ; | |
| if (new_chunks == NULL) | |
| { | |
| return SFE_MALLOC_FAILED ; | |
| } else { | |
| pchk->chunks = new_chunks; | |
| } ; | |
| } ; | |
| len = chunk_info->datalen ; | |
| while (len & 3) len ++ ; | |
| snprintf (u.str, sizeof (u.str), "%.4s", chunk_info->id) ; | |
| pchk->chunks [pchk->used].hash = strlen (chunk_info->id) > 4 ? hash_of_str (chunk_info->id) : u.marker ; | |
| pchk->chunks [pchk->used].mark32 = u.marker ; | |
| pchk->chunks [pchk->used].len = len ; | |
| pchk->chunks [pchk->used].data = psf_memdup (chunk_info->data, chunk_info->datalen) ; | |
| pchk->used ++ ; | |
| return SFE_NO_ERROR ; | |
| } /* psf_save_write_chunk */ | |