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.h File Reference

Go to the source code of this file.

Macros

#define ZALGO_MAX_MARKS   10

Functions

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

Macro Definition Documentation

◆ ZALGO_MAX_MARKS

#define ZALGO_MAX_MARKS   10

Definition at line 4 of file zalgo.h.

Referenced by zalgo_apply(), zalgo_cell_marks(), and zalgo_cell_marks().

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 an ANSI frame with deterministic combining marks. Caller frees *output. REP is expanded before decoration. Only printable ASCII bases are decorated; whitespace, existing UTF-8, and escape sequences are preserved. The byte limit excludes the NUL terminator; on overflow returns an error with *output = NULL.

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[10] )

Shared terminal/raster distribution. Returns 0-10 marks for one cell.

References ZALGO_MAX_MARKS.