summaryrefslogtreecommitdiff
path: root/src/sixel-fuzzer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/sixel-fuzzer.cc')
-rw-r--r--src/sixel-fuzzer.cc763
1 files changed, 0 insertions, 763 deletions
diff --git a/src/sixel-fuzzer.cc b/src/sixel-fuzzer.cc
deleted file mode 100644
index 207f3cfd..00000000
--- a/src/sixel-fuzzer.cc
+++ /dev/null
@@ -1,763 +0,0 @@
-/*
- * Copyright © 2020 Hans Petter Jansson <hpj@cl.no>
- *
- * This library is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library. If not, see <https://www.gnu.org/licenses/>.
- */
-
-#include "config.h"
-
-#include <assert.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdint.h>
-#include <stdio.h>
-
-#include <termios.h> /* TIOCGWINSZ */
-#include <sys/ioctl.h> /* ioctl() */
-
-#include <glib.h>
-
-/* The image data is stored as a series of palette indexes, with 16 bits
- * per pixel and TRANSPARENT_SLOT indicating transparency. This allows for
- * palette sizes up to 65535 colors.
- *
- * TRANSPARENT_SLOT can be any u16 value. Typically, the first or last
- * slot (0, n_colors) is used. The transparency index is never emitted;
- * instead pixels with this value are left blank in the output. */
-
-#define N_COLORS_MAX 65536
-#define TRANSPARENT_SLOT ((N_COLORS_MAX) - 1)
-
-#define WIDTH_MAX 65536
-#define HEIGHT_MAX 65536
-
-#define N_PIXELS_IN_SIXEL 6
-
-#define PRE_SEQ "\x1bP"
-#define POST_SEQ "\x1b\\"
-
-#define TEST_IMAGE_SIZE_MIN 16
-#define TEST_IMAGE_SIZE_MAX 512
-
-/* Big palettes make our toy printer extremely slow; use with caution */
-#define TEST_PALETTE_SIZE_MIN 1
-#define TEST_PALETTE_SIZE_MAX 16
-
-/* --- Helpers --- */
-
-static int
-random_int_in_range (int min, int max)
-{
- if (min == max)
- return min;
-
- if (min > max) {
- int t = max;
- max = min;
- min = t;
- }
-
- return min + (random () % (max - min));
-}
-
-static int
-round_up_to_multiple (int n, int m)
-{
- n += m - 1;
- return n - (n % m);
-}
-
-static int
-round_down_to_multiple (int n, int m)
-{
- return round_up_to_multiple (n, m + 1) - m;
-}
-
-static void
-memset_u16 (uint16_t *buf, uint16_t val, int n)
-{
- int i;
-
- for (i = 0; i < n; i++) {
- buf [i] = val;
- }
-}
-
-static uint16_t
-pen_to_slot (int i)
-{
- if (i >= TRANSPARENT_SLOT)
- return i + 1;
-
- return i;
-}
-
-static uint8_t
-interp_u8 (uint8_t a, uint8_t b, int fraction, int total)
-{
- uint32_t ta, tb;
-
- assert (fraction >= 0 && fraction <= total);
-
- /* Only one color in palette */
- if (total == 0)
- return a;
-
- ta = (uint32_t) a * (total - fraction) / total;
- tb = (uint32_t) b * fraction / total;
-
- return ta + tb;
-}
-
-static uint32_t
-interp_colors (uint32_t a, uint32_t b, int fraction, int total)
-{
- return interp_u8 (a, b, fraction, total)
- | (interp_u8 (a >> 8, b >> 8, fraction, total) << 8)
- | (interp_u8 (a >> 16, b >> 16, fraction, total) << 16)
- | (interp_u8 (a >> 24, b >> 24, fraction, total) << 24);
-}
-
-static int
-transform_range (int n, int old_min, int old_max, int new_min, int new_max)
-{
- if (new_min == new_max)
- return new_min;
-
- if (n < old_min)
- n = old_min;
- if (n > old_max)
- n = old_max;
-
- return ((n - old_min) * (new_max - new_min) / (old_max - old_min)) + new_min;
-}
-
-/* Transform to sixel color channels, which are in the 0..100 range. */
-static void
-argb_to_sixel_rgb (uint32_t argb, int *r, int *g, int *b)
-{
- *r = transform_range ((argb >> 16) & 0xff, 0, 256, 0, 101);
- *g = transform_range ((argb >> 8) & 0xff, 0, 256, 0, 101);
- *b = transform_range (argb & 0xff, 0, 256, 0, 101);
-}
-
-/* --- Image gen and sixel conversion --- */
-
-typedef struct
-{
- int width, height;
- int n_colors;
- uint32_t palette [N_COLORS_MAX];
- uint16_t *pixels;
-}
-Image;
-
-static void
-image_init (Image *image, int width, int height, int n_colors)
-{
- int alloc_height;
- int n_pixels;
-
- assert (width > 0 && width <= WIDTH_MAX);
- assert (height > 0 && height <= HEIGHT_MAX);
- assert (n_colors > 0 && n_colors < N_COLORS_MAX);
-
- image->width = width;
- image->height = height;
- image->n_colors = n_colors;
-
- alloc_height = round_up_to_multiple (height, N_PIXELS_IN_SIXEL);
-
- n_pixels = width * alloc_height;
- image->pixels = (uint16_t *) malloc (n_pixels * sizeof (uint16_t));
- memset_u16 (image->pixels, TRANSPARENT_SLOT, n_pixels);
-}
-
-static void
-image_deinit (Image *image)
-{
- free (image->pixels);
- image->pixels = NULL;
-}
-
-static void
-image_generate_palette (Image *image, uint32_t first_color, uint32_t last_color)
-{
- int pen;
-
- for (pen = 0; pen < image->n_colors; pen++) {
- image->palette [pen_to_slot (pen)]
- = interp_colors (first_color, last_color, pen, image->n_colors - 1);
- }
-}
-
-static void
-image_set_pixel (Image *image, int x, int y, uint16_t value)
-{
- image->pixels [y * image->width + x] = value;
-}
-
-static uint16_t
-image_get_pixel (const Image *image, int x, int y)
-{
- return image->pixels [y * image->width + x];
-}
-
-static uint8_t
-image_get_sixel (const Image *image, int x, int y, uint16_t value)
-{
- uint8_t sixel = 0;
- int i;
-
- for (i = 0; i < N_PIXELS_IN_SIXEL; i++) {
- uint16_t p = image_get_pixel (image, x, y + N_PIXELS_IN_SIXEL - 1 - i);
-
- sixel <<= 1;
- if (p == value)
- sixel |= 1;
- }
-
- return sixel;
-}
-
-static void
-image_draw_shape (Image *image)
-{
- int y, x;
-
- for (y = 0; y < image->height; y++) {
- int pen = ((image->n_colors - 1) * y + image->height / 2) / image->height;
-
- for (x = 0; x < image->width; x++) {
- if (x == 0 || x == image->width - 1 /* Box left/right */
- || y == 0 || y == image->height - 1 /* Box top/bottom */
- || y == x || y == image->width - 1 - x) /* X diagonals */
- image_set_pixel (image, x, y, pen_to_slot (pen));
- }
- }
-}
-
-static void
-image_generate (Image *image, uint32_t first_color, uint32_t last_color)
-{
- image_generate_palette (image, first_color, last_color);
- image_draw_shape (image);
-}
-
-static void
-image_print_sixels_palette (const Image *image, GString *gstr)
-{
- int pen;
-
- for (pen = 0; pen < image->n_colors; pen++) {
- uint32_t col = image->palette [pen_to_slot (pen)];
- int r, g, b;
-
- argb_to_sixel_rgb (col, &r, &g, &b);
- g_string_append_printf (gstr, "#%d;2;%d;%d;%d",
- pen_to_slot (pen), r, g, b);
- }
-}
-
-static void
-emit_sixels (GString *gstr, uint8_t sixel, int n, uint16_t slot,
- bool pass_ended, uint16_t *emitted_slot_inout,
- bool *need_emit_cr_inout, bool *need_emit_cr_inout_next)
-{
- if (n == 0) {
- return;
- }
-
- if (!pass_ended || sixel != 0) {
- char c = '?' + (char) sixel;
-
- if (*need_emit_cr_inout) {
- g_string_append_c (gstr, '$');
- *need_emit_cr_inout = FALSE;
- }
-
- if (slot != *emitted_slot_inout) {
- g_string_append_printf (gstr, "#%d", slot);
- *emitted_slot_inout = slot;
- }
-
- while (n > 255) {
- g_string_append_printf (gstr, "!255%c", c);
- n -= 255;
- }
-
- if (n >= 4) {
- g_string_append_printf (gstr, "!%d%c", n, c);
- n = 0;
- } else {
- for ( ; n > 0; n--)
- g_string_append_c (gstr, c);
- }
- }
-
- if (sixel != 0)
- *need_emit_cr_inout_next = TRUE;
-}
-
-static void
-image_print_sixels_row (const Image *image, GString *gstr, int y, uint16_t *emitted_slot_inout)
-{
- bool need_emit_cr = FALSE;
- bool need_emit_cr_next = FALSE;
- int pen;
-
- for (pen = 0; pen < image->n_colors; pen++) {
- uint16_t slot = pen_to_slot (pen);
- uint8_t cur_sixel = 0;
- int n_cur_sixel = 0;
- int x;
-
- for (x = 0; x < image->width; x++) {
- uint8_t next_sixel = image_get_sixel (image, x, y, slot);
-
- if (next_sixel == cur_sixel) {
- n_cur_sixel++;
- continue;
- }
-
- emit_sixels (gstr, cur_sixel, n_cur_sixel, slot, FALSE,
- emitted_slot_inout, &need_emit_cr, &need_emit_cr_next);
- cur_sixel = next_sixel;
- n_cur_sixel = 1;
- }
-
- emit_sixels (gstr, cur_sixel, n_cur_sixel, slot, TRUE,
- emitted_slot_inout, &need_emit_cr, &need_emit_cr_next);
- need_emit_cr = need_emit_cr_next;
- }
-
- /* CR + Linefeed */
- g_string_append_c (gstr, '-');
-}
-
-static void
-image_print_sixels_data (const Image *image, GString *gstr)
-{
- uint16_t emitted_slot = TRANSPARENT_SLOT;
- int y;
-
- for (y = 0; y < image->height; y += N_PIXELS_IN_SIXEL) {
- image_print_sixels_row (image, gstr, y, &emitted_slot);
- }
-}
-
-static void
-image_print_sixels (const Image *image, GString *gstr)
-{
- g_string_append_printf (gstr, PRE_SEQ "0;0;0q\"1;1;%d;%d",
- image->width, image->height);
- image_print_sixels_palette (image, gstr);
- image_print_sixels_data (image, gstr);
- g_string_append (gstr, POST_SEQ);
-}
-
-/* --- Main loop and printing --- */
-
-typedef enum
-{
- TEST_MODE_UNSET,
- TEST_MODE_FUZZ
-}
-TestMode;
-
-typedef struct
-{
- TestMode mode;
-
- float delay;
- int n_errors;
- int n_frames;
- int seed;
- int n_scroll;
-
- int term_width_cells, term_height_cells;
- int term_width_pixels, term_height_pixels;
-
- int term_cell_width, term_cell_height;
-}
-Options;
-
-static void
-cursor_to_offset (gint x, gint y, GString *gstr)
-{
- g_string_printf (gstr, "\x1b[%d;%df", y, x);
-}
-
-static void
-cursor_to_random_offset (gint x_max, gint y_max, GString *gstr)
-{
- cursor_to_offset (random_int_in_range (0, x_max),
- random_int_in_range (0, y_max),
- gstr);
-}
-
-static void
-scroll_n_lines (const Options *options, int n, GString *gstr)
-{
- if (n < 1)
- return;
-
- cursor_to_offset (0, options->term_height_cells, gstr);
- for (int i = 0; i < n; i++)
- g_string_append_printf (gstr, "\n");
-}
-
-static void
-print_text (const gchar *text, GString *gstr)
-{
- g_string_append (gstr, text);
-}
-
-static void
-print_random_image (const Options *options, GString *gstr)
-{
- int dim_max = MIN (TEST_IMAGE_SIZE_MAX,
- MAX (TEST_IMAGE_SIZE_MIN,
- MIN (options->term_width_pixels,
- round_down_to_multiple (options->term_height_pixels - options->term_cell_height,
- N_PIXELS_IN_SIXEL))));
- int dim = random_int_in_range (TEST_IMAGE_SIZE_MIN, dim_max + 1);
- Image image;
-
- image_init (&image, dim, dim,
- random_int_in_range (TEST_PALETTE_SIZE_MIN, TEST_PALETTE_SIZE_MAX));
-
- /* In order to produce colors that contrast both white and black backgrounds,
- * limit the range of the red component. This doesn't work reliably with grey
- * backgrounds, but eh. */
- image_generate (&image,
- random_int_in_range (0x00400000, 0x00a00000),
- random_int_in_range (0x00400000, 0x00a00000));
-
- cursor_to_random_offset ((options->term_width_pixels - dim) / options->term_cell_width,
- (options->term_height_pixels - dim) / options->term_cell_height,
- gstr);
- image_print_sixels (&image, gstr);
-
- image_deinit (&image);
-}
-
-static void
-print_random_text (const Options *options, GString *gstr)
-{
- cursor_to_random_offset (options->term_width_cells - strlen ("Hallo!"),
- options->term_height_cells,
- gstr);
- print_text ("Hallo!", gstr);
-}
-
-typedef enum
-{
- FUZZ_REPLACE,
- FUZZ_COPY,
- FUZZ_SWAP,
-
- FUZZ_MAX
-}
-FuzzType;
-
-static void
-fuzz_replace (GString *gstr)
-{
- int a, b;
-
- a = random_int_in_range (0, gstr->len - 1);
- b = a + random_int_in_range (0, MIN (gstr->len - a, 64));
-
- for (int i = a; i < b; i++) {
- gstr->str [i] = random_int_in_range (1, 256);
- }
-}
-
-static void
-fuzz_copy (GString *gstr)
-{
- int a, b, c;
-
- a = random_int_in_range (0, gstr->len - 1);
- b = random_int_in_range (0, MIN (gstr->len - a, 64));
- c = random_int_in_range (0, gstr->len - b);
-
- memcpy (gstr->str + c, gstr->str + a, b);
-}
-
-static void
-fuzz_swap (GString *gstr)
-{
- unsigned char buf [64];
- int a, b, c;
-
- a = random_int_in_range (0, gstr->len - 1);
- b = random_int_in_range (0, MIN (gstr->len - a, 64));
- c = random_int_in_range (0, gstr->len - b);
-
- memcpy (buf, gstr->str + c, b);
- memcpy (gstr->str + c, gstr->str + a, b);
- memcpy (gstr->str + c, buf, b);
-}
-
-static void
-random_fuzz (const Options *options, GString *gstr)
-{
- if (gstr->len < 1)
- return;
-
- for (int i = 0; i < options->n_errors; i++) {
- FuzzType fuzz_type = FuzzType(random () % FUZZ_MAX);
-
- switch (fuzz_type) {
- case FUZZ_REPLACE:
- fuzz_replace (gstr);
- break;
- case FUZZ_COPY:
- fuzz_copy (gstr);
- break;
- case FUZZ_SWAP:
- fuzz_swap (gstr);
- break;
- default:
- break;
- }
- }
-}
-
-static void
-print_loop (const Options *options)
-{
- for (int i = 0; options->n_frames == 0 || i < options->n_frames; i++) {
- GString *gstr;
-
- gstr = g_string_new ("");
-
- scroll_n_lines (options, options->n_scroll, gstr);
-
- if (random () % 2) {
- print_random_image (options, gstr);
- } else {
- print_random_text (options, gstr);
- }
-
- random_fuzz (options, gstr);
-
- fwrite (gstr->str, sizeof (char), gstr->len, stdout);
- g_string_free (gstr, TRUE);
- fflush (stdout);
-
- if (options->delay > 0.000001f)
- g_usleep (options->delay * 1000000.0f);
- }
-}
-
-/* --- Argument parsing and init --- */
-
-static bool
-parse_int (const char *arg, const char *val, int *out)
-{
- char *endptr;
- int ret;
- bool result = FALSE;
-
- assert (arg != NULL);
- assert (val != NULL);
- assert (out != NULL);
-
- if (*val == '\0') {
- fprintf (stderr, "Empty value for argument '%s'. Aborting.\n", arg);
- goto out;
- }
-
- ret = strtol (val, &endptr, 10);
-
- if (*endptr != '\0') {
- fprintf (stderr, "Unrecognized value for argument '%s': '%s'. Aborting.\n", arg, val);
- goto out;
- }
-
- *out = ret;
- result = TRUE;
-
-out:
- return result;
-}
-
-static bool
-parse_float (const char *arg, const char *val, float *out)
-{
- char *endptr;
- float ret;
- bool result = FALSE;
-
- assert (arg != NULL);
- assert (val != NULL);
- assert (out != NULL);
-
- if (*val == '\0') {
- fprintf (stderr, "Empty value for argument '%s'. Aborting.\n", arg);
- goto out;
- }
-
- ret = strtof (val, &endptr);
-
- if (*endptr != '\0') {
- fprintf (stderr, "Unrecognized value for argument '%s': '%s'. Aborting.\n", arg, val);
- goto out;
- }
-
- *out = ret;
- result = TRUE;
-
-out:
- return result;
-}
-
-static bool
-parse_options (Options *options, int argc, char **argv)
-{
- bool result = FALSE;
- int i;
-
- if (argc < 2) {
- fprintf (stderr, "Usage: %s <mode> [options]\n\n"
- "Modes:\n"
- " fuzz Perform fuzzing test.\n\n"
- "Options:\n"
- " -d <float> Delay between frames, in seconds (default: 0.0).\n"
- " -e <int> Maximum number of random errors per frame (default: 0).\n"
- " -n <int> Number of frames to output (default: infinite).\n"
- " -r <int> Random seed to use (default: current time).\n"
- " -s <int> Number of lines to scroll for each frame (default: 0).\n\n",
- argv [0]);
- goto out;
- }
-
- for (i = 1; i < argc; ) {
- const char *arg = argv [i];
- const char *val;
-
- if (!strcmp (arg, "fuzz")) {
- options->mode = TEST_MODE_FUZZ;
- i++;
- continue;
- }
-
- if (i + 1 >= argc)
- break;
-
- val = argv [i + 1];
-
- if (!strcmp (arg, "-d")) {
- if (!parse_float (arg, val, &options->delay))
- goto out;
- i += 2;
- } else if (!strcmp (arg, "-e")) {
- if (!parse_int (arg, val, &options->n_errors))
- goto out;
- i += 2;
- } else if (!strcmp (arg, "-n")) {
- if (!parse_int (arg, val, &options->n_frames))
- goto out;
- i += 2;
- } else if (!strcmp (arg, "-r")) {
- if (!parse_int (arg, val, &options->seed))
- goto out;
- i += 2;
- } else if (!strcmp (arg, "-s")) {
- if (!parse_int (arg, val, &options->n_scroll))
- goto out;
- i += 2;
- } else {
- fprintf (stderr, "Unrecognized option '%s'. Aborting.\n", arg);
- goto out;
- }
- }
-
- if (i != argc) {
- fprintf (stderr, "Stray option '%s'. Aborting.\n", argv [i]);
- goto out;
- }
-
- if (options->mode == TEST_MODE_UNSET) {
- fprintf (stderr, "No test mode specified. Try \"fuzz\".\n");
- goto out;
- }
-
- result = TRUE;
-
-out:
- return result;
-}
-
-static bool
-query_terminal (Options *options)
-{
- struct winsize wsz;
-
- if (ioctl (fileno (stdout), TIOCGWINSZ, &wsz) != 0) {
- fprintf (stderr, "ioctl() failed: %s\n", strerror (errno));
- return FALSE;
- }
-
- options->term_width_cells = wsz.ws_col;
- options->term_height_cells = wsz.ws_row;
- options->term_width_pixels = wsz.ws_xpixel;
- options->term_height_pixels = wsz.ws_ypixel;
-
- if (options->term_width_cells < 4
- || options->term_height_cells < 4) {
- fprintf (stderr, "Terminal window is too small (must be greater than 4x4 cells).\n");
- return FALSE;
- }
-
- if (options->term_width_pixels == 0
- || options->term_height_pixels == 0) {
- fprintf (stderr, "Terminal did not report its pixel size.\n");
- return FALSE;
- }
-
- if (options->term_width_pixels < 16
- || options->term_height_pixels < 16) {
- fprintf (stderr, "Terminal window is too small (must be greater than 16x16 pixels).\n");
- return FALSE;
- }
-
- options->term_cell_width = wsz.ws_xpixel / wsz.ws_col;
- options->term_cell_height = wsz.ws_ypixel / wsz.ws_row;
-
- return TRUE;
-}
-
-/* --- Entry point --- */
-
-int
-main (int argc, char *argv [])
-{
- static Options options = { };
-
- options.seed = (int) time (NULL);
-
- if (!parse_options (&options, argc, argv))
- return 1;
-
- if (!query_terminal (&options))
- return 2;
-
- print_loop (&options);
- return 0;
-}