Line data Source code
1 : /*
2 : * Copyright (C) 2011 Andrea Mazzoleni
3 : *
4 : * This program is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 3 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * This program is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "portable.h"
19 :
20 : #include "support.h"
21 : #include "util.h"
22 : #include "stream.h"
23 :
24 : /****************************************************************************/
25 : /* stream */
26 :
27 : unsigned STREAM_SIZE = 64 * 1024;
28 :
29 1806 : STREAM* sopen_read(const char* file)
30 : {
31 : #if HAVE_POSIX_FADVISE
32 : int ret;
33 : #endif
34 1806 : STREAM* s = malloc_nofail(sizeof(STREAM));
35 :
36 1806 : s->handle_size = 1;
37 1806 : s->handle = malloc_nofail(sizeof(struct stream_handle));
38 :
39 1806 : pathcpy(s->handle[0].path, sizeof(s->handle[0].path), file);
40 1806 : s->handle[0].f = open(file, O_RDONLY | O_BINARY | O_SEQUENTIAL);
41 1806 : if (s->handle[0].f == -1) {
42 50 : free(s->handle);
43 50 : free(s);
44 50 : return 0;
45 : }
46 :
47 : #if HAVE_POSIX_FADVISE
48 : /* advise sequential access */
49 1756 : ret = posix_fadvise(s->handle[0].f, 0, 0, POSIX_FADV_SEQUENTIAL);
50 1756 : if (ret == ENOSYS) {
51 0 : log_fatal("WARNING! fadvise() is not supported in this platform. Performance may not be optimal!\n");
52 : /* call is not supported, like in armhf, see posix_fadvise manpage */
53 0 : ret = 0;
54 : }
55 1756 : if (ret != 0) {
56 : /* LCOV_EXCL_START */
57 : close(s->handle[0].f);
58 : free(s->handle);
59 : free(s);
60 : errno = ret; /* posix_fadvise return the error code */
61 : return 0;
62 : /* LCOV_EXCL_STOP */
63 : }
64 : #endif
65 :
66 1756 : s->buffer = malloc_nofail_test(STREAM_SIZE);
67 1756 : s->pos = s->buffer;
68 1756 : s->end = s->buffer;
69 1756 : s->state = STREAM_STATE_READ;
70 1756 : s->state_index = 0;
71 1756 : s->offset = 0;
72 1756 : s->offset_uncached = 0;
73 1756 : s->crc = 0;
74 1756 : s->crc_uncached = 0;
75 1756 : s->crc_stream = CRC_IV;
76 :
77 1756 : return s;
78 : }
79 :
80 162 : STREAM* sopen_multi_write(unsigned count)
81 : {
82 : unsigned i;
83 :
84 162 : STREAM* s = malloc_nofail(sizeof(STREAM));
85 :
86 162 : s->handle_size = count;
87 162 : s->handle = malloc_nofail(count * sizeof(struct stream_handle));
88 :
89 1267 : for (i = 0; i < count; ++i)
90 1105 : s->handle[i].f = -1;
91 :
92 162 : s->buffer = malloc_nofail_test(STREAM_SIZE);
93 162 : s->pos = s->buffer;
94 162 : s->end = s->buffer + STREAM_SIZE;
95 162 : s->state = STREAM_STATE_WRITE;
96 162 : s->state_index = 0;
97 162 : s->offset = 0;
98 162 : s->offset_uncached = 0;
99 162 : s->crc = 0;
100 162 : s->crc_uncached = 0;
101 162 : s->crc_stream = CRC_IV;
102 :
103 162 : return s;
104 : }
105 :
106 1105 : int sopen_multi_file(STREAM* s, unsigned i, const char* file)
107 : {
108 : #if HAVE_POSIX_FADVISE
109 : int ret;
110 : #endif
111 : int f;
112 :
113 1105 : pathcpy(s->handle[i].path, sizeof(s->handle[i].path), file);
114 :
115 : /* O_EXCL to be resilient ensure to always create a new file and not use a stale link to the original file */
116 1105 : f = open(file, O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_SEQUENTIAL, 0600);
117 1105 : if (f == -1) {
118 : /* LCOV_EXCL_START */
119 : return -1;
120 : /* LCOV_EXCL_STOP */
121 : }
122 :
123 : #if HAVE_POSIX_FADVISE
124 : /* advise sequential access */
125 1105 : ret = posix_fadvise(f, 0, 0, POSIX_FADV_SEQUENTIAL);
126 1105 : if (ret == ENOSYS) {
127 : /* call is not supported, like in armhf, see posix_fadvise manpage */
128 0 : ret = 0;
129 : }
130 1105 : if (ret != 0) {
131 : /* LCOV_EXCL_START */
132 : close(f);
133 : errno = ret; /* posix_fadvise return the error code */
134 : return -1;
135 : /* LCOV_EXCL_STOP */
136 : }
137 : #endif
138 :
139 1105 : s->handle[i].f = f;
140 :
141 1105 : return 0;
142 : }
143 :
144 0 : STREAM* sopen_write(const char* file)
145 : {
146 0 : STREAM* s = sopen_multi_write(1);
147 :
148 0 : if (sopen_multi_file(s, 0, file) != 0) {
149 0 : sclose(s);
150 0 : return 0;
151 : }
152 :
153 0 : return s;
154 : }
155 :
156 1916 : int sclose(STREAM* s)
157 : {
158 1916 : int fail = 0;
159 : unsigned i;
160 :
161 1916 : if (s->state == STREAM_STATE_WRITE) {
162 162 : if (sflush(s) != 0) {
163 : /* LCOV_EXCL_START */
164 : fail = 1;
165 : /* LCOV_EXCL_STOP */
166 : }
167 : }
168 :
169 4775 : for (i = 0; i < s->handle_size; ++i) {
170 2859 : if (close(s->handle[i].f) != 0) {
171 : /* LCOV_EXCL_START */
172 : fail = 1;
173 : /* LCOV_EXCL_STOP */
174 : }
175 : }
176 :
177 1916 : free(s->handle);
178 1916 : free(s->buffer);
179 1916 : free(s);
180 :
181 1916 : if (fail) {
182 : /* LCOV_EXCL_START */
183 : return -1;
184 : /* LCOV_EXCL_STOP */
185 : }
186 :
187 1916 : return 0;
188 : }
189 :
190 255 : int shandle(STREAM* s)
191 : {
192 255 : if (!s->handle_size) {
193 : /* LCOV_EXCL_START */
194 : return -1;
195 : /* LCOV_EXCL_STOP */
196 : }
197 :
198 255 : return s->handle[0].f;
199 : }
200 :
201 : /**
202 : * Fill the read stream buffer.
203 : * \return 0 if at least on char is read, or EOF on error.
204 : */
205 606674 : static int sfill(STREAM* s)
206 : {
207 : ssize_t ret;
208 :
209 606674 : if (s->state != STREAM_STATE_READ) {
210 : /* LCOV_EXCL_START */
211 : return EOF;
212 : /* LCOV_EXCL_STOP */
213 : }
214 :
215 605606 : ret = read(s->handle[0].f, s->buffer, STREAM_SIZE);
216 :
217 605606 : if (ret < 0) {
218 : /* LCOV_EXCL_START */
219 : s->state = STREAM_STATE_ERROR;
220 : return EOF;
221 : /* LCOV_EXCL_STOP */
222 : }
223 605606 : if (ret == 0) {
224 1627 : s->state = STREAM_STATE_EOF;
225 1627 : return EOF;
226 : }
227 :
228 : /* update the crc */
229 603979 : s->crc_uncached = s->crc;
230 603979 : s->crc = crc32c(s->crc, s->buffer, ret);
231 :
232 : /* update the offset */
233 603979 : s->offset_uncached = s->offset;
234 603979 : s->offset += ret;
235 :
236 603979 : s->pos = s->buffer;
237 603979 : s->end = s->buffer + ret;
238 :
239 603979 : return 0;
240 : }
241 :
242 1106 : int sdeplete(STREAM* s, unsigned char* last)
243 : {
244 : /* last four bytes */
245 1106 : last[0] = 0;
246 1106 : last[1] = 0;
247 1106 : last[2] = 0;
248 1106 : last[3] = 0;
249 :
250 : while (1) {
251 : /* increase the position up to 4 bytes before the end */
252 307791 : if (s->pos + 4 <= s->end)
253 147950 : s->pos = s->end - 4;
254 :
255 : /* insert the last 4 bytes */
256 1159359 : while (s->pos < s->end) {
257 851568 : last[0] = last[1];
258 851568 : last[1] = last[2];
259 851568 : last[2] = last[3];
260 851568 : last[3] = *s->pos++;
261 : }
262 :
263 : /* fill again the buffer until the end of the file */
264 307791 : if (sfill(s) != 0) {
265 : /* on error fail */
266 1106 : if (serror(s)) {
267 : /* LCOV_EXCL_START */
268 : return EOF;
269 : /* LCOV_EXCL_STOP */
270 : }
271 :
272 : /* on EOF terminate */
273 1106 : break;
274 : }
275 : }
276 :
277 1106 : return 0;
278 : }
279 :
280 38976 : int sflush(STREAM* s)
281 : {
282 : ssize_t ret;
283 : ssize_t size;
284 : unsigned i;
285 :
286 38976 : if (s->state != STREAM_STATE_WRITE) {
287 : /* LCOV_EXCL_START */
288 : return EOF;
289 : /* LCOV_EXCL_STOP */
290 : }
291 :
292 38976 : size = s->pos - s->buffer;
293 38976 : if (!size)
294 146 : return 0;
295 :
296 346479 : for (i = 0; i < s->handle_size; ++i) {
297 307649 : ret = write(s->handle[i].f, s->buffer, size);
298 :
299 307649 : if (ret != size) {
300 : /* LCOV_EXCL_START */
301 : s->state = STREAM_STATE_ERROR;
302 : s->state_index = i;
303 : return EOF;
304 : /* LCOV_EXCL_STOP */
305 : }
306 : }
307 :
308 : /*
309 : * Update the crc *after* writing the data.
310 : *
311 : * This must be done after the file write,
312 : * to be able to detect memory errors on the buffer,
313 : * happening during the write.
314 : */
315 38830 : s->crc = crc32c(s->crc, s->buffer, size);
316 38830 : s->crc_uncached = s->crc;
317 :
318 : /* update the offset */
319 38830 : s->offset += size;
320 38830 : s->offset_uncached = s->offset;
321 :
322 38830 : s->pos = s->buffer;
323 :
324 38830 : return 0;
325 : }
326 :
327 1 : int64_t stell(STREAM* s)
328 : {
329 1 : return s->offset_uncached + (s->pos - s->buffer);
330 : }
331 :
332 1650 : uint32_t scrc(STREAM*s)
333 : {
334 1650 : return crc32c(s->crc_uncached, s->buffer, s->pos - s->buffer);
335 : }
336 :
337 162 : uint32_t scrc_stream(STREAM*s)
338 : {
339 162 : return s->crc_stream ^ CRC_IV;
340 : }
341 :
342 298883 : int sgetc_uncached(STREAM* s)
343 : {
344 : /* if at the end of the buffer, fill it */
345 298883 : if (s->pos == s->end && sfill(s) != 0)
346 1589 : return EOF;
347 297294 : return *s->pos++;
348 : }
349 :
350 9501 : int sgettok(STREAM* f, char* str, int size)
351 : {
352 9501 : char* i = str;
353 9501 : char* send = str + size;
354 : int c;
355 :
356 : while (1) {
357 62713 : c = sgetc(f);
358 62713 : if (c == EOF) {
359 267 : break;
360 : }
361 62446 : if (c == ' ' || c == '\t') {
362 8459 : sungetc(c, f);
363 8459 : break;
364 : }
365 53987 : if (c == '\n') {
366 : /* remove ending carriage return to support the Windows CR+LF format */
367 775 : if (i != str && i[-1] == '\r')
368 0 : --i;
369 775 : sungetc(c, f);
370 775 : break;
371 : }
372 :
373 53212 : *i++ = c;
374 :
375 53212 : if (i == send) {
376 : /* LCOV_EXCL_START */
377 : return -1;
378 : /* LCOV_EXCL_STOP */
379 : }
380 : }
381 :
382 9501 : *i = 0;
383 :
384 9501 : return i - str;
385 : }
386 :
387 12831229 : int sread(STREAM* f, void* void_data, unsigned size)
388 : {
389 12831229 : unsigned char* data = void_data;
390 :
391 : /* if there is enough space in memory */
392 12831229 : if (sptrlookup(f, size)) {
393 : /* optimized version with all the data in memory */
394 12805157 : unsigned char* pos = sptrget(f);
395 :
396 : /* copy it */
397 205276952 : while (size--)
398 192471795 : *data++ = *pos++;
399 :
400 12805157 : sptrset(f, pos);
401 : } else {
402 : /* standard version using sgetc() */
403 1347206 : while (size--) {
404 1321134 : int c = sgetc(f);
405 1321134 : if (c == EOF) {
406 : /* LCOV_EXCL_START */
407 : return -1;
408 : /* LCOV_EXCL_STOP */
409 : }
410 :
411 1321134 : *data++ = c;
412 : }
413 : }
414 :
415 12831229 : return 0;
416 : }
417 :
418 5492 : int sgetline(STREAM* f, char* str, int size)
419 : {
420 5492 : char* i = str;
421 5492 : char* send = str + size;
422 : int c;
423 :
424 : /* if there is enough data in memory */
425 5492 : if (sptrlookup(f, size)) {
426 : /* optimized version with all the data in memory */
427 0 : unsigned char* pos = sptrget(f);
428 :
429 : while (1) {
430 0 : c = *pos++;
431 0 : if (c == '\n') {
432 : /* remove ending carriage return to support the Windows CR+LF format */
433 0 : if (i != str && i[-1] == '\r')
434 0 : --i;
435 0 : --pos;
436 0 : break;
437 : }
438 :
439 0 : *i++ = c;
440 :
441 0 : if (i == send) {
442 : /* LCOV_EXCL_START */
443 : return -1;
444 : /* LCOV_EXCL_STOP */
445 : }
446 : }
447 :
448 0 : sptrset(f, pos);
449 : } else {
450 : while (1) {
451 141189 : c = sgetc(f);
452 141189 : if (c == EOF) {
453 : /* LCOV_EXCL_START */
454 : break;
455 : /* LCOV_EXCL_STOP */
456 : }
457 141189 : if (c == '\n') {
458 : /* remove ending carriage return to support the Windows CR+LF format */
459 5492 : if (i != str && i[-1] == '\r')
460 0 : --i;
461 5492 : sungetc(c, f);
462 5492 : break;
463 : }
464 :
465 135697 : *i++ = c;
466 :
467 135697 : if (i == send) {
468 : /* LCOV_EXCL_START */
469 : return -1;
470 : /* LCOV_EXCL_STOP */
471 : }
472 : }
473 : }
474 :
475 5492 : *i = 0;
476 :
477 5492 : return i - str;
478 : }
479 :
480 5451 : int sgetlasttok(STREAM* f, char* str, int size)
481 : {
482 : int ret;
483 :
484 5451 : ret = sgetline(f, str, size);
485 5451 : if (ret < 0) {
486 : /* LCOV_EXCL_START */
487 : return ret;
488 : /* LCOV_EXCL_STOP */
489 : }
490 :
491 5451 : while (ret > 0 && (str[ret - 1] == ' ' || str[ret - 1] == '\t'))
492 0 : --ret;
493 :
494 5451 : str[ret] = 0;
495 :
496 5451 : return ret;
497 : }
498 :
499 270 : int sgetu32(STREAM* f, uint32_t* value)
500 : {
501 : int c;
502 :
503 270 : c = sgetc(f);
504 270 : if (c == '0') {
505 0 : *value = 0;
506 0 : return 0;
507 270 : } else if (c >= '1' && c <= '9') {
508 : uint32_t v;
509 :
510 270 : v = c - '0';
511 :
512 270 : c = sgetc(f);
513 271 : while (c >= '0' && c <= '9') {
514 : uint32_t digit;
515 1 : if (v > 0xFFFFFFFFU / 10) {
516 : /* LCOV_EXCL_START */
517 : /* overflow */
518 : return -1;
519 : /* LCOV_EXCL_STOP */
520 : }
521 1 : v *= 10;
522 :
523 1 : digit = c - '0';
524 1 : if (v > 0xFFFFFFFFU - digit) {
525 : /* LCOV_EXCL_START */
526 : /* overflow */
527 : return -1;
528 : /* LCOV_EXCL_STOP */
529 : }
530 1 : v += digit;
531 :
532 1 : c = sgetc(f);
533 : }
534 :
535 270 : *value = v;
536 :
537 270 : sungetc(c, f);
538 270 : return 0;
539 : } else {
540 : /* LCOV_EXCL_START */
541 : /* nothing read */
542 : return -1;
543 : /* LCOV_EXCL_STOP */
544 : }
545 : }
546 :
547 18615016 : int sgetb32(STREAM* f, uint32_t* value)
548 : {
549 : uint32_t v;
550 : unsigned char b;
551 : unsigned char s;
552 : int c;
553 :
554 18615016 : v = 0;
555 18615016 : s = 0;
556 35444764 : loop:
557 35444764 : c = sgetc(f);
558 35444764 : if (c == EOF) {
559 : /* LCOV_EXCL_START */
560 : return -1;
561 : /* LCOV_EXCL_STOP */
562 : }
563 :
564 35444764 : b = (unsigned char)c;
565 35444764 : if ((b & 0x80) == 0) {
566 16829748 : v |= (uint32_t)b << s;
567 16829748 : s += 7;
568 16829748 : if (s >= 32) {
569 : /* LCOV_EXCL_START */
570 : return -1;
571 : /* LCOV_EXCL_STOP */
572 : }
573 16829748 : goto loop;
574 : }
575 :
576 18615016 : v |= (uint32_t)(b & 0x7f) << s;
577 :
578 18615016 : *value = v;
579 :
580 18615016 : return 0;
581 : }
582 :
583 10909065 : int sgetb64(STREAM* f, uint64_t* value)
584 : {
585 : uint64_t v;
586 : unsigned char b;
587 : unsigned char s;
588 : int c;
589 :
590 10909065 : v = 0;
591 10909065 : s = 0;
592 39774069 : loop:
593 39774069 : c = sgetc(f);
594 39774069 : if (c == EOF) {
595 : /* LCOV_EXCL_START */
596 : return -1;
597 : /* LCOV_EXCL_STOP */
598 : }
599 :
600 39774069 : b = (unsigned char)c;
601 39774069 : if ((b & 0x80) == 0) {
602 28865004 : v |= (uint64_t)b << s;
603 28865004 : s += 7;
604 28865004 : if (s >= 64) {
605 : /* LCOV_EXCL_START */
606 : return -1;
607 : /* LCOV_EXCL_STOP */
608 : }
609 28865004 : goto loop;
610 : }
611 :
612 10909065 : v |= (uint64_t)(b & 0x7f) << s;
613 :
614 10909065 : *value = v;
615 :
616 10909065 : return 0;
617 : }
618 :
619 382 : int sgetble32(STREAM* f, uint32_t* value)
620 : {
621 : unsigned char buf[4];
622 :
623 382 : if (sread(f, buf, 4) != 0) {
624 : /* LCOV_EXCL_START */
625 : return -1;
626 : /* LCOV_EXCL_STOP */
627 : }
628 :
629 382 : *value = buf[0] | (uint32_t)buf[1] << 8 | (uint32_t)buf[2] << 16 | (uint32_t)buf[3] << 24;
630 :
631 382 : return 0;
632 : }
633 :
634 3883522 : int sgetbs(STREAM* f, char* str, int size)
635 : {
636 : uint32_t len;
637 :
638 3883522 : if (sgetb32(f, &len) < 0) {
639 : /* LCOV_EXCL_START */
640 : return -1;
641 : /* LCOV_EXCL_STOP */
642 : }
643 :
644 3883522 : if (len + 1 > (uint32_t)size) {
645 : /* LCOV_EXCL_START */
646 : return -1;
647 : /* LCOV_EXCL_STOP */
648 : }
649 :
650 3883522 : str[len] = 0;
651 :
652 3883522 : return sread(f, str, (int)len);
653 : }
654 :
655 20116773 : int swrite(const void* void_data, unsigned size, STREAM* f)
656 : {
657 20116773 : const unsigned char* data = void_data;
658 :
659 : /* if there is enough space in memory */
660 20116773 : if (sptrlookup(f, size)) {
661 : /* optimized version with all the data in memory */
662 20110700 : unsigned char* pos = sptrget(f);
663 :
664 : /**
665 : * Update the crc *before* writing the data in the buffer
666 : *
667 : * This must be done before the memory write,
668 : * to be able to detect memory errors on the buffer,
669 : * happening before we write it on the file.
670 : */
671 20110700 : f->crc_stream = crc32c_plain(f->crc_stream, data, size);
672 :
673 : /* copy it */
674 147430516 : while (size--)
675 127319816 : *pos++ = *data++;
676 :
677 20110700 : sptrset(f, pos);
678 : } else {
679 : /* standard version using sputc() */
680 193471 : while (size--) {
681 187398 : if (sputc(*data++, f) != 0) {
682 : /* LCOV_EXCL_START */
683 : return -1;
684 : /* LCOV_EXCL_STOP */
685 : }
686 : }
687 : }
688 :
689 20116773 : return 0;
690 : }
691 :
692 8837401 : int sputb32(uint32_t value, STREAM* s)
693 : {
694 : unsigned char b;
695 : unsigned char buf[16];
696 : unsigned i;
697 :
698 8837401 : i = 0;
699 16839132 : loop:
700 16839132 : b = value & 0x7f;
701 16839132 : value >>= 7;
702 :
703 16839132 : if (value) {
704 8001731 : buf[i++] = b;
705 8001731 : goto loop;
706 : }
707 :
708 8837401 : buf[i++] = b | 0x80;
709 :
710 8837401 : return swrite(buf, i, s);
711 : }
712 :
713 5178964 : int sputb64(uint64_t value, STREAM* s)
714 : {
715 : unsigned char b;
716 : unsigned char buf[16];
717 : unsigned i;
718 :
719 5178964 : i = 0;
720 18892156 : loop:
721 18892156 : b = value & 0x7f;
722 18892156 : value >>= 7;
723 :
724 18892156 : if (value) {
725 13713192 : buf[i++] = b;
726 13713192 : goto loop;
727 : }
728 :
729 5178964 : buf[i++] = b | 0x80;
730 :
731 5178964 : return swrite(buf, i, s);
732 : }
733 :
734 162 : int sputble32(uint32_t value, STREAM* s)
735 : {
736 : unsigned char buf[4];
737 :
738 162 : buf[0] = value & 0xFF;
739 162 : buf[1] = (value >> 8) & 0xFF;
740 162 : buf[2] = (value >> 16) & 0xFF;
741 162 : buf[3] = (value >> 24) & 0xFF;
742 :
743 162 : return swrite(buf, 4, s);
744 : }
745 :
746 1843420 : int sputbs(const char* str, STREAM* f)
747 : {
748 1843420 : size_t len = strlen(str);
749 :
750 1843420 : if (sputb32(len, f) != 0) {
751 : /* LCOV_EXCL_START */
752 : return -1;
753 : /* LCOV_EXCL_STOP */
754 : }
755 :
756 1843420 : return swrite(str, len, f);
757 : }
758 :
759 : #if HAVE_FSYNC
760 146 : int ssync(STREAM* s)
761 : {
762 : unsigned i;
763 :
764 1123 : for (i = 0; i < s->handle_size; ++i) {
765 977 : if (fsync(s->handle[i].f) != 0) {
766 : /* LCOV_EXCL_START */
767 : s->state = STREAM_STATE_ERROR;
768 : s->state_index = i;
769 : return -1;
770 : /* LCOV_EXCL_STOP */
771 : }
772 : }
773 :
774 146 : return 0;
775 : }
776 : #endif
777 :
|