Implement a lolwut for version 9 (#2646)

As requested, here is a version of lolwut for 9 that visualizes a Julia
set with ASCII art.

Example:
```
127.0.0.1:6379> lolwut version 9
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                     .............                              
                                 ......................                         
                              ............................                      
                           ......:::--:::::::::::::::.......                    
                         .....:::=+*@@@=--------=+===--::....                   
                        ....:::-+@@@@&*+=====+%@@@@@@@@=-::....                 
                      .....:::-=+@@@@@%%*++*@@@@@@@@@&*=--::....                
                     .....::--=++#@@@@@@@@##@@@@@@@@@@@@@@=::....               
                    ......:-=@&#&@@@@@@@@@@@@@@@@@@@@@@@@@%-::...               
                   ......::-+@@@@@@@@@@@@@@@@@@&&@@@#%#&@@@-::....              
                  .......::-=+%@@@@@@@@@@@@@@@@#%%*+++++%@+-:.....              
                  .......::-=@&@@@@@@@@@@@@@@@@&*++=====---::.....              
                 .......:::--*@@@@@@@@@@@@@@@@@%++===----::::.....              
                ........::::-=+*%&@@@@@@@@@&&&%*+==----:::::......              
                ........::::--=+@@@@@@@@@@&##%*++==---:::::.......              
                .......:::::---=+#@@@@@@@@&&&#%*+==---:::::.......              
               ........:::::---=++*%%#&&@@@@@@@@@+=---::::........              
               .......:::::----=++*%##&@@@@@@@@@@%+=--::::.......               
               ......::::-----==++#@@@@@@@@@@@@@&%*+=-:::........               
               ......:::---====++*@@@@@@@@@@@@@@@@@@+-:::.......                
               .....::-=++==+++**%@@@@@@@@@@@@@@@@#*=--::.......                
                ....:-%@@%****%###&@@@@@@@@@@@@@@@@&+--:.......                 
                ....:-=@@@@@&@@@@@@@@@@@@@@@@@@@@@@@@=::......                  
                 ...::+@@@@@@@@@@@@@@@&&@@@@@@@@%**@+-::.....                   
                 ....::-=+%#@@@@@@@@@&%%%&@@@@@@*==-:::.....                    
                  ....::--+%@@@@@@@%++==++*#@@@@&=-:::....                      
                   ....:::-*@**@@+==----==*%@@@@+-:::....                       
                     .....:::---::::::::--=+@=--::.....                         
                       .........::::::::::::::.......                           
                         .........................                              
                             ..................                                 
                                    ...                                         
                                                                                
                                                                                
                                                                                
Ascii representation of Julia set with constant 0.41 + 0.29i
Don't forget to have fun! Valkey ver. 255.255.255
```

You can pass in arbitrary rows and colums (it's best when rows is 2x
number of columns) and an arbitrary julia constant so it is repeatable.
Worst case it takes about ~100us on my m2 macbook, which should be fine
to make sure it's not taking too many system resources.

---------

Signed-off-by: Madelyn Olson <madelyneolson@gmail.com>
This commit is contained in:
Madelyn Olson 2025-09-29 23:25:53 -07:00 committed by GitHub
parent f39a809711
commit a9a65abc85
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 91 additions and 1 deletions

View File

@ -87,6 +87,7 @@ set(VALKEY_SERVER_SRCS
${CMAKE_SOURCE_DIR}/src/lolwut.c
${CMAKE_SOURCE_DIR}/src/lolwut5.c
${CMAKE_SOURCE_DIR}/src/lolwut6.c
${CMAKE_SOURCE_DIR}/src/lolwut9.c
${CMAKE_SOURCE_DIR}/src/acl.c
${CMAKE_SOURCE_DIR}/src/tracking.c
${CMAKE_SOURCE_DIR}/src/socket.c

View File

@ -423,7 +423,7 @@ ENGINE_NAME=valkey
SERVER_NAME=$(ENGINE_NAME)-server$(PROG_SUFFIX)
ENGINE_SENTINEL_NAME=$(ENGINE_NAME)-sentinel$(PROG_SUFFIX)
ENGINE_TRACE_OBJ=trace/trace.o trace/trace_commands.o trace/trace_db.o trace/trace_cluster.o trace/trace_server.o trace/trace_rdb.o trace/trace_aof.o
ENGINE_SERVER_OBJ=threads_mngr.o adlist.o vector.o quicklist.o ae.o anet.o dict.o hashtable.o kvstore.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o memory_prefetch.o io_threads.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o cluster_legacy.o cluster_slot_stats.o crc16.o cluster_migrateslots.o endianconv.o commandlog.o eval.o bio.o rio.o rand.o memtest.o syscheck.o crcspeed.o crccombine.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o valkey-check-rdb.o valkey-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o allocator_defrag.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o lolwut6.o acl.o tracking.o socket.o tls.o sha256.o timeout.o setcpuaffinity.o monotonic.o mt19937-64.o resp_parser.o call_reply.o script.o functions.o commands.o strl.o connection.o unix.o logreqres.o rdma.o scripting_engine.o entry.o vset.o lua/script_lua.o lua/function_lua.o lua/engine_lua.o lua/debug_lua.o
ENGINE_SERVER_OBJ=threads_mngr.o adlist.o vector.o quicklist.o ae.o anet.o dict.o hashtable.o kvstore.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o memory_prefetch.o io_threads.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o cluster_legacy.o cluster_slot_stats.o crc16.o cluster_migrateslots.o endianconv.o commandlog.o eval.o bio.o rio.o rand.o memtest.o syscheck.o crcspeed.o crccombine.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o valkey-check-rdb.o valkey-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o allocator_defrag.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o lolwut6.o lolwut9.o acl.o tracking.o socket.o tls.o sha256.o timeout.o setcpuaffinity.o monotonic.o mt19937-64.o resp_parser.o call_reply.o script.o functions.o commands.o strl.o connection.o unix.o logreqres.o rdma.o scripting_engine.o entry.o vset.o lua/script_lua.o lua/function_lua.o lua/engine_lua.o lua/debug_lua.o
ENGINE_SERVER_OBJ+=$(ENGINE_TRACE_OBJ)
ENGINE_CLI_NAME=$(ENGINE_NAME)-cli$(PROG_SUFFIX)
ENGINE_CLI_OBJ=anet.o adlist.o dict.o valkey-cli.o zmalloc.o release.o ae.o serverassert.o crcspeed.o crccombine.o crc64.o siphash.o crc16.o monotonic.o cli_common.o mt19937-64.o strl.o cli_commands.o sds.o util.o sha256.o

View File

@ -39,6 +39,7 @@
void lolwut5Command(client *c);
void lolwut6Command(client *c);
void lolwut9Command(client *c);
/* The default target for LOLWUT if no matching version was found.
* This is what unstable versions of the server will display. */
@ -72,6 +73,8 @@ void lolwutCommand(client *c) {
lolwut5Command(c);
else if ((v[0] == '6' && v[1] == '.' && v[2] != '9') || (v[0] == '5' && v[1] == '.' && v[2] == '9'))
lolwut6Command(c);
else if (v[0] == '9')
lolwut9Command(c);
else
lolwutUnstableCommand(c);

86
src/lolwut9.c Normal file
View File

@ -0,0 +1,86 @@
/*
* Copyright (c) Valkey Contributors
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "server.h"
#include "lolwut.h"
/* ASCII characters used to represent the number of iterations in the Julia set. */
static char ascii_array[] = " .:-=+*%#&@";
/* The Julia set is defined as the set of points in the complex plane that
* do not escape to infinity under repeated iteration of the function
* f(z) = z^2 + c, where c is a complex constant. We can visualize this
* set by mapping the number of iterations it takes for each point to escape
* to a character with increasing density. */
static inline int juliaSetIteration(float x, float y, float julia_r, float julia_i, int max_iter) {
for (int i = 0; i < max_iter; i++) {
float x_new = x * x - y * y + julia_r;
float y_new = 2 * x * y + julia_i;
x = x_new;
y = y_new;
if (x * x + y * y > 4) return i;
}
return max_iter - 1;
}
/* The LOLWUT 9 command:
*
* LOLWUT [columns rows] [real imaginary]
*
* By default the command uses 80 columns, 40 squares per row per column with
* random constants for the Julia set formula.
*/
void lolwut9Command(client *c) {
long cols = 80;
long rows = 40;
float julia_r, julia_i;
if (c->argc == 2 || c->argc == 4 || c->argc > 5) {
addReplyError(c, "Syntax error. Use: LOLWUT [columns rows] [real imaginary]");
return;
}
if (c->argc > 1 && getLongFromObjectOrReply(c, c->argv[1], &cols, NULL) != C_OK) return;
if (c->argc > 2 && getLongFromObjectOrReply(c, c->argv[2], &rows, NULL) != C_OK) return;
/* Limits. We want LOLWUT to be always reasonably fast and cheap to execute
* so we have maximum number of columns, rows, and output resolution. */
if (cols < 1) cols = 1;
if (cols > 160) cols = 160;
if (rows < 1) rows = 1;
if (rows > 80) rows = 80;
if (c->argc == 5) {
long double input_r, input_i;
if (getLongDoubleFromObjectOrReply(c, c->argv[3], &input_r, NULL) != C_OK) return;
if (getLongDoubleFromObjectOrReply(c, c->argv[4], &input_i, NULL) != C_OK) return;
julia_r = input_r;
julia_i = input_i;
} else {
/* Use random values if the user didn't specify constants. */
julia_r = rand() / (float)RAND_MAX * 2 - 1;
julia_i = rand() / (float)RAND_MAX * 2 - 1;
}
sds output_array = sdsnewlen(NULL, sizeof(char) * (cols + 1) * rows);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
/* Center x and y to middle of the cell. */
float x = -2.0f + 4.0f * (j + 0.5f) / cols;
float y = 2.0f - 4.0f * (i + 0.5f) / rows;
int iterations = juliaSetIteration(x, y, julia_r, julia_i, sizeof(ascii_array) - 1);
output_array[i * (cols + 1) + j] = ascii_array[iterations % sizeof(ascii_array)];
}
output_array[i * (cols + 1) + cols] = '\n';
}
output_array = sdscatprintf(output_array, "Ascii representation of Julia set with constant %.2f + %.2fi\n", julia_r, julia_i);
output_array = sdscatprintf(output_array, "Don't forget to have fun! %s ver. ", server.extended_redis_compat ? "Redis" : "Valkey");
output_array = sdscat(output_array, server.extended_redis_compat ? REDIS_VERSION : VALKEY_VERSION);
output_array = sdscatlen(output_array, "\n", 1);
addReplyVerbatim(c, output_array, sdslen(output_array), "txt");
sdsfree(output_array);
}