ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
zalgo.c File Reference

Go to the source code of this file.

Functions

unsigned zalgo_cell_marks (uint32_t row, uint32_t col, int level, uint16_t marks[ZALGO_MAX_MARKS])
asciichat_error_t zalgo_apply (const char *input, size_t length, int level, size_t max_output_bytes, char **output)

Function Documentation

◆ zalgo_apply()

asciichat_error_t zalgo_apply ( const char * input,
size_t length,
int level,
size_t max_output_bytes,
char ** output )

Decorate a frame within the caller's byte budget; errors leave no partially decorated output.

Definition at line 69 of file zalgo.c.

69 {
70 if (!output)
71 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing Zalgo output pointer");
72 *output = NULL;
73 if (!input || level < 1 || level > 10)
74 return SET_ERRNO(ERROR_INVALID_PARAM, "Zalgo requires a frame and intensity 1-10");
75
76 // Bound REP expansion before allocating: a short remote frame can contain a
77 // very large repeat count. Four bytes per repeated code point is conservative.
78 const size_t expansion_limit = 1024 * 1024;
79 size_t expansion_bound = length;
80 bool has_rep = false;
81 if (length > expansion_limit)
82 return SET_ERRNO(ERROR_INVALID_PARAM, "Zalgo frame exceeds expansion limit");
83 for (size_t i = 0; i + 1 < length; i++) {
84 if (input[i] != '\x1b' || input[i + 1] != '[')
85 continue;
86 size_t j = i + 2;
87 uint32_t parameter = 0;
88 while (j < length && ((input[j] >= '0' && input[j] <= '9') || input[j] == ';')) {
89 if (input[j] == ';') {
90 parameter = 0;
91 } else {
92 unsigned digit = (unsigned)(input[j] - '0');
93 if (parameter > (UINT32_MAX - digit) / 10)
94 return SET_ERRNO(ERROR_INVALID_PARAM, "Zalgo frame contains an overflowing ANSI parameter");
95 parameter = parameter * 10 + digit;
96 }
97 j++;
98 }
99 if (j < length && input[j] == 'b') {
100 has_rep = true;
101 if (parameter > (expansion_limit - expansion_bound) / 4)
102 return SET_ERRNO(ERROR_INVALID_PARAM, "Zalgo REP expansion exceeds frame limit");
103 expansion_bound += (size_t)parameter * 4;
104 }
105 i = j;
106 }
107
108 char *expanded = NULL;
109 defer(SAFE_FREE(expanded));
110 if (has_rep) {
111 expanded = ansi_expand_rle(input, length);
112 } else {
113 expanded = SAFE_MALLOC(length + 1, char *);
114 memcpy(expanded, input, length);
115 expanded[length] = '\0';
116 }
117 if (!expanded)
118 return SET_ERRNO(ERROR_INVALID_PARAM, "Cannot expand empty Zalgo frame");
119 size_t n = strlen(expanded);
120 if (n > max_output_bytes) {
121 return SET_ERRNO(ERROR_INVALID_PARAM, "Zalgo frame exceeds output limit");
122 }
123 size_t mark_budget = max_output_bytes - n;
124 size_t factor = 1 + 2 * zalgo_maximum[level - 1];
125 // Every source byte introduces at most the selected level's mark limit.
126 if (n > (SIZE_MAX - 1) / factor) {
127 return SET_ERRNO(ERROR_INVALID_PARAM, "Zalgo frame is too large");
128 }
129 size_t capacity = n * factor < max_output_bytes ? n * factor : max_output_bytes;
130 SAFE_MALLOC_DEFER_FREE(result, capacity + 1, char *);
131 char *dst = result;
132 uint32_t row = 0, col = 0;
133 for (size_t i = 0; i < n;) {
134 unsigned char c = (unsigned char)expanded[i];
135 if (c == 0x1b) {
136 size_t count = zalgo_escape_length(expanded + i, n - i);
137 memcpy(dst, expanded + i, count);
138 dst += count;
139 i += count;
140 continue;
141 }
142 *dst++ = expanded[i++];
143 if (c == '\n') {
144 row++;
145 col = 0;
146 } else if (c == '\r') {
147 col = 0;
148 } else if (c == '\t') {
149 col = (col / 8 + 1) * 8;
150 } else if (c >= 0x20 && c < 0x7f) {
152 unsigned count = c == ' ' ? 0 : zalgo_cell_marks(row, col, level, marks);
153 col++;
154 if (count * 2 > mark_budget) {
155 return SET_ERRNO(ERROR_INVALID_PARAM, "Zalgo decoration exceeds output limit");
156 }
157 mark_budget -= count * 2;
158 for (unsigned mark = 0; mark < count; mark++) {
159 uint16_t cp = marks[mark];
160 *dst++ = (char)(0xc0 | (cp >> 6));
161 *dst++ = (char)(0x80 | (cp & 0x3f));
162 }
163 } else if (c >= 0xc0) {
164 // Preserve multibyte characters; continuation bytes do not advance columns.
165 col++;
166 }
167 }
168 *dst = '\0';
169 *output = result;
170 result = NULL;
171 return ASCIICHAT_OK;
172}
#define SET_ERRNO(code,...)
unsigned short uint16_t
Definition common.h:61
unsigned int uint32_t
Definition common.h:62
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_MALLOC_DEFER_FREE(name, size, cast)
Definition common.h:386
#define SAFE_MALLOC(size, cast)
Definition common.h:265
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_INVALID_PARAM
char * ansi_expand_rle(const char *input, size_t input_len)
Expand RLE escape sequences in a string.
Definition rle.c:14
unsigned zalgo_cell_marks(uint32_t row, uint32_t col, int level, uint16_t marks[ZALGO_MAX_MARKS])
Definition zalgo.c:19
#define ZALGO_MAX_MARKS
Definition zalgo.h:4

