Line | Count | Source |
1 | | /******************************************************************** |
2 | | * * |
3 | | * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE. * |
4 | | * * |
5 | | * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * |
6 | | * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * |
7 | | * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * |
8 | | * * |
9 | | * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2002 * |
10 | | * BY THE Xiph.Org FOUNDATION http://www.xiph.org/ * |
11 | | * * |
12 | | ******************************************************************** |
13 | | |
14 | | function: PCM data vector blocking, windowing and dis/reassembly |
15 | | |
16 | | ********************************************************************/ |
17 | | |
18 | | #include <stdio.h> |
19 | | #include <stdlib.h> |
20 | | #include <string.h> |
21 | | #include <ogg/ogg.h> |
22 | | #include "ivorbiscodec.h" |
23 | | #include "codec_internal.h" |
24 | | |
25 | | #include "window.h" |
26 | | #include "registry.h" |
27 | | #include "misc.h" |
28 | | |
29 | 9.94k | static int ilog(unsigned int v){ |
30 | 9.94k | int ret=0; |
31 | 9.94k | if(v)--v; |
32 | 10.8k | while(v){ |
33 | 872 | ret++; |
34 | 872 | v>>=1; |
35 | 872 | } |
36 | 9.94k | return(ret); |
37 | 9.94k | } |
38 | | |
39 | | /* pcm accumulator examples (not exhaustive): |
40 | | |
41 | | <-------------- lW ----------------> |
42 | | <--------------- W ----------------> |
43 | | : .....|..... _______________ | |
44 | | : .''' | '''_--- | |\ | |
45 | | :.....''' |_____--- '''......| | \_______| |
46 | | :.................|__________________|_______|__|______| |
47 | | |<------ Sl ------>| > Sr < |endW |
48 | | |beginSl |endSl | |endSr |
49 | | |beginW |endlW |beginSr |
50 | | |
51 | | |
52 | | |< lW >| |
53 | | <--------------- W ----------------> |
54 | | | | .. ______________ | |
55 | | | | ' `/ | ---_ | |
56 | | |___.'___/`. | ---_____| |
57 | | |_______|__|_______|_________________| |
58 | | | >|Sl|< |<------ Sr ----->|endW |
59 | | | | |endSl |beginSr |endSr |
60 | | |beginW | |endlW |
61 | | mult[0] |beginSl mult[n] |
62 | | |
63 | | <-------------- lW -----------------> |
64 | | |<--W-->| |
65 | | : .............. ___ | | |
66 | | : .''' |`/ \ | | |
67 | | :.....''' |/`....\|...| |
68 | | :.........................|___|___|___| |
69 | | |Sl |Sr |endW |
70 | | | | |endSr |
71 | | | |beginSr |
72 | | | |endSl |
73 | | |beginSl |
74 | | |beginW |
75 | | */ |
76 | | |
77 | | /* block abstraction setup *********************************************/ |
78 | | |
79 | | #ifndef WORD_ALIGN |
80 | 11.3M | #define WORD_ALIGN 8 |
81 | | #endif |
82 | | |
83 | 9.81k | int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb){ |
84 | 9.81k | memset(vb,0,sizeof(*vb)); |
85 | 9.81k | vb->vd=v; |
86 | 9.81k | vb->localalloc=0; |
87 | 9.81k | vb->localstore=NULL; |
88 | | |
89 | 9.81k | return(0); |
90 | 9.81k | } |
91 | | |
92 | 5.69M | void *_vorbis_block_alloc(vorbis_block *vb,long bytes){ |
93 | 5.69M | bytes=(bytes+(WORD_ALIGN-1)) & ~(WORD_ALIGN-1); |
94 | 5.69M | if(bytes+vb->localtop>vb->localalloc){ |
95 | | /* can't just _ogg_realloc... there are outstanding pointers */ |
96 | 496k | if(vb->localstore){ |
97 | 489k | struct alloc_chain *link=(struct alloc_chain *)_ogg_malloc(sizeof(*link)); |
98 | 489k | vb->totaluse+=vb->localtop; |
99 | 489k | link->next=vb->reap; |
100 | 489k | link->ptr=vb->localstore; |
101 | 489k | vb->reap=link; |
102 | 489k | } |
103 | | /* highly conservative */ |
104 | 496k | vb->localalloc=bytes; |
105 | 496k | vb->localstore=_ogg_malloc(vb->localalloc); |
106 | 496k | vb->localtop=0; |
107 | 496k | } |
108 | 5.69M | { |
109 | 5.69M | void *ret=(void *)(((char *)vb->localstore)+vb->localtop); |
110 | 5.69M | vb->localtop+=bytes; |
111 | 5.69M | return ret; |
112 | 5.69M | } |
113 | 5.69M | } |
114 | | |
115 | | /* reap the chain, pull the ripcord */ |
116 | 140k | void _vorbis_block_ripcord(vorbis_block *vb){ |
117 | | /* reap the chain */ |
118 | 140k | struct alloc_chain *reap=vb->reap; |
119 | 630k | while(reap){ |
120 | 489k | struct alloc_chain *next=reap->next; |
121 | 489k | _ogg_free(reap->ptr); |
122 | 489k | memset(reap,0,sizeof(*reap)); |
123 | 489k | _ogg_free(reap); |
124 | 489k | reap=next; |
125 | 489k | } |
126 | | /* consolidate storage */ |
127 | 140k | if(vb->totaluse){ |
128 | 7.76k | vb->localstore=_ogg_realloc(vb->localstore,vb->totaluse+vb->localalloc); |
129 | 7.76k | vb->localalloc+=vb->totaluse; |
130 | 7.76k | vb->totaluse=0; |
131 | 7.76k | } |
132 | | |
133 | | /* pull the ripcord */ |
134 | 140k | vb->localtop=0; |
135 | 140k | vb->reap=NULL; |
136 | 140k | } |
137 | | |
138 | 12.2k | int vorbis_block_clear(vorbis_block *vb){ |
139 | 12.2k | _vorbis_block_ripcord(vb); |
140 | 12.2k | if(vb->localstore)_ogg_free(vb->localstore); |
141 | | |
142 | 12.2k | memset(vb,0,sizeof(*vb)); |
143 | 12.2k | return(0); |
144 | 12.2k | } |
145 | | |
146 | 11.1k | static int _vds_init(vorbis_dsp_state *v,vorbis_info *vi){ |
147 | 11.1k | int i; |
148 | 11.1k | codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; |
149 | 11.1k | private_state *b=NULL; |
150 | | |
151 | 11.1k | if(ci==NULL) return 1; |
152 | | |
153 | 9.94k | memset(v,0,sizeof(*v)); |
154 | 9.94k | b=(private_state *)(v->backend_state=_ogg_calloc(1,sizeof(*b))); |
155 | | |
156 | 9.94k | v->vi=vi; |
157 | 9.94k | b->modebits=ilog(ci->modes); |
158 | | |
159 | | /* Vorbis I uses only window type 0 */ |
160 | 9.94k | b->window[0]=_vorbis_window(0,ci->blocksizes[0]/2); |
161 | 9.94k | b->window[1]=_vorbis_window(0,ci->blocksizes[1]/2); |
162 | | |
163 | | /* finish the codebooks */ |
164 | 9.94k | if(!ci->fullbooks){ |
165 | 9.94k | ci->fullbooks=(codebook *)_ogg_calloc(ci->books,sizeof(*ci->fullbooks)); |
166 | 23.3k | for(i=0;i<ci->books;i++){ |
167 | 13.4k | if(ci->book_param[i]==NULL) |
168 | 0 | goto abort_books; |
169 | 13.4k | if(vorbis_book_init_decode(ci->fullbooks+i,ci->book_param[i])) |
170 | 131 | goto abort_books; |
171 | | /* decode codebooks are now standalone after init */ |
172 | 13.3k | vorbis_staticbook_destroy(ci->book_param[i]); |
173 | 13.3k | ci->book_param[i]=NULL; |
174 | 13.3k | } |
175 | 9.94k | } |
176 | | |
177 | 9.81k | v->pcm_storage=ci->blocksizes[1]; |
178 | 9.81k | v->pcm=(ogg_int32_t **)_ogg_malloc(vi->channels*sizeof(*v->pcm)); |
179 | 9.81k | v->pcmret=(ogg_int32_t **)_ogg_malloc(vi->channels*sizeof(*v->pcmret)); |
180 | 760k | for(i=0;i<vi->channels;i++) |
181 | 750k | v->pcm[i]=(ogg_int32_t *)_ogg_calloc(v->pcm_storage,sizeof(*v->pcm[i])); |
182 | | |
183 | | /* all 1 (large block) or 0 (small block) */ |
184 | | /* explicitly set for the sake of clarity */ |
185 | 9.81k | v->lW=0; /* previous window size */ |
186 | 9.81k | v->W=0; /* current window size */ |
187 | | |
188 | | /* initialize all the mapping/backend lookups */ |
189 | 9.81k | b->mode=(vorbis_look_mapping **)_ogg_calloc(ci->modes,sizeof(*b->mode)); |
190 | 20.6k | for(i=0;i<ci->modes;i++){ |
191 | 10.8k | int mapnum=ci->mode_param[i]->mapping; |
192 | 10.8k | int maptype=ci->map_type[mapnum]; |
193 | 10.8k | b->mode[i]=_mapping_P[maptype]->look(v,ci->mode_param[i], |
194 | 10.8k | ci->map_param[mapnum]); |
195 | 10.8k | } |
196 | 9.81k | return 0; |
197 | 131 | abort_books: |
198 | 1.80k | for(i=0;i<ci->books;i++){ |
199 | 1.67k | if(ci->book_param[i]!=NULL){ |
200 | 959 | vorbis_staticbook_destroy(ci->book_param[i]); |
201 | 959 | ci->book_param[i]=NULL; |
202 | 959 | } |
203 | 1.67k | } |
204 | 131 | vorbis_dsp_clear(v); |
205 | 131 | return -1; |
206 | 9.94k | } |
207 | | |
208 | 9.81k | int vorbis_synthesis_restart(vorbis_dsp_state *v){ |
209 | 9.81k | vorbis_info *vi=v->vi; |
210 | 9.81k | codec_setup_info *ci; |
211 | | |
212 | 9.81k | if(!v->backend_state)return -1; |
213 | 9.81k | if(!vi)return -1; |
214 | 9.81k | ci=vi->codec_setup; |
215 | 9.81k | if(!ci)return -1; |
216 | | |
217 | 9.81k | v->centerW=ci->blocksizes[1]/2; |
218 | 9.81k | v->pcm_current=v->centerW; |
219 | | |
220 | 9.81k | v->pcm_returned=-1; |
221 | 9.81k | v->granulepos=-1; |
222 | 9.81k | v->sequence=-1; |
223 | 9.81k | ((private_state *)(v->backend_state))->sample_count=-1; |
224 | | |
225 | 9.81k | return(0); |
226 | 9.81k | } |
227 | | |
228 | 11.1k | int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi){ |
229 | 11.1k | if(_vds_init(v,vi))return 1; |
230 | 9.81k | vorbis_synthesis_restart(v); |
231 | | |
232 | 9.81k | return 0; |
233 | 11.1k | } |
234 | | |
235 | 12.3k | void vorbis_dsp_clear(vorbis_dsp_state *v){ |
236 | 12.3k | int i; |
237 | 12.3k | if(v){ |
238 | 12.3k | vorbis_info *vi=v->vi; |
239 | 12.3k | codec_setup_info *ci=(codec_setup_info *)(vi?vi->codec_setup:NULL); |
240 | 12.3k | private_state *b=(private_state *)v->backend_state; |
241 | | |
242 | 12.3k | if(v->pcm){ |
243 | 760k | for(i=0;i<vi->channels;i++) |
244 | 750k | if(v->pcm[i])_ogg_free(v->pcm[i]); |
245 | 9.81k | _ogg_free(v->pcm); |
246 | 9.81k | if(v->pcmret)_ogg_free(v->pcmret); |
247 | 9.81k | } |
248 | | |
249 | | /* free mode lookups; these are actually vorbis_look_mapping structs */ |
250 | 12.3k | if(ci){ |
251 | 21.1k | for(i=0;i<ci->modes;i++){ |
252 | 11.1k | int mapnum=ci->mode_param[i]->mapping; |
253 | 11.1k | int maptype=ci->map_type[mapnum]; |
254 | 11.1k | if(b && b->mode)_mapping_P[maptype]->free_look(b->mode[i]); |
255 | 11.1k | } |
256 | 9.94k | } |
257 | | |
258 | 12.3k | if(b){ |
259 | 9.94k | if(b->mode)_ogg_free(b->mode); |
260 | 9.94k | _ogg_free(b); |
261 | 9.94k | } |
262 | | |
263 | 12.3k | memset(v,0,sizeof(*v)); |
264 | 12.3k | } |
265 | 12.3k | } |
266 | | |
267 | | /* Unlike in analysis, the window is only partially applied for each |
268 | | block. The time domain envelope is not yet handled at the point of |
269 | | calling (as it relies on the previous block). */ |
270 | | |
271 | 55.8k | int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ |
272 | 55.8k | vorbis_info *vi=v->vi; |
273 | 55.8k | codec_setup_info *ci=(codec_setup_info *)vi->codec_setup; |
274 | 55.8k | private_state *b=v->backend_state; |
275 | 55.8k | int i,j; |
276 | | |
277 | 55.8k | if(v->pcm_current>v->pcm_returned && v->pcm_returned!=-1)return(OV_EINVAL); |
278 | | |
279 | 54.2k | v->lW=v->W; |
280 | 54.2k | v->W=vb->W; |
281 | 54.2k | v->nW=-1; |
282 | | |
283 | 54.2k | if((v->sequence==-1)|| |
284 | 47.7k | (v->sequence+1 != vb->sequence)){ |
285 | 27.8k | v->granulepos=-1; /* out of sequence; lose count */ |
286 | 27.8k | b->sample_count=-1; |
287 | 27.8k | } |
288 | | |
289 | 54.2k | v->sequence=vb->sequence; |
290 | | |
291 | 54.2k | if(vb->pcm){ /* no pcm to process if vorbis_synthesis_trackonly |
292 | | was called on block */ |
293 | 54.2k | int n=ci->blocksizes[v->W]/2; |
294 | 54.2k | int n0=ci->blocksizes[0]/2; |
295 | 54.2k | int n1=ci->blocksizes[1]/2; |
296 | | |
297 | 54.2k | int thisCenter; |
298 | 54.2k | int prevCenter; |
299 | | |
300 | 54.2k | if(v->centerW){ |
301 | 29.1k | thisCenter=n1; |
302 | 29.1k | prevCenter=0; |
303 | 29.1k | }else{ |
304 | 25.0k | thisCenter=0; |
305 | 25.0k | prevCenter=n1; |
306 | 25.0k | } |
307 | | |
308 | | /* v->pcm is now used like a two-stage double buffer. We don't want |
309 | | to have to constantly shift *or* adjust memory usage. Don't |
310 | | accept a new block until the old is shifted out */ |
311 | | |
312 | | /* overlap/add PCM */ |
313 | | |
314 | 5.43M | for(j=0;j<vi->channels;j++){ |
315 | | /* the overlap/add section */ |
316 | 5.38M | if(v->lW){ |
317 | 1.04M | if(v->W){ |
318 | | /* large/large */ |
319 | 1.03M | ogg_int32_t *pcm=v->pcm[j]+prevCenter; |
320 | 1.03M | ogg_int32_t *p=vb->pcm[j]; |
321 | 1.43G | for(i=0;i<n1;i++) |
322 | 1.43G | pcm[i]+=p[i]; |
323 | 1.03M | }else{ |
324 | | /* large/small */ |
325 | 11.2k | ogg_int32_t *pcm=v->pcm[j]+prevCenter+n1/2-n0/2; |
326 | 11.2k | ogg_int32_t *p=vb->pcm[j]; |
327 | 12.7M | for(i=0;i<n0;i++) |
328 | 12.7M | pcm[i]+=p[i]; |
329 | 11.2k | } |
330 | 4.33M | }else{ |
331 | 4.33M | if(v->W){ |
332 | | /* small/large */ |
333 | 236k | ogg_int32_t *pcm=v->pcm[j]+prevCenter; |
334 | 236k | ogg_int32_t *p=vb->pcm[j]+n1/2-n0/2; |
335 | 63.6M | for(i=0;i<n0;i++) |
336 | 63.4M | pcm[i]+=p[i]; |
337 | 93.2M | for(;i<n1/2+n0/2;i++) |
338 | 92.9M | pcm[i]=p[i]; |
339 | 4.09M | }else{ |
340 | | /* small/small */ |
341 | 4.09M | ogg_int32_t *pcm=v->pcm[j]+prevCenter; |
342 | 4.09M | ogg_int32_t *p=vb->pcm[j]; |
343 | 2.09G | for(i=0;i<n0;i++) |
344 | 2.08G | pcm[i]+=p[i]; |
345 | 4.09M | } |
346 | 4.33M | } |
347 | | |
348 | | /* the copy section */ |
349 | 5.38M | { |
350 | 5.38M | ogg_int32_t *pcm=v->pcm[j]+thisCenter; |
351 | 5.38M | ogg_int32_t *p=vb->pcm[j]+n; |
352 | 3.78G | for(i=0;i<n;i++) |
353 | 3.78G | pcm[i]=p[i]; |
354 | 5.38M | } |
355 | 5.38M | } |
356 | | |
357 | 54.2k | if(v->centerW) |
358 | 29.1k | v->centerW=0; |
359 | 25.0k | else |
360 | 25.0k | v->centerW=n1; |
361 | | |
362 | | /* deal with initial packet state; we do this using the explicit |
363 | | pcm_returned==-1 flag otherwise we're sensitive to first block |
364 | | being short or long */ |
365 | | |
366 | 54.2k | if(v->pcm_returned==-1){ |
367 | 6.86k | v->pcm_returned=thisCenter; |
368 | 6.86k | v->pcm_current=thisCenter; |
369 | 47.3k | }else{ |
370 | 47.3k | v->pcm_returned=prevCenter; |
371 | 47.3k | v->pcm_current=prevCenter+ |
372 | 47.3k | ci->blocksizes[v->lW]/4+ |
373 | 47.3k | ci->blocksizes[v->W]/4; |
374 | 47.3k | } |
375 | | |
376 | 54.2k | } |
377 | | |
378 | | /* track the frame number... This is for convenience, but also |
379 | | making sure our last packet doesn't end with added padding. If |
380 | | the last packet is partial, the number of samples we'll have to |
381 | | return will be past the vb->granulepos. |
382 | | |
383 | | This is not foolproof! It will be confused if we begin |
384 | | decoding at the last page after a seek or hole. In that case, |
385 | | we don't have a starting point to judge where the last frame |
386 | | is. For this reason, vorbisfile will always try to make sure |
387 | | it reads the last two marked pages in proper sequence */ |
388 | | |
389 | 54.2k | if(b->sample_count==-1){ |
390 | 27.8k | b->sample_count=0; |
391 | 27.8k | }else{ |
392 | 26.3k | b->sample_count+=ci->blocksizes[v->lW]/4+ci->blocksizes[v->W]/4; |
393 | 26.3k | } |
394 | | |
395 | 54.2k | if(v->granulepos==-1){ |
396 | 51.2k | if(vb->granulepos!=-1){ /* only set if we have a position to set to */ |
397 | | |
398 | 9.59k | v->granulepos=vb->granulepos; |
399 | | |
400 | | /* is this a short page? */ |
401 | 9.59k | if(b->sample_count>v->granulepos){ |
402 | | /* corner case; if this is both the first and last audio page, |
403 | | then spec says the end is cut, not beginning */ |
404 | 5.63k | long extra=b->sample_count-vb->granulepos; |
405 | | |
406 | | /* we use ogg_int64_t for granule positions because a |
407 | | uint64 isn't universally available. Unfortunately, |
408 | | that means granposes can be 'negative' and result in |
409 | | extra being negative */ |
410 | 5.63k | if(extra<0) |
411 | 115 | extra=0; |
412 | | |
413 | 5.63k | if(vb->eofflag){ |
414 | | /* trim the end */ |
415 | | /* no preceeding granulepos; assume we started at zero (we'd |
416 | | have to in a short single-page stream) */ |
417 | | /* granulepos could be -1 due to a seek, but that would result |
418 | | in a long coun`t, not short count */ |
419 | | |
420 | | /* Guard against corrupt/malicious frames that set EOP and |
421 | | a backdated granpos; don't rewind more samples than we |
422 | | actually have */ |
423 | 2.47k | if(extra > v->pcm_current - v->pcm_returned) |
424 | 2.32k | extra = v->pcm_current - v->pcm_returned; |
425 | | |
426 | 2.47k | v->pcm_current-=extra; |
427 | 3.16k | }else{ |
428 | | /* trim the beginning */ |
429 | 3.16k | v->pcm_returned+=extra; |
430 | 3.16k | if(v->pcm_returned>v->pcm_current) |
431 | 1.24k | v->pcm_returned=v->pcm_current; |
432 | 3.16k | } |
433 | | |
434 | 5.63k | } |
435 | | |
436 | 9.59k | } |
437 | 51.2k | }else{ |
438 | 2.91k | v->granulepos+=ci->blocksizes[v->lW]/4+ci->blocksizes[v->W]/4; |
439 | 2.91k | if(vb->granulepos!=-1 && v->granulepos!=vb->granulepos){ |
440 | | |
441 | 2.38k | if(v->granulepos>vb->granulepos){ |
442 | 1.77k | long extra=v->granulepos-vb->granulepos; |
443 | | |
444 | 1.77k | if(extra) |
445 | 1.77k | if(vb->eofflag){ |
446 | | /* partial last frame. Strip the extra samples off */ |
447 | | |
448 | | /* Guard against corrupt/malicious frames that set EOP and |
449 | | a backdated granpos; don't rewind more samples than we |
450 | | actually have */ |
451 | 1.34k | if(extra > v->pcm_current - v->pcm_returned) |
452 | 1.01k | extra = v->pcm_current - v->pcm_returned; |
453 | | |
454 | | /* we use ogg_int64_t for granule positions because a |
455 | | uint64 isn't universally available. Unfortunately, |
456 | | that means granposes can be 'negative' and result in |
457 | | extra being negative */ |
458 | 1.34k | if(extra<0) |
459 | 315 | extra=0; |
460 | | |
461 | 1.34k | v->pcm_current-=extra; |
462 | | |
463 | 1.34k | } /* else {Shouldn't happen *unless* the bitstream is out of |
464 | | spec. Either way, believe the bitstream } */ |
465 | 1.77k | } /* else {Shouldn't happen *unless* the bitstream is out of |
466 | | spec. Either way, believe the bitstream } */ |
467 | 2.38k | v->granulepos=vb->granulepos; |
468 | 2.38k | } |
469 | 2.91k | } |
470 | | |
471 | | /* Update, cleanup */ |
472 | | |
473 | 54.2k | if(vb->eofflag)v->eofflag=1; |
474 | 54.2k | return(0); |
475 | 55.8k | } |
476 | | |
477 | | /* pcm==NULL indicates we just want the pending samples, no more */ |
478 | 1.74M | int vorbis_synthesis_pcmout(vorbis_dsp_state *v,ogg_int32_t ***pcm){ |
479 | 1.74M | vorbis_info *vi=v->vi; |
480 | 1.74M | if(v->pcm_returned>-1 && v->pcm_returned<v->pcm_current){ |
481 | 1.62M | if(pcm){ |
482 | 1.58M | int i; |
483 | 327M | for(i=0;i<vi->channels;i++) |
484 | 325M | v->pcmret[i]=v->pcm[i]+v->pcm_returned; |
485 | 1.58M | *pcm=v->pcmret; |
486 | 1.58M | } |
487 | 1.62M | return(v->pcm_current-v->pcm_returned); |
488 | 1.62M | } |
489 | 122k | return(0); |
490 | 1.74M | } |
491 | | |
492 | 1.58M | int vorbis_synthesis_read(vorbis_dsp_state *v,int bytes){ |
493 | 1.58M | if(bytes && v->pcm_returned+bytes>v->pcm_current)return(OV_EINVAL); |
494 | 1.58M | v->pcm_returned+=bytes; |
495 | 1.58M | return(0); |
496 | 1.58M | } |
497 | | |