/src/libjxl/tools/streaming_fuzzer.cc
Line | Count | Source |
1 | | // Copyright (c) the JPEG XL Project Authors. All rights reserved. |
2 | | // |
3 | | // Use of this source code is governed by a BSD-style |
4 | | // license that can be found in the LICENSE file. |
5 | | |
6 | | #include <jxl/codestream_header.h> |
7 | | #include <jxl/color_encoding.h> |
8 | | #include <jxl/decode.h> |
9 | | #include <jxl/decode_cxx.h> |
10 | | #include <jxl/encode.h> |
11 | | #include <jxl/encode_cxx.h> |
12 | | #include <jxl/thread_parallel_runner.h> |
13 | | #include <jxl/thread_parallel_runner_cxx.h> |
14 | | #include <jxl/types.h> |
15 | | |
16 | | #include <array> |
17 | | #include <cstdint> |
18 | | #include <cstdio> |
19 | | #include <cstdlib> |
20 | | #include <cstring> |
21 | | #include <string> |
22 | | #include <vector> |
23 | | |
24 | | #include "lib/jxl/base/common.h" |
25 | | #include "lib/jxl/base/compiler_specific.h" |
26 | | #include "lib/jxl/base/status.h" |
27 | | #include "lib/jxl/fuzztest.h" |
28 | | #include "tools/tracking_memory_manager.h" |
29 | | |
30 | | namespace { |
31 | | |
32 | | using ::jpegxl::tools::kGiB; |
33 | | using ::jpegxl::tools::TrackingMemoryManager; |
34 | | using ::jxl::Status; |
35 | | using ::jxl::StatusOr; |
36 | | |
37 | | constexpr size_t kMemoryCap = kGiB; // enough for 85.3MPx without overhead |
38 | | constexpr size_t kBaseMaxSize = 16 << 20; // 16MPx |
39 | | |
40 | 51.9k | void CheckImpl(bool ok, const char* conndition, const char* file, int line) { |
41 | 51.9k | if (!ok) { |
42 | 0 | fprintf(stderr, "Check(%s) failed at %s:%d\n", conndition, file, line); |
43 | 0 | JXL_CRASH(); |
44 | 0 | } |
45 | 51.9k | } |
46 | 51.9k | #define Check(OK) CheckImpl((OK), #OK, __FILE__, __LINE__) |
47 | | |
48 | | struct FuzzSpec { |
49 | | uint32_t xsize; |
50 | | uint32_t ysize; |
51 | | bool grayscale; |
52 | | bool alpha; |
53 | | uint8_t bit_depth; // 1 - 16 |
54 | | |
55 | | struct IntOptionSpec { |
56 | | JxlEncoderFrameSettingId flag; |
57 | | std::string name; |
58 | | int min; |
59 | | int max; |
60 | | int value; |
61 | | }; |
62 | | |
63 | | #define INT_OPTION(FLAG, MIN_V, MAX_V, V) \ |
64 | | IntOptionSpec { FLAG, #FLAG, MIN_V, MAX_V, V } |
65 | | |
66 | | std::vector<IntOptionSpec> int_options = { |
67 | | INT_OPTION(JXL_ENC_FRAME_SETTING_EFFORT, 1, 9, 0), |
68 | | INT_OPTION(JXL_ENC_FRAME_SETTING_DECODING_SPEED, 0, 4, 0), |
69 | | INT_OPTION(JXL_ENC_FRAME_SETTING_NOISE, -1, 1, 0), |
70 | | INT_OPTION(JXL_ENC_FRAME_SETTING_DOTS, -1, 1, 0), |
71 | | INT_OPTION(JXL_ENC_FRAME_SETTING_PATCHES, -1, 1, 0), |
72 | | INT_OPTION(JXL_ENC_FRAME_SETTING_EPF, -1, 3, 0), |
73 | | INT_OPTION(JXL_ENC_FRAME_SETTING_GABORISH, -1, 1, 0), |
74 | | INT_OPTION(JXL_ENC_FRAME_SETTING_MODULAR, -1, 1, 0), |
75 | | INT_OPTION(JXL_ENC_FRAME_SETTING_KEEP_INVISIBLE, -1, 1, 0), |
76 | | INT_OPTION(JXL_ENC_FRAME_SETTING_RESPONSIVE, -1, 1, 0), |
77 | | INT_OPTION(JXL_ENC_FRAME_SETTING_PROGRESSIVE_AC, -1, 1, 0), |
78 | | INT_OPTION(JXL_ENC_FRAME_SETTING_QPROGRESSIVE_AC, -1, 1, 0), |
79 | | INT_OPTION(JXL_ENC_FRAME_SETTING_PROGRESSIVE_DC, -1, 1, 0), |
80 | | INT_OPTION(JXL_ENC_FRAME_SETTING_PALETTE_COLORS, -1, 255, 0), |
81 | | INT_OPTION(JXL_ENC_FRAME_SETTING_LOSSY_PALETTE, -1, 1, 0), |
82 | | INT_OPTION(JXL_ENC_FRAME_SETTING_COLOR_TRANSFORM, -1, 2, 0), |
83 | | INT_OPTION(JXL_ENC_FRAME_SETTING_MODULAR_COLOR_SPACE, -1, 41, 0), |
84 | | INT_OPTION(JXL_ENC_FRAME_SETTING_MODULAR_GROUP_SIZE, -1, 3, 0), |
85 | | INT_OPTION(JXL_ENC_FRAME_SETTING_MODULAR_PREDICTOR, -1, 15, 0), |
86 | | INT_OPTION(JXL_ENC_FRAME_SETTING_MODULAR_NB_PREV_CHANNELS, -1, 11, 0), |
87 | | }; |
88 | | |
89 | | #undef INT_OPTION |
90 | | |
91 | | struct FloatOptionSpec { |
92 | | JxlEncoderFrameSettingId flag; |
93 | | float possible_values[4]; |
94 | | float value; |
95 | | }; |
96 | | |
97 | | std::vector<FloatOptionSpec> float_options = { |
98 | | FloatOptionSpec{JXL_ENC_FRAME_SETTING_CHANNEL_COLORS_GLOBAL_PERCENT, |
99 | | {-1, 0, 50, 100}, |
100 | | -1}, |
101 | | FloatOptionSpec{JXL_ENC_FRAME_SETTING_CHANNEL_COLORS_GROUP_PERCENT, |
102 | | {-1, 0, 50, 100}, |
103 | | -1}, |
104 | | FloatOptionSpec{JXL_ENC_FRAME_SETTING_MODULAR_MA_TREE_LEARNING_PERCENT, |
105 | | {-1, 0, 50, 100}, |
106 | | -1}, |
107 | | FloatOptionSpec{ |
108 | | JXL_ENC_FRAME_SETTING_PHOTON_NOISE, {-1, 200, 1600, 10000}, -1}, |
109 | | }; |
110 | | |
111 | | uint8_t num_threads; |
112 | | |
113 | | float distance; // 0.01 - 25 |
114 | | |
115 | | // Tiled to cover the entire image area. |
116 | | uint16_t pixel_data[4][64][64]; |
117 | | |
118 | 630 | static FuzzSpec FromData(const uint8_t* data, size_t len) { |
119 | 630 | size_t pos = 0; |
120 | 20.6M | auto u8 = [&]() -> uint8_t { |
121 | 20.6M | if (pos == len) return 0; |
122 | 5.55M | return data[pos++]; |
123 | 20.6M | }; |
124 | 1.26k | auto b1 = [&]() -> bool { return static_cast<bool>(u8() % 2); }; |
125 | 10.3M | auto u16 = [&]() -> uint16_t { return (uint16_t{u8()} << 8) | u8(); }; |
126 | 630 | FuzzSpec spec; |
127 | | // TODO(eustas): allow dimensions to be 130k |
128 | 630 | spec.xsize = uint32_t{u16()} + 1; |
129 | 630 | spec.ysize = uint32_t{u16()} + 1; |
130 | 630 | spec.grayscale = b1(); |
131 | 630 | spec.alpha = b1(); |
132 | 630 | spec.bit_depth = u8() % 16 + 1; |
133 | | // constants chosen so to cover the entire 0.01 - 25 range. |
134 | 630 | bool lossless = ((u8() % 2) == 1); |
135 | 630 | spec.distance = lossless ? 0.0 : 0.01 + 0.00038132 * u16(); |
136 | | |
137 | 630 | spec.num_threads = u8() & 0xF; |
138 | | |
139 | 12.6k | for (auto& int_opt : spec.int_options) { |
140 | 12.6k | int_opt.value = u8() % (int_opt.max - int_opt.min + 1) + int_opt.min; |
141 | 12.6k | } |
142 | | |
143 | 630 | Check(spec.int_options[15].flag == JXL_ENC_FRAME_SETTING_COLOR_TRANSFORM); |
144 | 630 | if (!lossless || spec.int_options[15].value == 0) { |
145 | 447 | Check(spec.float_options[2].flag == |
146 | 447 | JXL_ENC_FRAME_SETTING_MODULAR_MA_TREE_LEARNING_PERCENT); |
147 | 447 | spec.float_options[2].possible_values[1] = 1; |
148 | 447 | } |
149 | | |
150 | 2.52k | for (auto& float_opt : spec.float_options) { |
151 | 2.52k | float_opt.value = float_opt.possible_values[u8() % 4]; |
152 | 2.52k | } |
153 | | |
154 | 630 | Check(spec.int_options[7].flag == JXL_ENC_FRAME_SETTING_MODULAR); |
155 | 630 | bool modular = (spec.int_options[7].value == 1); |
156 | 630 | Check(spec.int_options[18].flag == JXL_ENC_FRAME_SETTING_MODULAR_PREDICTOR); |
157 | 630 | bool slow_predictor = (spec.int_options[18].value >= 14); |
158 | 630 | uint64_t max_size = kBaseMaxSize; |
159 | 630 | if (modular && slow_predictor) max_size /= 2; |
160 | 630 | if (sizeof(size_t) == 4) max_size /= 1.5; |
161 | 630 | constexpr size_t group_dim = 256; |
162 | 630 | uint64_t in_mem_xsize = jxl::RoundUpTo(spec.xsize, group_dim); |
163 | 630 | if (in_mem_xsize * spec.ysize > max_size) { |
164 | 14 | spec.ysize = max_size / in_mem_xsize; |
165 | 14 | Check(spec.ysize > 0); |
166 | 14 | } |
167 | 630 | uint64_t in_mem_ysize = jxl::RoundUpTo(spec.ysize, group_dim); |
168 | 630 | if (spec.xsize * in_mem_ysize > max_size) { |
169 | 7 | spec.xsize = max_size / in_mem_ysize; |
170 | 7 | Check(spec.xsize > 0); |
171 | 7 | } |
172 | | |
173 | 2.52k | for (auto& x : spec.pixel_data) { |
174 | 161k | for (auto& y : x) { |
175 | 10.3M | for (auto& p : y) { |
176 | 10.3M | p = u16(); |
177 | 10.3M | } |
178 | 161k | } |
179 | 2.52k | } |
180 | | |
181 | 630 | if (false) { |
182 | 0 | fprintf(stderr, "Image size: %d X %d, d=%f, num_threads: %d\n", |
183 | 0 | spec.xsize, spec.ysize, spec.distance, spec.num_threads); |
184 | 0 | for (auto& int_opt : spec.int_options) { |
185 | 0 | fprintf(stderr, "%s = %d\n", int_opt.name.c_str(), int_opt.value); |
186 | 0 | } |
187 | 0 | } |
188 | | |
189 | 630 | return spec; |
190 | 630 | } |
191 | | }; |
192 | | |
193 | | StatusOr<std::vector<uint8_t>> Encode(const FuzzSpec& spec, |
194 | | TrackingMemoryManager& memory_manager, |
195 | 1.25k | bool streaming) { |
196 | 1.25k | auto runner = JxlThreadParallelRunnerMake(nullptr, spec.num_threads); |
197 | 1.25k | JxlEncoderPtr enc_ptr = JxlEncoderMake(memory_manager.get()); |
198 | 1.25k | JxlEncoder* enc = enc_ptr.get(); |
199 | | |
200 | 1.25k | Check(JxlEncoderSetParallelRunner(enc, JxlThreadParallelRunner, |
201 | 1.25k | runner.get()) == JXL_ENC_SUCCESS); |
202 | 1.25k | JxlEncoderFrameSettings* frame_settings = |
203 | 1.25k | JxlEncoderFrameSettingsCreate(enc, nullptr); |
204 | 1.25k | Check(frame_settings != nullptr); |
205 | | |
206 | 1.25k | Check(JxlEncoderSetFrameDistance(frame_settings, spec.distance) == |
207 | 1.25k | JXL_ENC_SUCCESS); |
208 | | |
209 | 25.1k | for (const auto& opt : spec.int_options) { |
210 | 25.1k | Check(JxlEncoderFrameSettingsSetOption(frame_settings, opt.flag, |
211 | 25.1k | opt.value) == JXL_ENC_SUCCESS); |
212 | 25.1k | } |
213 | 5.02k | for (const auto& opt : spec.float_options) { |
214 | 5.02k | if (opt.value != -1) { |
215 | 2.52k | Check(JxlEncoderFrameSettingsSetFloatOption( |
216 | 2.52k | frame_settings, opt.flag, opt.value) == JXL_ENC_SUCCESS); |
217 | 2.52k | } |
218 | 5.02k | } |
219 | | |
220 | 1.25k | Check(JxlEncoderFrameSettingsSetOption(frame_settings, |
221 | 1.25k | JXL_ENC_FRAME_SETTING_BUFFERING, |
222 | 1.25k | streaming ? 3 : 0) == JXL_ENC_SUCCESS); |
223 | | |
224 | 1.25k | JxlBasicInfo basic_info; |
225 | 1.25k | JxlEncoderInitBasicInfo(&basic_info); |
226 | 1.25k | basic_info.num_color_channels = spec.grayscale ? 1 : 3; |
227 | 1.25k | basic_info.xsize = spec.xsize; |
228 | 1.25k | basic_info.ysize = spec.ysize; |
229 | 1.25k | basic_info.bits_per_sample = spec.bit_depth; |
230 | 1.25k | bool lossless = (spec.distance == 0.0f); |
231 | 1.25k | basic_info.uses_original_profile = TO_JXL_BOOL(lossless); |
232 | 1.25k | uint32_t nchan = basic_info.num_color_channels; |
233 | 1.25k | if (spec.alpha) { |
234 | 320 | nchan += 1; |
235 | 320 | basic_info.alpha_bits = spec.bit_depth; |
236 | 320 | basic_info.num_extra_channels = 1; |
237 | 320 | } |
238 | 1.25k | Check(JxlEncoderSetBasicInfo(enc, &basic_info) == JXL_ENC_SUCCESS); |
239 | 1.25k | if (spec.alpha) { |
240 | 320 | JxlExtraChannelInfo info; |
241 | 320 | memset(&info, 0, sizeof(info)); |
242 | 320 | info.type = JxlExtraChannelType::JXL_CHANNEL_ALPHA; |
243 | 320 | info.bits_per_sample = spec.bit_depth; |
244 | 320 | JxlEncoderSetExtraChannelInfo(enc, 0, &info); |
245 | 320 | } |
246 | 1.25k | JxlColorEncoding color_encoding; |
247 | 1.25k | memset(&color_encoding, 0, sizeof(color_encoding)); |
248 | 1.25k | color_encoding.color_space = spec.grayscale |
249 | 1.25k | ? JxlColorSpace::JXL_COLOR_SPACE_GRAY |
250 | 1.25k | : JxlColorSpace::JXL_COLOR_SPACE_RGB; |
251 | 1.25k | color_encoding.transfer_function = |
252 | 1.25k | JxlTransferFunction::JXL_TRANSFER_FUNCTION_SRGB; |
253 | 1.25k | color_encoding.primaries = JxlPrimaries::JXL_PRIMARIES_2100; |
254 | 1.25k | color_encoding.white_point = JxlWhitePoint::JXL_WHITE_POINT_D65; |
255 | 1.25k | color_encoding.rendering_intent = |
256 | 1.25k | JxlRenderingIntent::JXL_RENDERING_INTENT_RELATIVE; |
257 | 1.25k | Check(JxlEncoderSetColorEncoding(enc, &color_encoding) == JXL_ENC_SUCCESS); |
258 | | |
259 | 1.25k | JxlFrameHeader frame_header; |
260 | 1.25k | JxlEncoderInitFrameHeader(&frame_header); |
261 | | // TODO(szabadka) Add more frame header options. |
262 | 1.25k | Check(JxlEncoderSetFrameHeader(frame_settings, &frame_header) == |
263 | 1.25k | JXL_ENC_SUCCESS); |
264 | 1.25k | JxlPixelFormat pixelformat = {nchan, JXL_TYPE_UINT16, JXL_LITTLE_ENDIAN, 0}; |
265 | 1.25k | std::vector<uint16_t> pixels(spec.xsize * static_cast<uint64_t>(spec.ysize) * |
266 | 1.25k | nchan); |
267 | 3.16M | for (size_t y = 0; y < spec.ysize; y++) { |
268 | 558M | for (size_t x = 0; x < spec.xsize; x++) { |
269 | 1.66G | for (size_t c = 0; c < nchan; c++) { |
270 | | // TODO(eustas): make it less regular for tiles except (0, 0) |
271 | 1.10G | pixels[(y * spec.xsize + x) * nchan + c] = |
272 | 1.10G | spec.pixel_data[c][y % 64][x % 64]; |
273 | 1.10G | } |
274 | 555M | } |
275 | 3.16M | } |
276 | 1.25k | JxlEncoderStatus status = |
277 | 1.25k | JxlEncoderAddImageFrame(frame_settings, &pixelformat, pixels.data(), |
278 | 1.25k | pixels.size() * sizeof(uint16_t)); |
279 | | // TODO(eustas): update when API will provide OOM status. |
280 | 1.25k | if (memory_manager.seen_oom) { |
281 | | // Actually, that is fine. |
282 | 0 | return JXL_FAILURE("OOM"); |
283 | 0 | } |
284 | 1.25k | Check(status == JXL_ENC_SUCCESS); |
285 | 1.25k | JxlEncoderCloseInput(enc); |
286 | | // Reading compressed output |
287 | 1.25k | JxlEncoderStatus process_result = JXL_ENC_NEED_MORE_OUTPUT; |
288 | 1.25k | std::vector<uint8_t> buf(1024); |
289 | 1.25k | size_t written = 0; |
290 | 6.39k | while (process_result == JXL_ENC_NEED_MORE_OUTPUT) { |
291 | 5.13k | buf.resize(buf.size() * 2); |
292 | 5.13k | uint8_t* next_out = buf.data() + written; |
293 | 5.13k | size_t avail_out = buf.size() - written; |
294 | 5.13k | process_result = JxlEncoderProcessOutput(enc, &next_out, &avail_out); |
295 | 5.13k | written = next_out - buf.data(); |
296 | 5.13k | } |
297 | | // TODO(eustas): update when API will provide OOM status. |
298 | 1.25k | if (memory_manager.seen_oom) { |
299 | | // Actually, that is fine. |
300 | 5 | return JXL_FAILURE("OOM"); |
301 | 5 | } |
302 | 1.25k | Check(process_result == JXL_ENC_SUCCESS); |
303 | 1.25k | buf.resize(written); |
304 | | |
305 | 1.25k | return buf; |
306 | 1.25k | } |
307 | | |
308 | | Status Decode(const std::vector<uint8_t>& data, |
309 | | TrackingMemoryManager& memory_manager, |
310 | 1.25k | std::vector<float>& pixels) { |
311 | | // Multi-threaded parallel runner. |
312 | 1.25k | auto dec = JxlDecoderMake(memory_manager.get()); |
313 | 1.25k | Check(JxlDecoderSubscribeEvents(dec.get(), |
314 | 1.25k | JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE) == |
315 | 1.25k | JXL_DEC_SUCCESS); |
316 | | |
317 | 1.25k | JxlBasicInfo info; |
318 | 1.25k | JxlPixelFormat format = {3, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0}; |
319 | | |
320 | 1.25k | pixels.clear(); |
321 | | |
322 | 1.25k | JxlDecoderSetInput(dec.get(), data.data(), data.size()); |
323 | 1.25k | JxlDecoderCloseInput(dec.get()); |
324 | | |
325 | 3.75k | for (;;) { |
326 | 3.75k | JxlDecoderStatus status = JxlDecoderProcessInput(dec.get()); |
327 | | |
328 | 3.75k | if (status == JXL_DEC_BASIC_INFO) { |
329 | 1.25k | Check(JxlDecoderGetBasicInfo(dec.get(), &info) == JXL_DEC_SUCCESS); |
330 | 2.50k | } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) { |
331 | 1.25k | size_t buffer_size; |
332 | 1.25k | Check(JxlDecoderImageOutBufferSize(dec.get(), &format, &buffer_size) == |
333 | 1.25k | JXL_DEC_SUCCESS); |
334 | 1.25k | pixels.resize(buffer_size / sizeof(float)); |
335 | 1.25k | void* pixels_buffer = static_cast<void*>(pixels.data()); |
336 | 1.25k | size_t pixels_buffer_size = pixels.size() * sizeof(float); |
337 | 1.25k | Check(JxlDecoderSetImageOutBuffer(dec.get(), &format, pixels_buffer, |
338 | 1.25k | pixels_buffer_size) == JXL_DEC_SUCCESS); |
339 | 1.25k | } else if (status == JXL_DEC_FULL_IMAGE || status == JXL_DEC_SUCCESS) { |
340 | 1.25k | return true; |
341 | 1.25k | } else { |
342 | | // TODO(eustas): update when API will provide OOM status. |
343 | 0 | if (memory_manager.seen_oom) { |
344 | | // Actually, that is fine. |
345 | 0 | return JXL_FAILURE("OOM"); |
346 | 0 | } |
347 | | // Unexpected status |
348 | 0 | Check(false); |
349 | 0 | } |
350 | 3.75k | } |
351 | 1.25k | } |
352 | | |
353 | 630 | void Run(const FuzzSpec& spec) { |
354 | 630 | size_t memory_cap = kMemoryCap; |
355 | 630 | size_t total_cap_multiplier = 5; |
356 | 630 | if (spec.xsize < 64 || spec.ysize < 64) { |
357 | 442 | total_cap_multiplier = 20; |
358 | 442 | } |
359 | 630 | TrackingMemoryManager memory_manager{memory_cap, |
360 | 630 | memory_cap * total_cap_multiplier}; |
361 | | |
362 | 630 | std::vector<uint8_t> enc_default; |
363 | 630 | std::vector<uint8_t> enc_streaming; |
364 | | |
365 | 630 | const auto encode = [&]() -> Status { |
366 | | // It is not clear, which approach eats more memory. |
367 | 630 | JXL_ASSIGN_OR_RETURN(enc_default, Encode(spec, memory_manager, false)); |
368 | 625 | Check(memory_manager.Reset()); |
369 | 625 | JXL_ASSIGN_OR_RETURN(enc_streaming, Encode(spec, memory_manager, true)); |
370 | 625 | Check(memory_manager.Reset()); |
371 | 625 | return true; |
372 | 625 | }; |
373 | | // It is fine, if encoder OOMs. |
374 | 630 | if (!encode()) return; |
375 | | |
376 | | // It is NOT OK, if decoder OOMs - it should not consume more than encoder. |
377 | 625 | std::vector<float> dec_default; |
378 | 625 | Check(Decode(enc_default, memory_manager, dec_default)); |
379 | 625 | Check(memory_manager.Reset()); |
380 | 625 | std::vector<float> dec_streaming; |
381 | 625 | Check(Decode(enc_streaming, memory_manager, dec_streaming)); |
382 | 625 | Check(memory_manager.Reset()); |
383 | | |
384 | 625 | Check(dec_default.size() == dec_streaming.size()); |
385 | | |
386 | 625 | Check(spec.int_options[0].flag == JXL_ENC_FRAME_SETTING_EFFORT); |
387 | 625 | int effort = spec.int_options[0].value; |
388 | 625 | std::array<float, 10> kThreshold = {0.00f, 0.05f, 0.05f, 0.05f, 0.05f, |
389 | 625 | 0.0625f, 0.0625f, 0.0625f, 0.10f, 0.10f}; |
390 | 625 | float threshold = kThreshold[effort]; |
391 | | |
392 | 625 | int outlier_count = 0; |
393 | 713M | for (size_t i = 0; i < dec_default.size(); ++i) { |
394 | 713M | float d1 = ::jxl::Clamp1(dec_default[i], 0.0f, 1.0f); |
395 | 713M | float d2 = ::jxl::Clamp1(dec_streaming[i], 0.0f, 1.0f); |
396 | 713M | float abs_diff = std::abs(d1 - d2); |
397 | 713M | if (abs_diff > threshold) outlier_count++; |
398 | 713M | } |
399 | 625 | if (false) { |
400 | 0 | fprintf(stderr, "Number of outlier values: %d / %d\n", outlier_count, |
401 | 0 | static_cast<int>(dec_default.size())); |
402 | 0 | } |
403 | 625 | Check(outlier_count == 0); |
404 | 625 | } |
405 | | |
406 | 630 | int DoTestOneInput(const uint8_t* data, size_t size) { |
407 | 630 | auto spec = FuzzSpec::FromData(data, size); |
408 | | |
409 | 630 | Run(spec); |
410 | 630 | return 0; |
411 | 630 | } |
412 | | |
413 | | } // namespace |
414 | | |
415 | 63.5k | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
416 | 63.5k | return DoTestOneInput(data, size); |
417 | 63.5k | } |
418 | | |
419 | 0 | void TestOneInput(const std::vector<uint8_t>& data) { |
420 | 0 | DoTestOneInput(data.data(), data.size()); |
421 | 0 | } |
422 | | |
423 | | FUZZ_TEST(StreamingFuzzTest, TestOneInput); |