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 1838 : STREAM* sopen_read(const char* file)
30 : {
31 : #if HAVE_POSIX_FADVISE
32 : int ret;
33 : #endif
34 1838 : STREAM* s = malloc_nofail(sizeof(STREAM));
35 :
36 1838 : s->handle_size = 1;
37 1838 : s->handle = malloc_nofail(sizeof(struct stream_handle));
38 :
39 1838 : pathcpy(s->handle[0].path, sizeof(s->handle[0].path), file);
40 1838 : s->handle[0].f = open(file, O_RDONLY | O_BINARY | O_SEQUENTIAL);
41 1838 : 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 1788 : ret = posix_fadvise(s->handle[0].f, 0, 0, POSIX_FADV_SEQUENTIAL);
50 1788 : 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 1788 : 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 1788 : s->buffer = malloc_nofail_test(STREAM_SIZE);
67 1788 : s->pos = s->buffer;
68 1788 : s->end = s->buffer;
69 1788 : s->state = STREAM_STATE_READ;
70 1788 : s->state_index = 0;
71 1788 : s->offset = 0;
72 1788 : s->offset_uncached = 0;
73 1788 : s->crc = 0;
74 1788 : s->crc_uncached = 0;
75 1788 : s->crc_stream = CRC_IV;
76 :
77 1788 : return s;
78 : }
79 :
80 167 : STREAM* sopen_multi_write(unsigned count)
81 : {
82 : unsigned i;
83 :
84 167 : STREAM* s = malloc_nofail(sizeof(STREAM));
85 :
86 167 : s->handle_size = count;
87 167 : s->handle = malloc_nofail(count * sizeof(struct stream_handle));
88 :
89 1307 : for (i = 0; i < count; ++i)
90 1140 : s->handle[i].f = -1;
91 :
92 167 : s->buffer = malloc_nofail_test(STREAM_SIZE);
93 167 : s->pos = s->buffer;
94 167 : s->end = s->buffer + STREAM_SIZE;
95 167 : s->state = STREAM_STATE_WRITE;
96 167 : s->state_index = 0;
97 167 : s->offset = 0;
98 167 : s->offset_uncached = 0;
99 167 : s->crc = 0;
100 167 : s->crc_uncached = 0;
101 167 : s->crc_stream = CRC_IV;
102 :
103 167 : return s;
104 : }
105 :
106 1140 : 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 1140 : pathcpy(s->handle[i].path, sizeof(s->handle[i].path), file);
114 :
115 1140 : f = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_SEQUENTIAL, 0600);
116 1140 : if (f == -1) {
117 : /* LCOV_EXCL_START */
118 : return -1;
119 : /* LCOV_EXCL_STOP */
120 : }
121 :
122 : #if HAVE_POSIX_FADVISE
123 : /* advise sequential access */
124 1140 : ret = posix_fadvise(f, 0, 0, POSIX_FADV_SEQUENTIAL);
125 1140 : if (ret == ENOSYS) {
126 : /* call is not supported, like in armhf, see posix_fadvise manpage */
127 0 : ret = 0;
128 : }
129 1140 : if (ret != 0) {
130 : /* LCOV_EXCL_START */
131 : close(f);
132 : errno = ret; /* posix_fadvise return the error code */
133 : return -1;
134 : /* LCOV_EXCL_STOP */
135 : }
136 : #endif
137 :
138 1140 : s->handle[i].f = f;
139 :
140 1140 : return 0;
141 : }
142 :
143 0 : STREAM* sopen_write(const char* file)
144 : {
145 0 : STREAM* s = sopen_multi_write(1);
146 :
147 0 : if (sopen_multi_file(s, 0, file) != 0) {
148 0 : sclose(s);
149 0 : return 0;
150 : }
151 :
152 0 : return s;
153 : }
154 :
155 1953 : int sclose(STREAM* s)
156 : {
157 1953 : int fail = 0;
158 : unsigned i;
159 :
160 1953 : if (s->state == STREAM_STATE_WRITE) {
161 167 : if (sflush(s) != 0) {
162 : /* LCOV_EXCL_START */
163 : fail = 1;
164 : /* LCOV_EXCL_STOP */
165 : }
166 : }
167 :
168 4879 : for (i = 0; i < s->handle_size; ++i) {
169 2926 : if (close(s->handle[i].f) != 0) {
170 : /* LCOV_EXCL_START */
171 : fail = 1;
172 : /* LCOV_EXCL_STOP */
173 : }
174 : }
175 :
176 1953 : free(s->handle);
177 1953 : free(s->buffer);
178 1953 : free(s);
179 :
180 1953 : if (fail) {
181 : /* LCOV_EXCL_START */
182 : return -1;
183 : /* LCOV_EXCL_STOP */
184 : }
185 :
186 1953 : return 0;
187 : }
188 :
189 254 : int shandle(STREAM* s)
190 : {
191 254 : if (!s->handle_size) {
192 : /* LCOV_EXCL_START */
193 : return -1;
194 : /* LCOV_EXCL_STOP */
195 : }
196 :
197 254 : return s->handle[0].f;
198 : }
199 :
200 : /**
201 : * Fill the read stream buffer.
202 : * \return 0 if at least on char is read, or EOF on error.
203 : */
204 607128 : static int sfill(STREAM* s)
205 : {
206 : ssize_t ret;
207 :
208 607128 : if (s->state != STREAM_STATE_READ) {
209 : /* LCOV_EXCL_START */
210 : return EOF;
211 : /* LCOV_EXCL_STOP */
212 : }
213 :
214 606068 : ret = read(s->handle[0].f, s->buffer, STREAM_SIZE);
215 :
216 606061 : if (ret < 0) {
217 : /* LCOV_EXCL_START */
218 : s->state = STREAM_STATE_ERROR;
219 : return EOF;
220 : /* LCOV_EXCL_STOP */
221 : }
222 606061 : if (ret == 0) {
223 1659 : s->state = STREAM_STATE_EOF;
224 1659 : return EOF;
225 : }
226 :
227 : /* update the crc */
228 604402 : s->crc_uncached = s->crc;
229 604402 : s->crc = crc32c(s->crc, s->buffer, ret);
230 :
231 : /* update the offset */
232 604397 : s->offset_uncached = s->offset;
233 604397 : s->offset += ret;
234 :
235 604397 : s->pos = s->buffer;
236 604397 : s->end = s->buffer + ret;
237 :
238 604397 : return 0;
239 : }
240 :
241 1157 : int sdeplete(STREAM* s, unsigned char* last)
242 : {
243 : /* last four bytes */
244 1157 : last[0] = 0;
245 1157 : last[1] = 0;
246 1157 : last[2] = 0;
247 1157 : last[3] = 0;
248 :
249 : while (1) {
250 : /* increase the position up to 4 bytes before the end */
251 308264 : if (s->pos + 4 <= s->end)
252 148367 : s->pos = s->end - 4;
253 :
254 : /* insert the last 4 bytes */
255 1469818 : while (s->pos < s->end) {
256 853290 : last[0] = last[1];
257 853290 : last[1] = last[2];
258 853290 : last[2] = last[3];
259 853290 : last[3] = *s->pos++;
260 : }
261 :
262 : /* fill again the buffer until the end of the file */
263 308264 : if (sfill(s) != 0) {
264 : /* on error fail */
265 1141 : if (serror(s)) {
266 : /* LCOV_EXCL_START */
267 : return EOF;
268 : /* LCOV_EXCL_STOP */
269 : }
270 :
271 : /* on EOF terminate */
272 1141 : break;
273 : }
274 307107 : }
275 :
276 1141 : return 0;
277 : }
278 :
279 39049 : int sflush(STREAM* s)
280 : {
281 : ssize_t ret;
282 : ssize_t size;
283 : unsigned i;
284 :
285 39049 : if (s->state != STREAM_STATE_WRITE) {
286 : /* LCOV_EXCL_START */
287 : return EOF;
288 : /* LCOV_EXCL_STOP */
289 : }
290 :
291 39049 : size = s->pos - s->buffer;
292 39049 : if (!size)
293 151 : return 0;
294 :
295 347023 : for (i = 0; i < s->handle_size; ++i) {
296 308125 : ret = write(s->handle[i].f, s->buffer, size);
297 :
298 308125 : if (ret != size) {
299 : /* LCOV_EXCL_START */
300 : s->state = STREAM_STATE_ERROR;
301 : s->state_index = i;
302 : return EOF;
303 : /* LCOV_EXCL_STOP */
304 : }
305 : }
306 :
307 : /*
308 : * Update the crc *after* writing the data.
309 : *
310 : * This must be done after the file write,
311 : * to be able to detect memory errors on the buffer,
312 : * happening during the write.
313 : */
314 38898 : s->crc = crc32c(s->crc, s->buffer, size);
315 38898 : s->crc_uncached = s->crc;
316 :
317 : /* update the offset */
318 38898 : s->offset += size;
319 38898 : s->offset_uncached = s->offset;
320 :
321 38898 : s->pos = s->buffer;
322 :
323 38898 : return 0;
324 : }
325 :
326 1 : int64_t stell(STREAM* s)
327 : {
328 1 : return s->offset_uncached + (s->pos - s->buffer);
329 : }
330 :
331 1689 : uint32_t scrc(STREAM*s)
332 : {
333 1689 : return crc32c(s->crc_uncached, s->buffer, s->pos - s->buffer);
334 : }
335 :
336 167 : uint32_t scrc_stream(STREAM*s)
337 : {
338 167 : return s->crc_stream ^ CRC_IV;
339 : }
340 :
341 298863 : int sgetc_uncached(STREAM* s)
342 : {
343 : /* if at the end of the buffer, fill it */
344 298863 : if (s->pos == s->end && sfill(s) != 0)
345 1578 : return EOF;
346 297285 : return *s->pos++;
347 : }
348 :
349 8196 : int sgettok(STREAM* f, char* str, int size)
350 : {
351 8196 : char* i = str;
352 8196 : char* send = str + size;
353 : int c;
354 :
355 : while (1) {
356 54612 : c = sgetc(f);
357 54612 : if (c == EOF) {
358 265 : break;
359 : }
360 54347 : if (c == ' ' || c == '\t') {
361 7665 : sungetc(c, f);
362 7665 : break;
363 : }
364 46682 : if (c == '\n') {
365 : /* remove ending carrige return to support the Windows CR+LF format */
366 266 : if (i != str && i[-1] == '\r')
367 0 : --i;
368 266 : sungetc(c, f);
369 266 : break;
370 : }
371 :
372 46416 : *i++ = c;
373 :
374 46416 : if (i == send) {
375 : /* LCOV_EXCL_START */
376 : return -1;
377 : /* LCOV_EXCL_STOP */
378 : }
379 46416 : }
380 :
381 8196 : *i = 0;
382 :
383 8196 : return i - str;
384 : }
385 :
386 12810001 : int sread(STREAM* f, void* void_data, unsigned size)
387 : {
388 12810001 : unsigned char* data = void_data;
389 :
390 : /* if there is enough space in memory */
391 12810001 : if (sptrlookup(f, size)) {
392 : /* optimized version with all the data in memory */
393 12783371 : unsigned char* pos = sptrget(f);
394 :
395 : /* copy it */
396 217709478 : while (size--)
397 192142736 : *data++ = *pos++;
398 :
399 12783371 : sptrset(f, pos);
400 : } else {
401 : /* standard version using sgetc() */
402 1385569 : while (size--) {
403 1332309 : int c = sgetc(f);
404 1332309 : if (c == EOF) {
405 : /* LCOV_EXCL_START */
406 : return -1;
407 : /* LCOV_EXCL_STOP */
408 : }
409 :
410 1332309 : *data++ = c;
411 : }
412 : }
413 :
414 12810001 : return 0;
415 : }
416 :
417 5448 : int sgetline(STREAM* f, char* str, int size)
418 : {
419 5448 : char* i = str;
420 5448 : char* send = str + size;
421 : int c;
422 :
423 : /* if there is enough data in memory */
424 5448 : if (sptrlookup(f, size)) {
425 : /* optimized version with all the data in memory */
426 0 : unsigned char* pos = sptrget(f);
427 :
428 : while (1) {
429 0 : c = *pos++;
430 0 : if (c == '\n') {
431 : /* remove ending carrige return to support the Windows CR+LF format */
432 0 : if (i != str && i[-1] == '\r')
433 0 : --i;
434 0 : --pos;
435 0 : break;
436 : }
437 :
438 0 : *i++ = c;
439 :
440 0 : if (i == send) {
441 : /* LCOV_EXCL_START */
442 : return -1;
443 : /* LCOV_EXCL_STOP */
444 : }
445 0 : }
446 :
447 0 : sptrset(f, pos);
448 : } else {
449 : while (1) {
450 139951 : c = sgetc(f);
451 139951 : if (c == EOF) {
452 : /* LCOV_EXCL_START */
453 : break;
454 : /* LCOV_EXCL_STOP */
455 : }
456 139951 : if (c == '\n') {
457 : /* remove ending carrige return to support the Windows CR+LF format */
458 5448 : if (i != str && i[-1] == '\r')
459 0 : --i;
460 5448 : sungetc(c, f);
461 5448 : break;
462 : }
463 :
464 134503 : *i++ = c;
465 :
466 134503 : if (i == send) {
467 : /* LCOV_EXCL_START */
468 : return -1;
469 : /* LCOV_EXCL_STOP */
470 : }
471 134503 : }
472 : }
473 :
474 5448 : *i = 0;
475 :
476 5448 : return i - str;
477 : }
478 :
479 5407 : int sgetlasttok(STREAM* f, char* str, int size)
480 : {
481 : int ret;
482 :
483 5407 : ret = sgetline(f, str, size);
484 5407 : if (ret < 0) {
485 : /* LCOV_EXCL_START */
486 : return ret;
487 : /* LCOV_EXCL_STOP */
488 : }
489 :
490 10814 : while (ret > 0 && (str[ret - 1] == ' ' || str[ret - 1] == '\t'))
491 0 : --ret;
492 :
493 5407 : str[ret] = 0;
494 :
495 5407 : return ret;
496 : }
497 :
498 266 : int sgetu32(STREAM* f, uint32_t* value)
499 : {
500 : int c;
501 :
502 266 : c = sgetc(f);
503 266 : if (c >= '0' && c <= '9') {
504 : uint32_t v;
505 :
506 266 : v = c - '0';
507 :
508 266 : c = sgetc(f);
509 532 : while (c >= '0' && c <= '9') {
510 0 : v *= 10;
511 0 : v += c - '0';
512 0 : c = sgetc(f);
513 : }
514 :
515 266 : *value = v;
516 :
517 266 : sungetc(c, f);
518 266 : return 0;
519 : } else {
520 : /* LCOV_EXCL_START */
521 : /* nothing read */
522 : return -1;
523 : /* LCOV_EXCL_STOP */
524 : }
525 : }
526 :
527 18666900 : int sgetb32(STREAM* f, uint32_t* value)
528 : {
529 : uint32_t v;
530 : unsigned char b;
531 : unsigned char s;
532 : int c;
533 :
534 18666900 : v = 0;
535 18666900 : s = 0;
536 : loop:
537 36346607 : c = sgetc(f);
538 36346607 : if (c == EOF) {
539 : /* LCOV_EXCL_START */
540 : return -1;
541 : /* LCOV_EXCL_STOP */
542 : }
543 :
544 36346607 : b = (unsigned char)c;
545 36346607 : if ((b & 0x80) == 0) {
546 17679707 : v |= (uint32_t)b << s;
547 17679707 : s += 7;
548 17679707 : if (s >= 32) {
549 : /* LCOV_EXCL_START */
550 : return -1;
551 : /* LCOV_EXCL_STOP */
552 : }
553 17679707 : goto loop;
554 : }
555 :
556 18666900 : v |= (uint32_t)(b & 0x7f) << s;
557 :
558 18666900 : *value = v;
559 :
560 18666900 : return 0;
561 : }
562 :
563 10849135 : int sgetb64(STREAM* f, uint64_t* value)
564 : {
565 : uint64_t v;
566 : unsigned char b;
567 : unsigned char s;
568 : int c;
569 :
570 10849135 : v = 0;
571 10849135 : s = 0;
572 : loop:
573 39665263 : c = sgetc(f);
574 39665263 : if (c == EOF) {
575 : /* LCOV_EXCL_START */
576 : return -1;
577 : /* LCOV_EXCL_STOP */
578 : }
579 :
580 39665263 : b = (unsigned char)c;
581 39665263 : if ((b & 0x80) == 0) {
582 28816128 : v |= (uint64_t)b << s;
583 28816128 : s += 7;
584 28816128 : if (s >= 64) {
585 : /* LCOV_EXCL_START */
586 : return -1;
587 : /* LCOV_EXCL_STOP */
588 : }
589 28816128 : goto loop;
590 : }
591 :
592 10849135 : v |= (uint64_t)(b & 0x7f) << s;
593 :
594 10849135 : *value = v;
595 :
596 10849135 : return 0;
597 : }
598 :
599 381 : int sgetble32(STREAM* f, uint32_t* value)
600 : {
601 : unsigned char buf[4];
602 :
603 381 : if (sread(f, buf, 4) != 0) {
604 : /* LCOV_EXCL_START */
605 : return -1;
606 : /* LCOV_EXCL_STOP */
607 : }
608 :
609 381 : *value = buf[0] | (uint32_t)buf[1] << 8 | (uint32_t)buf[2] << 16 | (uint32_t)buf[3] << 24;
610 :
611 381 : return 0;
612 : }
613 :
614 3877059 : int sgetbs(STREAM* f, char* str, int size)
615 : {
616 : uint32_t len;
617 :
618 3877059 : if (sgetb32(f, &len) < 0) {
619 : /* LCOV_EXCL_START */
620 : return -1;
621 : /* LCOV_EXCL_STOP */
622 : }
623 :
624 3877059 : if (len + 1 > (uint32_t)size) {
625 : /* LCOV_EXCL_START */
626 : return -1;
627 : /* LCOV_EXCL_STOP */
628 : }
629 :
630 3877058 : str[len] = 0;
631 :
632 3877058 : return sread(f, str, (int)len);
633 : }
634 :
635 20725042 : int swrite(const void* void_data, unsigned size, STREAM* f)
636 : {
637 20725042 : const unsigned char* data = void_data;
638 :
639 : /* if there is enough space in memory */
640 20725042 : if (sptrlookup(f, size)) {
641 : /* optimized version with all the data in memory */
642 20718864 : unsigned char* pos = sptrget(f);
643 :
644 : /**
645 : * Update the crc *before* writing the data in the buffer
646 : *
647 : * This must be done before the memory write,
648 : * to be able to detect memory errors on the buffer,
649 : * happening before we write it on the file.
650 : */
651 20718864 : f->crc_stream = crc32c_plain(f->crc_stream, data, size);
652 :
653 : /* copy it */
654 172990467 : while (size--)
655 131552739 : *pos++ = *data++;
656 :
657 20718864 : sptrset(f, pos);
658 : } else {
659 : /* standard version using sputc() */
660 202580 : while (size--) {
661 190224 : if (sputc(*data++, f) != 0) {
662 : /* LCOV_EXCL_START */
663 : return -1;
664 : /* LCOV_EXCL_STOP */
665 : }
666 : }
667 : }
668 :
669 20725042 : return 0;
670 : }
671 :
672 9127415 : int sputb32(uint32_t value, STREAM* s)
673 : {
674 : unsigned char b;
675 : unsigned char buf[16];
676 : unsigned i;
677 :
678 9127415 : i = 0;
679 : loop:
680 17834227 : b = value & 0x7f;
681 17834227 : value >>= 7;
682 :
683 17834227 : if (value) {
684 8706812 : buf[i++] = b;
685 8706812 : goto loop;
686 : }
687 :
688 9127415 : buf[i++] = b | 0x80;
689 :
690 9127415 : return swrite(buf, i, s);
691 : }
692 :
693 5317120 : int sputb64(uint64_t value, STREAM* s)
694 : {
695 : unsigned char b;
696 : unsigned char buf[16];
697 : unsigned i;
698 :
699 5317120 : i = 0;
700 : loop:
701 19434306 : b = value & 0x7f;
702 19434306 : value >>= 7;
703 :
704 19434306 : if (value) {
705 14117186 : buf[i++] = b;
706 14117186 : goto loop;
707 : }
708 :
709 5317120 : buf[i++] = b | 0x80;
710 :
711 5317120 : return swrite(buf, i, s);
712 : }
713 :
714 167 : int sputble32(uint32_t value, STREAM* s)
715 : {
716 : unsigned char buf[4];
717 :
718 167 : buf[0] = value & 0xFF;
719 167 : buf[1] = (value >> 8) & 0xFF;
720 167 : buf[2] = (value >> 16) & 0xFF;
721 167 : buf[3] = (value >> 24) & 0xFF;
722 :
723 167 : return swrite(buf, 4, s);
724 : }
725 :
726 1898005 : int sputbs(const char* str, STREAM* f)
727 : {
728 1898005 : size_t len = strlen(str);
729 :
730 1898005 : if (sputb32(len, f) != 0) {
731 : /* LCOV_EXCL_START */
732 : return -1;
733 : /* LCOV_EXCL_STOP */
734 : }
735 :
736 1898005 : return swrite(str, len, f);
737 : }
738 :
739 : #if HAVE_FSYNC
740 151 : int ssync(STREAM* s)
741 : {
742 : unsigned i;
743 :
744 1163 : for (i = 0; i < s->handle_size; ++i) {
745 1012 : if (fsync(s->handle[i].f) != 0) {
746 : /* LCOV_EXCL_START */
747 : s->state = STREAM_STATE_ERROR;
748 : s->state_index = i;
749 : return -1;
750 : /* LCOV_EXCL_STOP */
751 : }
752 : }
753 :
754 151 : return 0;
755 : }
756 : #endif
757 :
|