File Coverage

src/ogf.c
Criterion Covered Total %
statement 197 261 75.4
branch 86 152 56.5
condition n/a
subroutine n/a
pod n/a
total 283 413 68.5


line stmt bran cond sub pod time code
1             /*
2             * This program is free software; you can redistribute it and/or modify
3             * it under the terms of the GNU General Public License as published by
4             * the Free Software Foundation; either version 2 of the License, or
5             * (at your option) any later version.
6             *
7             * This program is distributed in the hope that it will be useful,
8             * but WITHOUT ANY WARRANTY; without even the implied warranty of
9             * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10             * GNU General Public License for more details.
11             *
12             * You should have received a copy of the GNU General Public License
13             * along with this program; if not, write to the Free Software
14             * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15             */
16              
17             #include "ogf.h"
18             #include "ogg.h"
19             #include "flac.h"
20              
21             #pragma pack(push, 1)
22             typedef struct {
23             char tag[4];
24             uint8_t version, flag;
25             uint64_t granule_pos;
26             uint32_t serialno, page_num;
27             uint32_t checksum;
28             uint8_t segments;
29             } ogg_header_t;
30              
31             typedef struct {
32             uint8_t type;
33             char signature[4];
34             uint8_t maj;
35             uint8_t min;
36             uint16_t num_headers;
37             struct {
38             char tag[4];
39             uint8_t type;
40             uint8_t size[3];
41             } header;
42             struct {
43             uint16_t min_block_size;
44             uint16_t max_block_size;
45             uint8_t min_frame_size[3];
46             uint8_t max_frame_size[3];
47             uint8_t combo[4];
48             uint8_t sample_count[4];
49             uint8_t md5[16];
50             } streaminfo;
51             } flac_page_t;
52             #pragma pack(pop)
53              
54             uint32_t compute_crc32(uint8_t *data, size_t n);
55              
56             static int32_t
57 4           __le32toh__(int32_t n)
58             {
59             #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
60 4           return n;
61             #else
62             return __builtin_bswap32(n);
63             #endif
64             }
65              
66             /* https://xiph.org/ogg/doc/framing.html
67             * https://xiph.org/flac/ogg_mapping.html
68             * https://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-610004.2 */
69              
70             int
71 3           get_ogf_metadata(PerlIO *infile, char *file, HV *info, HV *tags)
72             {
73 3           return _ogf_parse(infile, file, info, tags, 0);
74             }
75              
76             static int
77 5           _ogf_parse(PerlIO *infile, char *file, HV *info, HV *tags, uint8_t seeking)
78             {
79             Buffer ogg_buf;
80             //Buffer vorbis_buf;
81             unsigned char *bptr;
82             unsigned char *last_bptr;
83             unsigned int buf_size;
84 5           unsigned int id3_size = 0; // size of leading ID3 data
85 5           uint32_t song_length_ms = 0;
86              
87             off_t file_size; // total file size
88             off_t audio_size; // total size of audio without tags
89 5           off_t audio_offset = 0; // offset to audio
90             off_t seek_position;
91              
92             unsigned char ogghdr[OGG_HEADER_SIZE];
93             char header_type;
94             int serialno;
95 5           uint64_t our_serialno = ULLONG_MAX;
96             int final_serialno;
97             int pagenum;
98             uint8_t num_segments;
99             int pagelen;
100 5           int page = 0;
101 5           int packets = 0;
102 5           int streams = 0;
103             int i;
104 5           int err = 0;
105              
106 5           short num_headers = 0;
107              
108             unsigned char opushdr[11];
109             unsigned char channels;
110 5           unsigned int input_samplerate = 0;
111 5           uint64_t granule_pos = 0;
112              
113 5           unsigned char TOC_byte = 0;
114              
115             flacinfo *flac;
116 5           Newz(0, flac, sizeof(flacinfo), flacinfo);
117 5           Newz(0, flac->buf, sizeof(Buffer), Buffer);
118              
119 5           flac->infile = infile;
120 5           flac->file = file;
121 5           flac->info = info;
122 5           flac->tags = tags;
123 5           flac->audio_offset = 0;
124 5           flac->seeking = seeking ? 1 : 0;
125 5           flac->num_seekpoints = 0;
126              
127 5           buffer_init(flac->buf, 0);
128              
129 5           flac->file_size = _file_size(infile);
130              
131 5           buffer_init(&ogg_buf, OGG_BLOCK_SIZE);
132              
133 5           file_size = _file_size(infile);
134 5 50         if (file_size < 0) {
135 0           PerlIO_printf(PerlIO_stderr(), "no file found: %s\n", file);
136 0           err = -1;
137 0           goto out;
138             }
139              
140 5           my_hv_store( info, "file_size", newSVuv(file_size) );
141              
142 5 50         if ( !_check_buf(infile, &ogg_buf, 10, OGG_BLOCK_SIZE) ) {
143 0           err = -1;
144 0           goto out;
145             }
146              
147             // Skip ID3 tags if any
148 5           bptr = (unsigned char *)buffer_ptr(&ogg_buf);
149 5           if (
150 5 50         (bptr[0] == 'I' && bptr[1] == 'D' && bptr[2] == '3') &&
    0          
    0          
151 0 0         bptr[3] < 0xff && bptr[4] < 0xff &&
    0          
152 0 0         bptr[6] < 0x80 && bptr[7] < 0x80 && bptr[8] < 0x80 && bptr[9] < 0x80
    0          
    0          
    0          
153             ) {
154             /* found an ID3 header... */
155 0           id3_size = 10 + (bptr[6]<<21) + (bptr[7]<<14) + (bptr[8]<<7) + bptr[9];
156              
157 0 0         if (bptr[5] & 0x10) {
158             // footer present
159 0           id3_size += 10;
160             }
161              
162 0           buffer_clear(&ogg_buf);
163              
164 0           audio_offset += id3_size;
165              
166             DEBUG_TRACE("Skipping ID3v2 tag of size %d\n", id3_size);
167              
168 0           PerlIO_seek(infile, id3_size, SEEK_SET);
169             }
170              
171 82           while (1) {
172 87           bool full_packet = true;
173              
174             // Grab 28-byte Ogg header
175 87 50         if ( !_check_buf(infile, &ogg_buf, OGG_HEADER_SIZE, OGG_BLOCK_SIZE) ) {
176 0           err = -1;
177 0           goto out;
178             }
179              
180 87           buffer_get(&ogg_buf, ogghdr, OGG_HEADER_SIZE);
181              
182 87           audio_offset += OGG_HEADER_SIZE;
183              
184             // check that the first four bytes are 'OggS'
185 87 50         if ( ogghdr[0] != 'O' || ogghdr[1] != 'g' || ogghdr[2] != 'g' || ogghdr[3] != 'S' ) {
    50          
    50          
    50          
186 0           PerlIO_printf(PerlIO_stderr(), "Not an Ogg file (bad OggS header): %s\n", file);
187 0           err = -1;
188 0           goto out;
189             }
190              
191             // Header type flag
192 87           header_type = ogghdr[5];
193              
194             // Absolute granule position, used to find the first audio page
195 87           bptr = ogghdr + 6;
196 87           granule_pos = (uint64_t)CONVERT_INT32LE(bptr);
197 87           bptr += 4;
198 87           granule_pos |= (uint64_t)CONVERT_INT32LE(bptr) << 32;
199              
200             // Stream serial number
201 87           serialno = CONVERT_INT32LE((ogghdr+14));
202              
203             // Count start-of-stream pages
204 87 100         if ( header_type & 0x02 ) {
205             // we only care about first stream (and no multiplex)
206 5 50         if (our_serialno == ULLONG_MAX) our_serialno = serialno;
207 5           streams++;
208             }
209              
210             // stop processing if we reach the 3rd packet and have no data
211 87 100         if (!num_headers && packets > 2 * streams && !buffer_len(flac->buf) ) {
    50          
    0          
212 0           break;
213             }
214              
215             // Page seq number
216 87           pagenum = CONVERT_INT32LE((ogghdr+18));
217              
218 87 50         if (page >= 0 && page == pagenum) {
    50          
219 87           page++;
220             }
221             else {
222 0           page = -1;
223             DEBUG_TRACE("Missing page(s) in Ogg file: %s\n", file);
224             }
225              
226             // Number of page segments
227 87           num_segments = ogghdr[26];
228              
229             // Calculate total page size
230 87           pagelen = ogghdr[27];
231 87 100         if (num_segments > 1) {
232             int i;
233 76           full_packet = false;
234              
235 76 50         if ( !_check_buf(infile, &ogg_buf, num_segments, OGG_BLOCK_SIZE) ) {
236 0           err = -1;
237 0           goto out;
238             }
239              
240 1408 100         for( i = 0; i < num_segments - 1; i++ ) {
241             u_char x;
242 1332           x = buffer_get_char(&ogg_buf);
243             // detect packet termination(s) - there is only one packet per page in OggFlac
244 1332 100         if (x < 255) full_packet = true;
245 1332           pagelen += x;
246             }
247              
248 76           audio_offset += num_segments - 1;
249             }
250              
251 87 50         if ( !_check_buf(infile, &ogg_buf, pagelen, OGG_BLOCK_SIZE) ) {
252 0           err = -1;
253 0           goto out;
254             }
255              
256             // Still don't have enough data, must have reached the end of the file
257 87 50         if ( buffer_len(&ogg_buf) < pagelen ) {
258 0           PerlIO_printf(PerlIO_stderr(), "Premature end of file: %s\n", file);
259 0           err = -1;
260 0           goto out;
261             }
262              
263             DEBUG_TRACE("OggS page %d (len:%d+28, sn:%u) at %d\n", pagenum, pagelen, serialno, (int)(audio_offset - OGG_HEADER_SIZE));
264             if (granule_pos != ULLONG_MAX) DEBUG_TRACE(" granule_pos: %llu\n", granule_pos);
265             else DEBUG_TRACE(" granule_pos: -1\n");
266              
267 87           audio_offset += pagelen;
268              
269             // if this is not for us, just consume data
270 87 50         if (serialno != our_serialno) {
271 0           buffer_consume( &ogg_buf, pagelen );
272 0           continue;
273 87 100         } else if (granule_pos && granule_pos != -1) {
    50          
274 0           PerlIO_printf(PerlIO_stderr(), "Audio granule before end of headers\n");
275 0           err = -1;
276 0           goto out;
277             }
278              
279             DEBUG_TRACE(" Append %d into buffer\n", pagelen);
280 87           buffer_append( flac->buf, buffer_ptr(&ogg_buf), pagelen );
281              
282 87 100         if (!full_packet) {
283 68           buffer_consume( &ogg_buf, pagelen );
284 68           continue;
285             } else {
286 19           packets++;
287             }
288              
289             // we have a full packet in buffer, let's process it
290 19           TOC_byte = buffer_get_char(flac->buf);
291             DEBUG_TRACE("Packet number %d\n", packets);
292              
293             // Process \x7fFLAC packet
294 19 100         if ( TOC_byte == 0x7f ) {
295             DEBUG_TRACE("First packet");
296 5 50         if ( strncmp( buffer_ptr(flac->buf), "FLAC", 4 ) == 0) {
297 5           buffer_consume(flac->buf, 4+2);
298 5           num_headers = buffer_get_short(flac->buf);
299             DEBUG_TRACE(" Found OggFlac tags TOC packet type with %hu headers\n", num_headers);
300 5 50         if ( strncmp( buffer_ptr(flac->buf), "fLaC", 4 ) != 0) {
301 0           PerlIO_printf(PerlIO_stderr(), "Not an OggFlac (fLaC) file: %s\n", file);
302 0           err = -1;
303 0           goto out;
304             }
305 5           buffer_consume(flac->buf, 8);
306 5           _flac_parse_streaminfo(flac);
307             }
308             else {
309 0           PerlIO_printf(PerlIO_stderr(), "Not and OggFlac (FLAC) file: %s\n", file);
310 0           err = -1;
311 0           goto out;
312             }
313             } else {
314             DEBUG_TRACE("Parsing header type %d\n", TOC_byte & 0x7f);
315 14 100         if (!seeking) {
316 8           uint8_t type = TOC_byte & 0x7f;
317 8           buffer_consume(flac->buf, 3);
318              
319 8 100         if (type == FLAC_TYPE_VORBIS_COMMENT) {
320             DEBUG_TRACE("Parsing vorbis_comment\n");
321 3           _parse_vorbis_comments(infile, flac->buf, tags, 0);
322 5 100         } else if (type == FLAC_TYPE_PICTURE) {
323             DEBUG_TRACE("Parsing picture\n");
324 1 50         if (!_flac_parse_picture(flac)) {
325 0           err = -1;
326 0           goto out;
327             }
328             }
329             }
330 14 100         if (TOC_byte & 0x80 || (num_headers && packets == num_headers + 1)) {
    50          
    50          
331             DEBUG_TRACE("Last header\n");
332             break;
333             }
334             }
335              
336             // this page belongs to a new packet
337 14           buffer_clear(flac->buf);
338 14           buffer_consume( &ogg_buf, pagelen );
339             }
340              
341             DEBUG_TRACE("All headers parsed, now doing audio\n");
342              
343             // from the first packet past the comments
344 5           my_hv_store( info, "audio_offset", newSViv(audio_offset) );
345              
346 5           audio_size = file_size - audio_offset;
347 5           my_hv_store( info, "audio_size", newSVuv(audio_size) );
348              
349 5           my_hv_store( info, "serial_number", newSVuv(our_serialno) );
350              
351 5           song_length_ms = SvIV( *( my_hv_fetch(info, "song_length_ms") ) );
352              
353 5 100         if (song_length_ms > 0) {
354 4           my_hv_store( info, "bitrate", newSVuv( _bitrate(audio_size, song_length_ms) ) );
355             }
356              
357             // find the last Ogg page
358              
359             #define BUF_SIZE 8500 // from vlc
360              
361 5 100         if (file_size < audio_offset + OGG_HEADER_SIZE) goto out;
362            
363 4           seek_position = file_size - BUF_SIZE;
364             while (1) {
365 4 50         if ( seek_position < audio_offset ) {
366 0           seek_position = audio_offset;
367             }
368              
369             // calculate average bitrate and duration
370             DEBUG_TRACE("Seeking to %d to calculate bitrate/duration\n", (int)seek_position);
371 4           PerlIO_seek(infile, seek_position, SEEK_SET);
372              
373 4           buffer_clear(&ogg_buf);
374              
375 4 50         if ( !_check_buf(infile, &ogg_buf, OGG_HEADER_SIZE, BUF_SIZE) ) {
376 0           err = -1;
377 0           goto out;
378             }
379              
380             // Find sync
381 4           bptr = (unsigned char *)buffer_ptr(&ogg_buf);
382 4           buf_size = buffer_len(&ogg_buf);
383 4           last_bptr = bptr;
384             // make sure we have room for at least the one ogg page header
385 33902 100         while (buf_size >= OGG_HEADER_SIZE) {
386 33898 100         if (bptr[0] == 'O' && bptr[1] == 'g' && bptr[2] == 'g' && bptr[3] == 'S') {
    100          
    50          
    50          
387 6           bptr += 6;
388              
389             // Get absolute granule value
390 6           granule_pos = (uint64_t)CONVERT_INT32LE(bptr);
391 6           bptr += 4;
392 6           granule_pos |= (uint64_t)CONVERT_INT32LE(bptr) << 32;
393 6           bptr += 4;
394             DEBUG_TRACE("found granule_pos %llu / samplerate %d to calculate bitrate/duration\n", granule_pos, flac->samplerate);
395             //XXX: jump the header size
396 6           last_bptr = bptr;
397             }
398             else {
399 33892           bptr++;
400 33892           buf_size--;
401             }
402             }
403 4           bptr = last_bptr;
404              
405             // Get serial number of this page, if the serial doesn't match the beginning of the file
406             // we have changed logical bitstreams and can't use the granule_pos for bitrate
407 4           final_serialno = CONVERT_INT32LE((bptr));
408              
409 4 50         if ( granule_pos && flac->samplerate && our_serialno == final_serialno ) {
    50          
    50          
410             // XXX: needs to adjust for initial granule value if file does not start at 0 samples
411 4           int length = (int)(((granule_pos) * 1.0 / flac->samplerate) * 1000);
412 4 50         if (!song_length_ms) my_hv_store( info, "song_length_ms", newSVuv(length) );
413 4           my_hv_store( info, "bitrate_ogg", newSVuv( _bitrate(audio_size, length) ) );
414              
415             DEBUG_TRACE("Using granule_pos %llu / samplerate %d to calculate bitrate/duration\n", granule_pos, flac->samplerate);
416 4           break;
417             }
418 0 0         if ( !song_length_ms && seek_position == audio_offset ) {
    0          
419             DEBUG_TRACE("Packet not found we won't be able to determine the length\n");
420 0           break;
421             }
422             // seek backwards by BUF_SIZE - OGG_HEADER_SIZE so that if our previous sync happened to include the end
423             // of page header we will include it in the next read
424 0           seek_position -= (BUF_SIZE - OGG_HEADER_SIZE);
425             }
426              
427 5           out:
428 5           buffer_free(&ogg_buf);
429              
430 5           buffer_free(flac->buf);
431 5           Safefree(flac->buf);
432              
433             DEBUG_TRACE("Err %d\n", err);
434 5           return err;
435             }
436              
437             off_t
438 0           ogf_find_frame(PerlIO *infile, char *file, int offset)
439             {
440 0           HV *info = newHV();
441 0           HV *tags = newHV();
442 0           int frame_offset = -1;
443              
444 0 0         if (offset < 0) {
445 0           goto out;
446             }
447              
448 0           frame_offset = _ogf_find_frame(infile, file, offset, info, tags);
449              
450 0           out:
451             // Don't leak
452 0           SvREFCNT_dec(info);
453 0           SvREFCNT_dec(tags);
454              
455 0           return frame_offset;
456             }
457              
458             static off_t
459 2           _ogf_find_frame(PerlIO *infile, char *file, int offset, HV *info, HV *tags)
460             {
461 2           int frame_offset = -1;
462             uint32_t samplerate;
463             uint32_t song_length_ms;
464             uint64_t target_sample;
465              
466             DEBUG_TRACE("Find_frame %d in %s\n", offset, file);
467              
468             // We need to read all metadata first to get some data we need to calculate
469 2 50         if ( _ogf_parse(infile, file, info, tags, 1) != 0 ) {
470 0           goto out;
471             }
472              
473 2           song_length_ms = SvUV( *(my_hv_fetch( info, "song_length_ms" )) );
474 2 50         if (offset >= song_length_ms) {
475 0           goto out;
476             }
477              
478             // Determine target sample we're looking for
479 2           samplerate = SvIV( *(my_hv_fetch( info, "samplerate" )) );
480 2           target_sample = (uint64_t)offset * samplerate / 1000;
481              
482             DEBUG_TRACE("Looking for target sample %llu\n", target_sample);
483 2           frame_offset = _ogg_binary_search_sample(infile, file, info, target_sample);
484              
485 2           out:
486 2           return frame_offset;
487             }
488              
489             // offset is in ms
490             int
491 2           ogf_find_frame_return_info(PerlIO *infile, char *file, int offset, HV *info)
492             {
493 2           int err = -1;
494 2           HV *tags = newHV();
495 2           int frame_offset = _ogf_find_frame(infile, file, offset, info, tags);
496              
497             // finally adjust STREAMINFO header
498 2 50         if (frame_offset >= 0) {
499             Buffer buf;
500             flac_page_t *page;
501             ogg_header_t *header;
502 2           uint32_t audio_offset = SvIV( *(my_hv_fetch( info, "audio_offset" )) );
503              
504             // don't understand my mp4.c does not seek here...
505 2           PerlIO_seek(infile, 0, SEEK_SET);
506 2           buffer_init(&buf, OGG_MAX_PAGE_SIZE + OGG_HEADER_SIZE);
507              
508             // there is only one segment in header
509 2           _check_buf(infile, &buf, OGG_MAX_PAGE_SIZE, OGG_MAX_PAGE_SIZE + OGG_HEADER_SIZE);
510 2           header = buffer_ptr(&buf);
511 2           page = buffer_ptr(&buf) + sizeof(*header) + 1;
512              
513             DEBUG_TRACE("now reading vorbis comment\n");
514              
515             // 1st page is 1st packet and with single lacing value
516 2 50         if (!strncmp(header->tag, "OggS", 4) && page->type == 0x7f &&
    50          
517 2 50         !strncmp(page->signature, "FLAC", 4) && !strncmp(page->header.tag, "fLaC", 4)) {
    50          
518 2           SV* seek_header = newSVpv("", 0);
519 2           int page_count = 0;
520 2           bool done = false;
521 2           off_t page_len = sizeof(*header) + 1 + sizeof(*page);
522              
523 2           page->streaminfo.combo[3] &= 0xf0;
524 2           memset(page->streaminfo.sample_count, 0, sizeof(page->streaminfo.sample_count));
525 2           memset(page->streaminfo.md5, 0, sizeof(page->streaminfo.md5));
526 2           page->num_headers = 1;
527             #if (__BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__)
528             page->num_headers <<= 8;
529             #endif
530 2           header->checksum = 0;
531 2           header->checksum = __le32toh__(compute_crc32(buffer_ptr(&buf), page_len));
532              
533             // store the updated OggFlac first packet/page (same in this case)
534 2           sv_catpvn( seek_header, (char*) buffer_ptr(&buf), page_len);
535              
536             // now we need to keep the 1st page (vorbis comment) and the rest is useless
537             do {
538             int i;
539             uint8_t *ptr;
540              
541             // replenish what we consumed to that we have a full buffer
542 46           buffer_consume(&buf, page_len);
543 46           _check_buf(infile, &buf, page_len, page_len);
544 46           page_len = 0;
545              
546 46           header = buffer_ptr(&buf);
547 46           ptr = buffer_ptr(&buf) + sizeof(*header) + 1;
548            
549             // make sure this is a page
550 46 50         if (memcmp(header->tag, "OggS", 4)) {
551 0           PerlIO_printf(PerlIO_stderr(), "error reading vorbis comment (%s)\n", file);
552 0           buffer_free(&buf);
553 0           SvREFCNT_dec(seek_header);
554 0           goto out;
555             }
556              
557 46 100         if (header->granule_pos == ULLONG_MAX) {
558 44           page_len = header->segments * 255;
559 18 100         } else for (ptr = buffer_ptr(&buf) + sizeof(*header), i = 0; i < header->segments && !done; i++, ptr++) {
    50          
560 16           page_len += *ptr;
561 16 100         if (*ptr != 255) done = true;
562             }
563              
564 46           page_len += sizeof(*header) + header->segments;
565            
566             // this is the last flac header, need to to set VORBIS_COMMENT as last header and update crc
567 46 100         if (page_count++ == 0) {
568 2           ptr = buffer_ptr(&buf) + sizeof(*header) + header->segments;
569 2           *ptr = 0x80 | FLAC_TYPE_VORBIS_COMMENT;
570 2           header->checksum = 0;
571 2           header->checksum = __le32toh__(compute_crc32(buffer_ptr(&buf), page_len));
572             DEBUG_TRACE("found vorbis comment header\n", page_len, header->segments);
573             }
574            
575 46           sv_catpvn( seek_header, (char*) buffer_ptr(&buf), page_len );
576             DEBUG_TRACE("adding page %d of len:%d with %d segments\n", page_count, page_len, header->segments);
577 46 100         } while (!done);
578              
579 2           my_hv_store( info, "seek_header", seek_header );
580             }
581              
582 2           err = 1;
583 2           buffer_free(&buf);
584             }
585              
586 0           out:
587             // Don't leak
588 2           SvREFCNT_dec(tags);
589 2 50         if (frame_offset != -1) {
590 2           my_hv_store( info, "seek_offset", newSVuv(frame_offset) );
591             }
592 2           return err;
593             }
594              
595             uint32_t crc32_table[] = {
596             0x00000000,0x04C11DB7,0x09823B6E,0x0D4326D9,0x130476DC,0x17C56B6B,0x1A864DB2,0x1E475005,
597             0x2608EDB8,0x22C9F00F,0x2F8AD6D6,0x2B4BCB61,0x350C9B64,0x31CD86D3,0x3C8EA00A,0x384FBDBD,
598             0x4C11DB70,0x48D0C6C7,0x4593E01E,0x4152FDA9,0x5F15ADAC,0x5BD4B01B,0x569796C2,0x52568B75,
599             0x6A1936C8,0x6ED82B7F,0x639B0DA6,0x675A1011,0x791D4014,0x7DDC5DA3,0x709F7B7A,0x745E66CD,
600             0x9823B6E0,0x9CE2AB57,0x91A18D8E,0x95609039,0x8B27C03C,0x8FE6DD8B,0x82A5FB52,0x8664E6E5,
601             0xBE2B5B58,0xBAEA46EF,0xB7A96036,0xB3687D81,0xAD2F2D84,0xA9EE3033,0xA4AD16EA,0xA06C0B5D,
602             0xD4326D90,0xD0F37027,0xDDB056FE,0xD9714B49,0xC7361B4C,0xC3F706FB,0xCEB42022,0xCA753D95,
603             0xF23A8028,0xF6FB9D9F,0xFBB8BB46,0xFF79A6F1,0xE13EF6F4,0xE5FFEB43,0xE8BCCD9A,0xEC7DD02D,
604             0x34867077,0x30476DC0,0x3D044B19,0x39C556AE,0x278206AB,0x23431B1C,0x2E003DC5,0x2AC12072,
605             0x128E9DCF,0x164F8078,0x1B0CA6A1,0x1FCDBB16,0x018AEB13,0x054BF6A4,0x0808D07D,0x0CC9CDCA,
606             0x7897AB07,0x7C56B6B0,0x71159069,0x75D48DDE,0x6B93DDDB,0x6F52C06C,0x6211E6B5,0x66D0FB02,
607             0x5E9F46BF,0x5A5E5B08,0x571D7DD1,0x53DC6066,0x4D9B3063,0x495A2DD4,0x44190B0D,0x40D816BA,
608             0xACA5C697,0xA864DB20,0xA527FDF9,0xA1E6E04E,0xBFA1B04B,0xBB60ADFC,0xB6238B25,0xB2E29692,
609             0x8AAD2B2F,0x8E6C3698,0x832F1041,0x87EE0DF6,0x99A95DF3,0x9D684044,0x902B669D,0x94EA7B2A,
610             0xE0B41DE7,0xE4750050,0xE9362689,0xEDF73B3E,0xF3B06B3B,0xF771768C,0xFA325055,0xFEF34DE2,
611             0xC6BCF05F,0xC27DEDE8,0xCF3ECB31,0xCBFFD686,0xD5B88683,0xD1799B34,0xDC3ABDED,0xD8FBA05A,
612             0x690CE0EE,0x6DCDFD59,0x608EDB80,0x644FC637,0x7A089632,0x7EC98B85,0x738AAD5C,0x774BB0EB,
613             0x4F040D56,0x4BC510E1,0x46863638,0x42472B8F,0x5C007B8A,0x58C1663D,0x558240E4,0x51435D53,
614             0x251D3B9E,0x21DC2629,0x2C9F00F0,0x285E1D47,0x36194D42,0x32D850F5,0x3F9B762C,0x3B5A6B9B,
615             0x0315D626,0x07D4CB91,0x0A97ED48,0x0E56F0FF,0x1011A0FA,0x14D0BD4D,0x19939B94,0x1D528623,
616             0xF12F560E,0xF5EE4BB9,0xF8AD6D60,0xFC6C70D7,0xE22B20D2,0xE6EA3D65,0xEBA91BBC,0xEF68060B,
617             0xD727BBB6,0xD3E6A601,0xDEA580D8,0xDA649D6F,0xC423CD6A,0xC0E2D0DD,0xCDA1F604,0xC960EBB3,
618             0xBD3E8D7E,0xB9FF90C9,0xB4BCB610,0xB07DABA7,0xAE3AFBA2,0xAAFBE615,0xA7B8C0CC,0xA379DD7B,
619             0x9B3660C6,0x9FF77D71,0x92B45BA8,0x9675461F,0x8832161A,0x8CF30BAD,0x81B02D74,0x857130C3,
620             0x5D8A9099,0x594B8D2E,0x5408ABF7,0x50C9B640,0x4E8EE645,0x4A4FFBF2,0x470CDD2B,0x43CDC09C,
621             0x7B827D21,0x7F436096,0x7200464F,0x76C15BF8,0x68860BFD,0x6C47164A,0x61043093,0x65C52D24,
622             0x119B4BE9,0x155A565E,0x18197087,0x1CD86D30,0x029F3D35,0x065E2082,0x0B1D065B,0x0FDC1BEC,
623             0x3793A651,0x3352BBE6,0x3E119D3F,0x3AD08088,0x2497D08D,0x2056CD3A,0x2D15EBE3,0x29D4F654,
624             0xC5A92679,0xC1683BCE,0xCC2B1D17,0xC8EA00A0,0xD6AD50A5,0xD26C4D12,0xDF2F6BCB,0xDBEE767C,
625             0xE3A1CBC1,0xE760D676,0xEA23F0AF,0xEEE2ED18,0xF0A5BD1D,0xF464A0AA,0xF9278673,0xFDE69BC4,
626             0x89B8FD09,0x8D79E0BE,0x803AC667,0x84FBDBD0,0x9ABC8BD5,0x9E7D9662,0x933EB0BB,0x97FFAD0C,
627             0xAFB010B1,0xAB710D06,0xA6322BDF,0xA2F33668,0xBCB4666D,0xB8757BDA,0xB5365D03,0xB1F740B4
628             };
629              
630 4           uint32_t compute_crc32(uint8_t *data, size_t n) {
631 4           uint32_t crc = 0;
632              
633 8920 100         while (n--) {
634 8916           uint8_t pos = (crc ^ (((uint32_t) *data++) << 24)) >> 24;
635 8916           crc = (crc << 8) ^ crc32_table[pos];
636             }
637              
638 4           return crc;
639             }