xref: /third_party/libsnd/src/chunk.c (revision b815c7f3)
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