11103:完整读取二进制流
题目
从流的当前位置读取到文件结束,以动态存储返回全部原始字节。读取失败或分配失败时,输出对象必须保持原值。
解析
不能依赖 fseek 和 ftell,因为流可能不可定位。使用固定大小的局部块反复读取,再把每块追加到动态存储即可支持普通文件、管道等不同来源。
全部状态先保存在函数内部。只有确认到达文件结束且没有读取错误时,才写入两个输出对象。这样任何失败都只需释放局部存储,不会破坏调用方原有结果。
解析
c
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static bool append_block(
unsigned char **data,
size_t *size,
size_t *capacity,
const unsigned char *source,
size_t count
) {
if (count > SIZE_MAX - *size) {
return false;
}
size_t required = *size + count;
if (required > *capacity) {
size_t next = *capacity == 0 ? 4096 : *capacity;
while (next < required) {
if (next > SIZE_MAX / 2) {
next = required;
break;
}
next *= 2;
}
unsigned char *replacement = realloc(*data, next);
if (replacement == NULL) {
return false;
}
*data = replacement;
*capacity = next;
}
memcpy(*data + *size, source, count);
*size = required;
return true;
}
bool read_all(
FILE *stream,
unsigned char **out_data,
size_t *out_size
) {
if (stream == NULL || out_data == NULL || out_size == NULL) {
return false;
}
unsigned char *data = NULL;
size_t size = 0;
size_t capacity = 0;
unsigned char block[4096];
for (;;) {
size_t count = fread(block, 1, sizeof block, stream);
if (count > 0 &&
!append_block(
&data,
&size,
&capacity,
block,
count
)) {
free(data);
return false;
}
if (count < sizeof block) {
if (ferror(stream)) {
free(data);
return false;
}
if (feof(stream)) {
*out_data = data;
*out_size = size;
return true;
}
free(data);
return false;
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88