1 /*
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright © 2023 Keith Packard
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 *
18 * 3. Neither the name of the copyright holder nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
33 * OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35 #define _DEFAULT_SOURCE
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <stdint.h>
39 #include <string.h>
40
41 #ifndef TEST_FILE_NAME
42 #define TEST_FILE_NAME "FIO.TXT"
43 #endif
44
45 #define check(condition, message) do { \
46 if (!(condition)) { \
47 printf("%s: %s\n", message, #condition); \
48 exit(1); \
49 } \
50 } while(0)
51
52 #define RAND_MUL 1664525
53 #define RAND_ADD 1013904223
54
55 static inline unsigned long
my_permute(long input)56 my_permute(long input)
57 {
58 return input * RAND_MUL + RAND_ADD;
59 }
60
61 static inline uint8_t
test_value(long offset,int generation)62 test_value(long offset, int generation)
63 {
64 return (uint8_t) my_permute(offset * (generation + 1));
65 }
66
67 static void
fill_buf(long offset,int generation,void * b,size_t len)68 fill_buf(long offset, int generation, void *b, size_t len)
69 {
70 uint8_t *bp = b;
71 while (len--)
72 *bp++ = test_value(offset++, generation);
73 }
74
75 static long
check_buf(long offset,int generation,void * b,size_t len)76 check_buf(long offset, int generation, void *b, size_t len)
77 {
78 uint8_t *bp = b;
79 while (len--) {
80 if (test_value(offset, generation) != *bp++) {
81 return offset;
82 }
83 offset++;
84 }
85 return -1L;
86 }
87
88 #define BUF_SIZE 2048
89
90 static uint8_t out[BUF_SIZE];
91 static uint8_t in[BUF_SIZE];
92
93 static long offsets[BUF_SIZE];
94
95 static void
random_offsets(int generation,int num_ops,size_t per_op)96 random_offsets(int generation, int num_ops, size_t per_op)
97 {
98 int i, j;
99
100 for (i = 0; i < num_ops; i++)
101 offsets[i] = per_op * i;
102
103 for (i = 0; i < num_ops - 1; i++) {
104 j = my_permute(i * num_ops * per_op * generation) % (num_ops - i) + i;
105 long tmp = offsets[i];
106 offsets[i] = offsets[j];
107 offsets[j] = tmp;
108 }
109 }
110
111 int
main(int argc,char ** argv)112 main(int argc, char **argv)
113 {
114 FILE *f;
115 int generation = 0;
116 size_t ret;
117 size_t size;
118 size_t nmemb, per_op;
119 off_t offset;
120 char *file_name = TEST_FILE_NAME;
121 int order;
122 int num_ops;
123 int op;
124 int status = 0;
125 int repeat = 10000;
126 long failed;
127
128 if (argc > 1) {
129 repeat = atoi(argv[1]);
130 if (!repeat)
131 repeat = 10000;
132 }
133
134 printf("test file %s repeat %d\n", file_name, repeat);
135
136 /* Make sure we can create a file, write contents and read them back */
137
138 for (size = 1; size < 16; size <<= 1) {
139 nmemb = BUF_SIZE / size;
140 for (order = 0; order < 2; order++) {
141 for (per_op = nmemb; per_op >= 1; per_op /= 2) {
142
143 num_ops = nmemb / per_op;
144
145 f = fopen(file_name, "w");
146 check(f != NULL, "fopen w");
147
148 fill_buf(0, generation, out, BUF_SIZE);
149
150 if (order == 0) {
151 for (op = 0; op < num_ops; op++) {
152 ret = fwrite(out + op * size * per_op, size, per_op, f);
153 check(ret == per_op, "fwrite ordered");
154 }
155 } else {
156 random_offsets(generation, num_ops, per_op);
157 offset = 0;
158 for (op = 0; op < num_ops; op++) {
159 fseek(f, size * offsets[op] - offset, SEEK_CUR);
160 ret = fwrite(out + size * offsets[op], size, per_op, f);
161 offset = size * offsets[op] + size * per_op;
162 check(ret == per_op, "fwrite random");
163 }
164 }
165 fclose(f);
166
167 memset(in, 0xcd, BUF_SIZE);
168
169 f = fopen(file_name, "r");
170 check(f != NULL, "fopen r");
171 if (order == 0) {
172 for (op = 0; op < num_ops; op++) {
173 ret = fread(in + op * size * per_op, size, per_op, f);
174 check(ret == per_op, "fread ordered");
175 }
176 } else {
177 random_offsets(generation * 1146533039, num_ops, per_op);
178 offset = 0;
179 for (op = 0; op < num_ops; op++) {
180 fseek(f, size * offsets[op] - offset, SEEK_CUR);
181 ret = fread(in + size * offsets[op], size, per_op, f);
182 offset = size * offsets[op] + size * per_op;
183 check(ret == per_op, "fread random");
184 }
185 }
186
187 fclose(f);
188 failed = check_buf(0, generation, in, BUF_SIZE);
189 if (failed != -1L) {
190 printf("check failed at offset %ld size %zd order %d per_op %zd\n",
191 failed, size, order, per_op);
192 status = 1;
193 }
194 generation++;
195 }
196 }
197 }
198
199 /* Now do a mix of read/write operations */
200
201 f = fopen(file_name, "w+");
202 check(f != NULL, "fopen w+");
203
204 memset(out, '\0', BUF_SIZE);
205
206 ret = fwrite(out, 1, BUF_SIZE, f);
207 check(ret == BUF_SIZE, "fwrite mixed setup");
208
209 for (int generation = 0; generation < repeat; generation++) {
210
211 if (random() & 0x8) {
212 long write_offset, write_len;
213
214 write_offset = random() % BUF_SIZE;
215 write_len = random() % (BUF_SIZE - write_offset);
216
217 fill_buf(write_offset, generation, out + write_offset, write_len);
218
219 fseek(f, write_offset, SEEK_SET);
220
221 ret = fwrite(out + write_offset, 1, write_len, f);
222 check(ret == (size_t) write_len, "fwrite mixed");
223 } else {
224 long read_offset, read_len, i;
225 read_offset = random() % BUF_SIZE;
226 read_len = random() % (BUF_SIZE - read_offset);
227
228 fseek(f, read_offset, SEEK_SET);
229
230 /* Make sure we can unget a char and have it appear on the input */
231 if (generation % 10 == 9) {
232 int c = getc(f);
233 ungetc(c + 1, f);
234 }
235
236 ret = fread(in + read_offset, 1, read_len, f);
237 check(ret == (size_t) read_len, "fread mixed");
238
239 for (i = 0; i < read_len; i++) {
240 uint8_t expect = out[read_offset + i];
241 if (generation % 10 == 9 && i == 0)
242 expect++;
243 if (in[read_offset + i] != expect) {
244 printf("mixed check failed at offset %ld generation %d\n",
245 read_offset + i, generation);
246 status = 1;
247 }
248 }
249 }
250 }
251
252 (void) remove(file_name);
253
254 exit(status);
255 }
256