References ansi_expand_rle(), ASCIICHAT_OK, ERROR_INVALID_PARAM, SAFE_FREE, SAFE_MALLOC, SAFE_MALLOC_DEFER_FREE, SET_ERRNO, zalgo_cell_marks(), and ZALGO_MAX_MARKS.

Referenced by session_display_write_ascii_frame().

◆ zalgo_cell_marks()

unsigned zalgo_cell_marks ( uint32_t row,
uint32_t col,
int level,
uint16_t marks[ZALGO_MAX_MARKS] )

Definition at line 19 of file zalgo.c.

19 {
20 if (level < 1 || level > 10 || !marks)
21 return 0;
22 static const unsigned coverage[] = {3, 6, 10, 18, 28, 40, 55, 70, 85, 100};
23 static const unsigned minimum[] = {1, 1, 1, 1, 1, 1, 2, 3, 4, 6};
24 static const uint16_t short_marks[] = {0x0334, 0x0335, 0x0336, 0x0337, 0x0338};
25 static const uint16_t stack_marks[] = {0x0300, 0x0301, 0x0302, 0x0303, 0x0308, 0x030a,
26 0x0310, 0x0311, 0x0324, 0x0325, 0x0329, 0x0330};
27 unsigned index = (unsigned)level - 1;
28 uint32_t hash = zalgo_hash((uint32_t)((uint64_t)row * UINT64_C(0x9e3779b9)) ^ col ^ UINT32_C(0x5a17));
29 if (hash % 100 >= coverage[index])
30 return 0;
31 unsigned count = minimum[index] + ((hash >> 8) % 256) * (zalgo_maximum[index] - minimum[index] + 1) / 256;
32 for (unsigned mark = 0; mark < count; mark++) {
33 uint32_t choice = zalgo_hash((uint32_t)((uint64_t)hash + mark));
34 marks[mark] = mark == 0 ? short_marks[choice % 5] : stack_marks[choice % 12];
35 }
36 return count;
37}
unsigned long long uint64_t
Definition common.h:63
static bool size_t index
Definition queries.h:8

References index, and ZALGO_MAX_MARKS.

Referenced by zalgo_apply().