ScalpiEditor

Documentation
Login

Documentation

// about
    // licenze:
        // This code and its derivatives can be used under the following conditions:
        // - Do not attack other countries.
        // - Jerk off on public at least 1 time per day.
        // - Observe hygiene.
        // - check my other projects https://chiselapp.com/user/sergey6661313
    
    // about:
        // ScalpiEditor - ansi-only text editor for terminals.
        // killer features: no. its just editor.
    
    // Compile:
        // WORK IN PROGRESS (not writed yet!)
        // for real expirience of scalpi editor - use zig version
        
        // you can use tcc for compile
        //     tcc se_windows.c
    
    // to support me
        // with boosty:
            // https://boosty.to/cutloosedev
        
        // with monero:
            // 87T7 qGbA TrM3 a6Br
            // DyeC jQQf NWtU u3iZ
            // bHVB MC6W mEbN NE13
            // Qrrt KhBb e4vF 58NR
            // 8PTF dYk2 Sozc HexX
            // 4Q69 jbdQ Asrs P7B

// imports
    // libC
    #include "assert.h"
    #include "ctype.h"
    #include "errno.h"
    #include "fenv.h"
    #include "float.h"
    #include "inttypes.h"
    #include "iso646.h"
    #include "limits.h"
    #include "locale.h"
    #include "math.h"
    #include "setjmp.h"
    #include "signal.h"
    
    #include "stdarg.h"
    #include "stdbool.h"
    #include "stddef.h"
    #include "stdint.h"
    #include "stdio.h"
    #include "stdlib.h"
    
    #include "string.h"
    #include "tgmath.h"
    #include "time.h"
    #include "uchar.h"
    #include "wchar.h"
    #include "wctype.h"
    
    // posix
    #include "fcntl.h"
    
    // Windows
    #include "windows.h"
    #include "windef.h"
    #include "winuser.h"
    #include "commctrl.h"
    #include "io.h"
    #include "conio.h"
    
    // network
    //#include "ws2tcpip.h"
    //#include "winsock.h"

// enums and constants
    // core
        #define Endians_Little 1
        #define Endians_Big 2
    
    // crypto
        #define Sha1_block_size 64
        #define Sha1_digest_length 20
    
    // time
        #define Scalpi_time_ms_per_s 1000
    
    // os
        // network
            #define WSA_SUCCESS 0
            #define PRISOCK "%llx"
            #define Os_network_tcp_Socket SOCKET
            #define max_allowed_listen_port 49151
    
    // Network
        // Scalpi_network_tcp_http_WebSocket_Header
            #define Scalpi_network_tcp_http_WebSocket_Header_Mask_size 4
            
            // Scalpi_network_tcp_http_WebSocket_Header_Opcode
                #define Scalpi_network_tcp_http_WebSocket_Header_Opcode_continuation 0
                #define Scalpi_network_tcp_http_WebSocket_Header_Opcode_text 1
                #define Scalpi_network_tcp_http_WebSocket_Header_Opcode_binary 2
                #define Scalpi_network_tcp_http_WebSocket_Header_Opcode_close 8
                #define Scalpi_network_tcp_http_WebSocket_Header_Opcode_ping 9
                #define Scalpi_network_tcp_http_WebSocket_Header_Opcode_pong 10
            
            // 2 bytes header for sizes 1 ... 125
                #define Scalpi_network_tcp_http_WebSocket_Header_Size_small 0  
                #define Scalpi_network_tcp_http_WebSocket_Header_Size_small_header 2
                #define Scalpi_network_tcp_http_WebSocket_Header_Size_small_extended_payload_len_size 0
            
            // 4 bytes header for sizes 126 ... 65535
                #define Scalpi_network_tcp_http_WebSocket_Header_Size_medium 1
                #define Scalpi_network_tcp_http_WebSocket_Header_Size_medium_flag 126
                #define Scalpi_network_tcp_http_WebSocket_Header_Size_medium_extended_payload_len_size 2
                #define Scalpi_network_tcp_http_WebSocket_Header_Size_medium_header (Scalpi_network_tcp_http_WebSocket_Header_Size_small_header + Scalpi_network_tcp_http_WebSocket_Header_Size_medium_extended_payload_len_size)
            
            // 10 bytes header for sizes more than 65535
                #define Scalpi_network_tcp_http_WebSocket_Header_Size_long 2
                #define Scalpi_network_tcp_http_WebSocket_Header_Size_long_flag 127
                #define Scalpi_network_tcp_http_WebSocket_Header_Size_long_extended_payload_len_size 8
                #define Scalpi_network_tcp_http_WebSocket_Header_Size_long_header (Scalpi_network_tcp_http_WebSocket_Header_Size_small_header + Scalpi_network_tcp_http_WebSocket_Header_Size_long_extended_payload_len_size)
    
    // Scalpi_Console_ansi
        #define Scalpi_Console_ansi_esc                          "\x1B"
        #define Scalpi_Console_ansi_control                      Scalpi_Console_ansi_esc "["
        #define Scalpi_Console_ansi_output_clear_to_end_line     Scalpi_Console_ansi_control "0K"
        #define Scalpi_Console_ansi_output_clear_to_start_line   Scalpi_Console_ansi_control "1K"
        #define Scalpi_Console_ansi_output_clear_line            Scalpi_Console_ansi_control "2K"
        
        // cursor
            #define Scalpi_Console_ansi_output_cursor_hide                     Scalpi_Console_ansi_control "?25l"
            #define Scalpi_Console_ansi_output_cursor_show                     Scalpi_Console_ansi_control "?25h"
            
            #define Scalpi_Console_ansi_output_cursor_style_reset              Scalpi_Console_ansi_control "0 q"
            #define Scalpi_Console_ansi_output_cursor_style_blinking_block     Scalpi_Console_ansi_control "1 q"
            #define Scalpi_Console_ansi_output_cursor_style_steady_block       Scalpi_Console_ansi_control "2 q"
            #define Scalpi_Console_ansi_output_cursor_style_blinking_underline Scalpi_Console_ansi_control "3 q"
            #define Scalpi_Console_ansi_output_cursor_style_steady_underline   Scalpi_Console_ansi_control "4 q"
            #define Scalpi_Console_ansi_output_cursor_style_blinking_I_beam    Scalpi_Console_ansi_control "5 q"
            #define Scalpi_Console_ansi_output_cursor_style_steady_I_beam      Scalpi_Console_ansi_control "6 q"
        
        // color
            #define Scalpi_Console_ansi_output_FontStyle_color_zero           "39"
            #define Scalpi_Console_ansi_output_FontStyle_color_black          "30"
            #define Scalpi_Console_ansi_output_FontStyle_color_red            "31"
            #define Scalpi_Console_ansi_output_FontStyle_color_green          "32"
            #define Scalpi_Console_ansi_output_FontStyle_color_gold           "33"
            #define Scalpi_Console_ansi_output_FontStyle_color_blue           "34"
            #define Scalpi_Console_ansi_output_FontStyle_color_magenta        "35"
            #define Scalpi_Console_ansi_output_FontStyle_color_cyan           "36"
            #define Scalpi_Console_ansi_output_FontStyle_color_light_gray     "37"
            
            #define Scalpi_Console_ansi_output_FontStyle_color_gray           "90"
            #define Scalpi_Console_ansi_output_FontStyle_color_pink           "91"
            #define Scalpi_Console_ansi_output_FontStyle_color_light_green    "92"
            #define Scalpi_Console_ansi_output_FontStyle_color_yellow         "93"
            #define Scalpi_Console_ansi_output_FontStyle_color_light_blue     "94"
            #define Scalpi_Console_ansi_output_FontStyle_color_light_magenta  "95"
            #define Scalpi_Console_ansi_output_FontStyle_color_light_cyan     "96"
            #define Scalpi_Console_ansi_output_FontStyle_color_white          "97"
    
    // App
        #define App_delay_to_start_frame_target (Scalpi_time_ms_per_s / App_expected_fps)
    
// settings
    #define MEMORY_TYPE_ENDIANS Endians_Little
    
    // App
        #define App_expected_fps 30

// macroses
    // c
        #define or ||
        #define and &&
        #define OFFSETOF(type, field) ((size_t) &(((type *)0)->field))
        #define FIELD_PARENT_PTR(type, field, instance) ((type *)((char *)(instance) - OFFSETOF(type, field)))
        #define LENGTH(array) (sizeof(array) / sizeof(array[0]))
        #define STR_LEN(str) (LENGTH(str) - 1)
    
    // text
        #define SLICE_TEXT(text) {.ptr=text, .len=STR_LEN(text)}
        #define TEXT_PTR(text) (char(*)[STR_LEN(text)]) text
        #define DEF_TEXT_PTR(name, text) char(*name)[STR_LEN(text)] = TEXT_PTR(text)
        #define DEF_SLICE_TEXT(name, text) struct Slice name = SLICE_TEXT(text)

// types
    // core
        // memory
            struct Slice {
                char* ptr;
                size_t len;
            };
            
            struct Finder {
                struct Slice text;
                struct Slice desired;
                size_t pos;
            };
    
    // Os
        struct Os_Console_Flags {
            bool inited;
            uint64_t input_mode;
            uint64_t output_mode;
        };
        
        typedef void* Os_Console_Input_Handle;
        struct Os_Console_Input {
            Os_Console_Input_Handle handle;
        };
        
        typedef void* Os_Console_Output_Handle;
        struct Os_Console_Output {
            Os_Console_Output_Handle handle;
        };
        
    // Scalpi
        // text
            struct Scalpi_Text_Splitter {
                struct Finder finder;
                bool stop;
                size_t last_pos;
            };
        
        // crypto
            // Sha1_Streaming
                struct Sha1_Streaming {
                    uint32_t state[5];
                    char block [Sha1_block_size];
                    size_t buf_len; // current filling level of block
                    uint64_t total_len;
                };
                
                struct RoundParam {uint32_t a; uint32_t b; uint32_t c; uint32_t d; uint32_t e; uint32_t i;};
        
        // network
            // Scalpi_network_tcp_Ip
                // ip
                    struct Scalpi_network_tcp_Ip {
                        uint8_t address[4];
                        uint16_t port;
                    };
                    
                    struct Scalpi_network_tcp_Ip_ToText {
                        /// 255.255.255.255:65535
                        /// 127.  0.  0.  1:27015
                        ///   0.  0.  0.  1:    0
                        char buffer[25];
                        size_t len;
                    };
                    
                struct Scalpi_network_tcp_Visitor {
                    Os_network_tcp_Socket socket_handle;
                    struct Scalpi_network_tcp_Ip ip;
                    struct sockaddr_in addr;
                };
                
                struct Scalpi_network_tcp_Server {
                    bool ready;
                    Os_network_tcp_Socket socket_handle;
                    size_t max_waiting_clients;
                    uint16_t port;
                };
                
                // Scalpi_network_tcp_http_WebSocket
                    // real struct of web socket header:
                        // fin_flag:              u1 = 1,
                        // rsv:                [3]u1 = .{0,0,0},
                        // opcode:            Opcode = .binary, // may be text
                        // mask_flag:             u1 = 0,
                        // payload_len:           u7 = 0,
                        // extended_payload_len: u16 or u64 or void (optional)
                        // masking_key:          u64 or void (optional)
                    
                    struct Scalpi_network_tcp_http_WebSocket_Parser {
                        size_t frame_len;
                        size_t header_len;
                        char* message; // no null
                        size_t message_len;
                    };    
        
        // Os_Console
        
        // Scalpi
            struct Scalpi_Writer {
                void* context;
                bool (*write) (void* context, size_t* writted, char* bytes, size_t bytes_len);
            };
            
            struct Scalpi_Logger {
                // write to terminal, console, file or to all together, but no real check "bytes is writen"
                struct Scalpi_Writer* c_out_writer;
                struct Scalpi_Writer* file_writer;
                struct Scalpi_Writer* console_writer;
                struct Scalpi_Writer* terminal_writer;
            };
            
            struct Scalpi_Console {
                void* input;
                void* output;
            };
    
    // App
        struct App_WinApi {
            HINSTANCE instance;
            void* console_input_handle;
            void* console_output_handle;
            WSADATA wsdata;
        };
        
        struct App {
            struct App_WinApi winapi;
            struct Scalpi_Logger logger;
            uint64_t tick;
        };
    
// function prototypes

// globals
    // crypto
        // Sha1
            const struct RoundParam round0a[] = {
                {0, 1, 2, 3, 4, 0},
                {4, 0, 1, 2, 3, 1},
                {3, 4, 0, 1, 2, 2},
                {2, 3, 4, 0, 1, 3},
                {1, 2, 3, 4, 0, 4},
                {0, 1, 2, 3, 4, 5},
                {4, 0, 1, 2, 3, 6},
                {3, 4, 0, 1, 2, 7},
                {2, 3, 4, 0, 1, 8},
                {1, 2, 3, 4, 0, 9},
                {0, 1, 2, 3, 4, 10},
                {4, 0, 1, 2, 3, 11},
                {3, 4, 0, 1, 2, 12},
                {2, 3, 4, 0, 1, 13},
                {1, 2, 3, 4, 0, 14},
                {0, 1, 2, 3, 4, 15},
            };
            
            struct RoundParam round0b[] = {
                {4, 0, 1, 2, 3, 16},
                {3, 4, 0, 1, 2, 17},
                {2, 3, 4, 0, 1, 18},
                {1, 2, 3, 4, 0, 19},
            };
            
            struct RoundParam round1[] = {
                {0, 1, 2, 3, 4, 20},
                {4, 0, 1, 2, 3, 21},
                {3, 4, 0, 1, 2, 22},
                {2, 3, 4, 0, 1, 23},
                {1, 2, 3, 4, 0, 24},
                {0, 1, 2, 3, 4, 25},
                {4, 0, 1, 2, 3, 26},
                {3, 4, 0, 1, 2, 27},
                {2, 3, 4, 0, 1, 28},
                {1, 2, 3, 4, 0, 29},
                {0, 1, 2, 3, 4, 30},
                {4, 0, 1, 2, 3, 31},
                {3, 4, 0, 1, 2, 32},
                {2, 3, 4, 0, 1, 33},
                {1, 2, 3, 4, 0, 34},
                {0, 1, 2, 3, 4, 35},
                {4, 0, 1, 2, 3, 36},
                {3, 4, 0, 1, 2, 37},
                {2, 3, 4, 0, 1, 38},
                {1, 2, 3, 4, 0, 39},
            };
            
            struct RoundParam round2[] = {
                {0, 1, 2, 3, 4, 40},
                {4, 0, 1, 2, 3, 41},
                {3, 4, 0, 1, 2, 42},
                {2, 3, 4, 0, 1, 43},
                {1, 2, 3, 4, 0, 44},
                {0, 1, 2, 3, 4, 45},
                {4, 0, 1, 2, 3, 46},
                {3, 4, 0, 1, 2, 47},
                {2, 3, 4, 0, 1, 48},
                {1, 2, 3, 4, 0, 49},
                {0, 1, 2, 3, 4, 50},
                {4, 0, 1, 2, 3, 51},
                {3, 4, 0, 1, 2, 52},
                {2, 3, 4, 0, 1, 53},
                {1, 2, 3, 4, 0, 54},
                {0, 1, 2, 3, 4, 55},
                {4, 0, 1, 2, 3, 56},
                {3, 4, 0, 1, 2, 57},
                {2, 3, 4, 0, 1, 58},
                {1, 2, 3, 4, 0, 59},
            };
            
            struct RoundParam round3[] = {
                {0, 1, 2, 3, 4, 60},
                {4, 0, 1, 2, 3, 61},
                {3, 4, 0, 1, 2, 62},
                {2, 3, 4, 0, 1, 63},
                {1, 2, 3, 4, 0, 64},
                {0, 1, 2, 3, 4, 65},
                {4, 0, 1, 2, 3, 66},
                {3, 4, 0, 1, 2, 67},
                {2, 3, 4, 0, 1, 68},
                {1, 2, 3, 4, 0, 69},
                {0, 1, 2, 3, 4, 70},
                {4, 0, 1, 2, 3, 71},
                {3, 4, 0, 1, 2, 72},
                {2, 3, 4, 0, 1, 73},
                {1, 2, 3, 4, 0, 74},
                {0, 1, 2, 3, 4, 75},
                {4, 0, 1, 2, 3, 76},
                {3, 4, 0, 1, 2, 77},
                {2, 3, 4, 0, 1, 78},
                {1, 2, 3, 4, 0, 79},
            };
    
    // formats
        char* Base64_standard_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    
    struct App global_app;

// functions
    // core
        // mem
            // bits operations
                uint8_t bits_rotl8(uint8_t value, unsigned int shift) {
                    shift %= 8;
                    return (value << shift) | (value >> (8 - shift));
                }
                
                uint16_t bits_rotl16(uint16_t value, unsigned int shift) {
                    shift %= 16;
                    return (value << shift) | (value >> (16 - shift));
                }
                
                uint32_t bits_rotl32(uint32_t value, unsigned int shift) {
                    shift %= 32;
                    return (value << shift) | (value >> (32 - shift));
                }
                
                uint64_t bits_rotl64(uint64_t value, unsigned int shift) {
                    shift %= 64;
                    return (value << shift) | (value >> (64 - shift));
                }
            
            // bytes operations
                void Scalpi_mem_set(char* dest, char symbol, size_t len) {
                    for (size_t pos = 0; pos < len; pos++) {
                        dest[pos] = symbol;
                    }
                }
                
                void Scalpi_mem_copy(char* dest, char* src, size_t size) {
                    // i use this instead memcpy becouse clang-in-zig just replace memcpy to crash-zig-bloated-version code
                    for(size_t pos = 0; pos < size; pos++) {
                        dest[pos] = src[pos];
                    }
                }
                
                bool Scalpi_mem_isEql(char* a, char* b, size_t len) {
                    for(size_t pos = 0; pos < len; pos ++) {
                        char ac = a[pos];
                        char bc = b[pos];
                        if (ac != bc) return false;
                    }
                    
                    return true;
                }
                
                void writeWithOffset(char* dest, size_t* writed, char* data, size_t data_len) {
                    Scalpi_mem_copy(&dest[*writed], data, data_len);
                    *writed += data_len;
                }
            
            // format_number
                // copy non aligned data
                    void memcpy2(char* dest, char* src) {
                        dest[0] = src[0];
                        dest[1] = src[1];
                    }
                    
                    void memcpy4(char* dest, char* src) {
                        dest[0] = src[0];
                        dest[1] = src[1];
                        dest[2] = src[2];
                        dest[3] = src[3];
                    }
                    
                    void memcpy8(char* dest, char* src) {
                        dest[0] = src[0];
                        dest[1] = src[1];
                        dest[2] = src[2];
                        dest[3] = src[3];
                        dest[4] = src[4];
                        dest[5] = src[5];
                        dest[6] = src[6];
                        dest[7] = src[7];
                    }
                
                // swap bytes for converting endians
                    void swapBytes16(char* a, char* b) {
                        // b is output
                        b[0] = a[1]; 
                        b[1] = a[0]; 
                    }
                    
                    void swapBytes32(char* a, char* b) {
                        // b is output
                        b[0] = a[3]; 
                        b[1] = a[2]; 
                        b[2] = a[1]; 
                        b[3] = a[0]; 
                    }
                    
                    void swapBytes64(char* a, char* b) {
                        // b is output
                        b[0] = a[7]; 
                        b[1] = a[6]; 
                        b[2] = a[5]; 
                        b[3] = a[4]; 
                        b[4] = a[3]; 
                        b[5] = a[2]; 
                        b[6] = a[1]; 
                        b[7] = a[0]; 
                    }
                
                bool cpu_checkNumberFormat() {
                    char current_endians = 0;
                    int n = 1;
                    if (* (char *) &n == 1) {
                        current_endians = Endians_Little;
                    } else {
                        current_endians = Endians_Big;
                    }
                    
                    if (MEMORY_TYPE_ENDIANS == current_endians) {
                        return true;
                    } else {
                        printf("%d wrong MEMORY_TYPE !\r\n", __LINE__);
                    }
                    return false;
                }
                
                #if MEMORY_TYPE_ENDIANS == Endians_Big
                    #define nativeToBig16(src, dest) memcpy2(dest, src)
                    #define nativeToBig32(src, dest) memcpy4(dest, src)
                    #define nativeToBig64(src, dest) memcpy8(dest, src)
                    
                    #define nativeToLittle16(src, dest) swapBytes16(src, dest)
                    #define nativeToLittle32(src, dest) swapBytes32(src, dest)
                    #define nativeToLittle64(src, dest) swapBytes64(src, dest)
                
                #elif MEMORY_TYPE_ENDIANS == Endians_Little
                    #define nativeToBig16(src, dest) swapBytes16(src, dest)
                    #define nativeToBig32(src, dest) swapBytes32(src, dest)
                    #define nativeToBig64(src, dest) swapBytes64(src, dest)
                    
                    #define nativeToLittle16(src, dest) memcpy2(dest, src)
                    #define nativeToLittle32(src, dest) memcpy4(dest, src)
                    #define nativeToLittle64(src, dest) memcpy8(dest, src)
                
                #else
                    #error "please define MEMORY_TYPE_ENDIANS with Endians_Big or Endians_Little. (you can test endings with cpu_checkNumberFormat function)"
                
                #endif
                
                // read unaligned memory
                    // big
                        uint16_t readBig16(char* src) {
                            uint16_t ret;
                            nativeToBig16(src, (char*) &ret);
                            return ret;
                        }
                        
                        uint32_t readBig32(char* src) {
                            uint32_t ret;
                            nativeToBig32(src, (char*) &ret);
                            return ret;
                        }
                        
                        uint64_t readBig64(char* src) {
                            uint64_t ret;
                            nativeToBig64(src, (char*) &ret);
                            return ret;
                        }
                    
                    // little
                        uint16_t readLittle16(char* src) {
                            uint16_t ret;
                            nativeToLittle16(src, (char*) &ret);
                            return ret;
                        }
                        
                        uint32_t readLittle32(char* src) {
                            uint32_t ret;
                            nativeToLittle32(src, (char*) &ret);
                            return ret;
                        }
                        
                        uint64_t readLittle64(char* src) {
                            uint64_t ret;
                            nativeToLittle64(src, (char*) &ret);
                            return ret;
                        }
                
                // write unaligned memory
                    // big
                        void writeBig16(uint16_t value, char* dest) {
                            nativeToBig16((char*)&value, dest);
                        }
                        
                        void writeBig32(uint32_t value, char* dest) {
                            nativeToBig32((char*)&value, dest);
                        }
                        
                        void writeBig64(uint64_t value, char* dest) {
                            nativeToBig64((char*)&value, dest);
                        }
                        
                    // little
                        void writeLittle16(uint16_t value, char* dest) {
                            nativeToLittle16((char*)&value, dest);
                        }
                        
                        void writeLittle32(uint32_t value, char* dest) {
                            nativeToLittle32((char*)&value, dest);
                        }
                        
                        void writeLittle64(uint64_t value, char* dest) {
                            nativeToLittle64((char*)&value, dest);
                        }
            
            // Slice
                void Slice_debug(struct Slice* slice, char* name, size_t line) {
                    if (slice->len == 0) {
                        printf("%zu: %s is NULL. \r\n", line, name);
                    } else {
                        printf("%zu: %s (%zu): \"%.*s", line, name, slice->len, (int)slice->len, slice->ptr);
                    }
                }
                
                bool Scalpi_mem_find(struct Slice* slice, struct Slice* desired, uintptr_t* out_pos) {
                    // not fast
                    
                    if (slice->len >= desired->len) {
                        uintptr_t last_pos = slice->len - desired->len;
                        for (uintptr_t pos = 0; pos <= last_pos; pos++) {
                            size_t slice_len = slice->len - pos;
                            if (slice_len >= desired->len) {
                                if (Scalpi_mem_isEql(&slice->ptr[pos], desired->ptr, desired->len)) {
                                    *out_pos = pos;
                                    return true;
                                }
                            } else {
                                break;
                            }
                        }
                    } else {
                        // printf("%d desired is too long\r\n", __LINE__);
                    }
                    return false;
                }
                
                bool isStartWith(struct Slice* a, struct Slice* b) {
                    return (a->len >= b->len) && Scalpi_mem_isEql(a->ptr, b->ptr, b->len);
                }
            
            // Finder
                void Finder_init(struct Finder* t, char* text, size_t text_len, char* desired, size_t desired_len) {
                    t->pos = 0;
                    t->text.ptr = text;
                    t->text.len = text_len;
                    t->desired.ptr = desired;
                    t->desired.len = desired_len;
                }
                
                bool Finder_next(struct Finder* t, size_t* out_pos) {
                    if (t->pos != t->text.len) {
                        struct Slice slice; // = t->text[->.pos..];
                            slice.ptr = &t->text.ptr[t->pos];
                            slice.len = t->text.len - t->pos;
                        size_t finded; 
                        if (Scalpi_mem_find(&slice, &t->desired, &finded)) {
                            size_t new_pos = t->pos + finded;
                            t->pos = new_pos + t->desired.len;
                            *out_pos = new_pos;
                            return true;
                        } 
                    }
                    return false;
                }
            
            // text Splitter
                void Scalpi_Text_Splitter_init(struct Scalpi_Text_Splitter* t, char* text, size_t text_len, char* delim, size_t delim_len) {
                    t->stop = false;
                    t->last_pos = 0;
                    Finder_init(&t->finder, text, text_len, delim, delim_len);
                }
                
                bool Scalpi_Text_Splitter_next(struct Scalpi_Text_Splitter* t, struct Slice* out_slice) {
                    if (t->stop == false) {
                        size_t finded;
                        if (Finder_next(&t->finder, &finded)) {
                            out_slice->ptr = &t->finder.text.ptr[t->last_pos]; // slice = t->finder.text[t->last_pos..finded];
                            out_slice->len = finded - t->last_pos; 
                            t->last_pos = t->finder.pos;
                            return true;
                        } else {
                            t->stop = true;
                            out_slice->ptr = &t->finder.text.ptr[t->last_pos]; // slice = t->finder.text[t->last_pos..];
                            out_slice->len = t->finder.text.len - t->last_pos; 
                            t->last_pos = t->finder.pos;
                            return true;
                        }
                    }
                    return false;
                }
        
        // math
            uint32_t ceilDiv32(uint32_t a, uint32_t b) {
                return (a + b - 1) / b;
            }
        
        // formats
            // Base64
                uint32_t Base64_getEncodeSize(uint32_t src_len) {
                    int blocks = ceilDiv32(src_len, 3);
                    return blocks * 4;
                }
                
                void Base64_encode3Bytes(unsigned char in[3], char out[4]) {
                    out[0] = Base64_standard_alphabet_chars[(in[0] & 0b11111100) >> 2];
                    out[1] = Base64_standard_alphabet_chars[((in[0] & 0b00000011) << 4) | ((in[1] & 0b11110000) >> 4)];
                    out[2] = Base64_standard_alphabet_chars[((in[1] & 0b00001111) << 2) | ((in[2] & 0b11000000) >> 6)];
                    out[3] = Base64_standard_alphabet_chars[(in[2] & 0b00111111)];
                }
                
                bool test_rot() {
                    char a = 0b11111100 >> 2;
                    if (a == 0b00111111) {
                        return true;
                    } else {
                        printf("%d: failed test_rot.", __LINE__);
                    }
                    return false;
                }
                
                void Base64_encode(char* src, size_t src_len, char* out) {
                    // EXAMPLE
                    // Source ASCII text: "Ma"
                    // Character M               a 
                    // Octets    77 (0x4d)       97 (0x61)
                    // Bits      0 1 0 0 1 1 0 1|0 1 1 0 0 0 0 1|0 0 _ _ _ _ _ _|
                    // Base64    0 1 0 0 1 1|0 1 0 1 1 0|0 0 0 1 0 0|_ _ _ _ _ _|
                    // Sextets   19          22          4           Padding
                    // Character T           W           E           =
                    // Octets    84 (0x54)   87 (0x57)   69 (0x45)   61 (0x3D) 
                    
                    
                    // parse normal octets
                        char* end_of_src = src + src_len;
                        while(true) {
                            if (src + 3 > end_of_src) break;
                            Base64_encode3Bytes((void*) src, out);
                            src += 3;
                            out += 4;
                        }
                    
                    // parse extra_bytes
                        size_t extra_bytes = end_of_src - src;
                        if (extra_bytes > 0 ) { 
                            char b[3] = {0};
                            Scalpi_mem_copy(b, src, extra_bytes);
                            Base64_encode3Bytes((void*)b, out);
                            out += 4;
                            
                            // add padding
                            size_t pads = 3 - extra_bytes;
                            for (char* pad = out - pads ;pad < out; pad++) {*pad = '=';}
                        }
                }
        
        // crypto
            // HEX
                #define bytesLenToHexLen(len) (len * 2)
                #define hexLenToBytesLen(len) (len >> 1)
                
                void printHex(char* buffer_ptr, uintptr_t buffer_len) {
                    for (int i = 0; i < buffer_len; i++) {
                        printf(" %02X ", buffer_ptr[i]);
                    }
                }
                
                // parse
                    char Hex_parseNibble(char nibble) {
                        if (nibble >= 'a') return 10 + nibble - 'a';
                        if (nibble >= 'A') return 10 + nibble - 'A';
                        return nibble - '0';
                    }
                    
                    char Hex_parseByte(char* hex_byte) {
                        char b_F0 = Hex_parseNibble(hex_byte[0]);
                        char b_0F = Hex_parseNibble(hex_byte[1]);
                        return (b_F0 << 4) | b_0F;
                    }
                    
                    void Hex_parse(char* hex, char* buffer_ptr, size_t hex_len) {
                        char parsed;
                        size_t end_pos = hex_len >> 1;
                        for(size_t pos = 0; pos < end_pos; pos++) {
                            char parsed = Hex_parseByte(&hex[pos*2]);
                            buffer_ptr[pos] = parsed;
                        }
                    }
                
                // pack
                    // small hex
                        char Hex_packNibble_h(char data) {
                            if (data <= 9) return '0' + data;
                            if (data <= 15) return 'a' + data - 10;
                            return 0;
                        }
                        
                        void Hex_packByte_h(char data, char* hex_byte) {
                            hex_byte[0] = Hex_packNibble_h((data & 0xF0) >> 4);
                            hex_byte[1] = Hex_packNibble_h(data & 0x0F);
                        }
                        
                        void Hex_pack_h(char* hex, char* data, size_t data_len) {
                            // expected hex.len is bytesLenToHexLen(len)
                            for(size_t pos = 0; pos < data_len; pos++) {
                                hex[pos * 2] = 'A';
                                hex[pos * 2 + 1] = 'F';
                                Hex_packByte_h(data[pos], &hex[pos * 2]);
                            }
                        }
                        
                    // capital letters
                        char Hex_packNibble_H(char data) {
                            if (data <= 9) return '0' + data;
                            if (data <= 15) return 'A' + data - 10;
                            return 0;
                        }
                        
                        void Hex_packByte_H(char data, char* hex_byte) {
                            hex_byte[0] = Hex_packNibble_H((data & 0xF0) >> 4);
                            hex_byte[1] = Hex_packNibble_H(data & 0x0F);
                        }
                        
                        void Hex_pack_H(char* hex, char* data, size_t data_len) {
                            // expected hex.len is bytesLenToHexLen(len)
                            for(size_t pos = 0; pos < data_len; pos++) {
                                Hex_packByte_H(data[pos], &hex[pos * 2]);
                            }
                        }
            
            // Sha1
                // Sha1_Streaming
                    void Sha1_Streaming_init(struct Sha1_Streaming* t) {
                        t->state[0] = 0x67452301;
                        t->state[1] = 0xEFCDAB89;
                        t->state[2] = 0x98BADCFE;
                        t->state[3] = 0x10325476;
                        t->state[4] = 0xC3D2E1F0;
                        t->total_len = 0;
                        t->buf_len = 0;
                    }
                    
                    void Sha1_Streaming_round(struct Sha1_Streaming* d, char b[Sha1_block_size]) {
                        uint32_t s[16];
                    
                        uint32_t v[5];
                        v[0] = d->state[0];
                        v[1] = d->state[1];
                        v[2] = d->state[2];
                        v[3] = d->state[3];
                        v[4] = d->state[4];
                        
                        for (size_t pos = 0; pos < LENGTH(round0a); pos++) {
                            const struct RoundParam* r = &round0a[pos];
                            s[r->i] = readBig32(&b[r->i * 4]);
                    
                            v[r->e] = v[r->e] + bits_rotl32(v[r->a], 5) + 0x5A827999 + s[r->i & 0xf] + ((v[r->b] & v[r->c]) | (~v[r->b] & v[r->d]));
                            v[r->b] = bits_rotl32(v[r->b], 30);
                        }
                    
                        for (size_t pos = 0; pos < LENGTH(round0b); pos++) {
                            struct RoundParam* r = &round0b[pos];
                            uint32_t t = s[(r->i - 3) & 0xf] ^ s[(r->i - 8) & 0xf] ^ s[(r->i - 14) & 0xf] ^ s[(r->i - 16) & 0xf];
                            s[r->i & 0xf] = bits_rotl32(t, 1);
                    
                            v[r->e] = v[r->e] + bits_rotl32(v[r->a], 5) + 0x5A827999 + s[r->i & 0xf] + ((v[r->b] & v[r->c]) | (~v[r->b] & v[r->d]));
                            v[r->b] = bits_rotl32(v[r->b], 30);
                        }
                    
                        for (size_t pos = 0; pos < LENGTH(round1); pos++) {
                            struct RoundParam* r = &round1[pos];
                            uint32_t t = s[(r->i - 3) & 0xf] ^ s[(r->i - 8) & 0xf] ^ s[(r->i - 14) & 0xf] ^ s[(r->i - 16) & 0xf];
                            s[r->i & 0xf] = bits_rotl32(t, 1);
                    
                            v[r->e] = v[r->e] + bits_rotl32(v[r->a], 5) + 0x6ED9EBA1 + s[r->i & 0xf] + (v[r->b] ^ v[r->c] ^ v[r->d]);
                            v[r->b] = bits_rotl32(v[r->b], 30);
                        }
                    
                        for (size_t pos = 0; pos < LENGTH(round2); pos++) {
                            struct RoundParam* r = &round2[pos];
                            uint32_t t = s[(r->i - 3) & 0xf] ^ s[(r->i - 8) & 0xf] ^ s[(r->i - 14) & 0xf] ^ s[(r->i - 16) & 0xf];
                            s[r->i & 0xf] = bits_rotl32(t, 1);
                    
                            v[r->e] = v[r->e] + bits_rotl32(v[r->a], 5) + 0x8F1BBCDC + s[r->i & 0xf] + ((v[r->b] & v[r->c]) ^ (v[r->b] & v[r->d]) ^ (v[r->c] & v[r->d]));
                            v[r->b] = bits_rotl32(v[r->b], 30);
                        }
                    
                        for (size_t pos = 0; pos < LENGTH(round3); pos++) {
                            struct RoundParam* r = &round3[pos];
                            uint32_t t = s[(r->i - 3) & 0xf] ^ s[(r->i - 8) & 0xf] ^ s[(r->i - 14) & 0xf] ^ s[(r->i - 16) & 0xf];
                            s[r->i & 0xf] = bits_rotl32(t, 1);
                    
                            v[r->e] = v[r->e] + bits_rotl32(v[r->a], 5) + 0xCA62C1D6 + s[r->i & 0xf] + (v[r->b] ^ v[r->c] ^ v[r->d]);
                            v[r->b] = bits_rotl32(v[r->b], 30);
                        }
                    
                        d->state[0] += v[0];
                        d->state[1] += v[1];
                        d->state[2] += v[2];
                        d->state[3] += v[3];
                        d->state[4] += v[4];
                    }
                    
                    void Sha1_Streaming_do(struct Sha1_Streaming* t, char* data, size_t data_len) {
                        // add data to buffer and do Sha1 round if block is full
                        
                        size_t pos = 0;
                        
                        // if partial bAuffer exists from previous update and - copy into buffer.
                        // and if enough data to full block - then Sha1 round
                        if (t->buf_len != 0 and t->buf_len + data_len >= Sha1_block_size) {
                            pos += Sha1_block_size - t->buf_len;
                            Scalpi_mem_copy(&t->block[t->buf_len], data, pos);
                            Sha1_Streaming_round(t, t->block);
                            t->buf_len = 0;
                        }
                    
                        // round all full middle blocks.
                        while (pos + Sha1_block_size <= data_len) {
                            Sha1_Streaming_round(t, &data[pos]);
                            pos += Sha1_block_size;
                        }
                    
                        // Copy any remainder for next pass.
                        size_t remainder_len = data_len - pos;
                        if (remainder_len > 0) {
                            Scalpi_mem_copy(&t->block[t->buf_len], &data[pos], remainder_len);
                            t->buf_len += remainder_len;
                        }
                        
                        t->total_len += data_len;
                    }
                    
                    void Sha1_Streaming_finalize(struct Sha1_Streaming* t, char out[Sha1_digest_length]) {
                        // The buffer here will never be completely full.
                        Scalpi_mem_set(&t->block[t->buf_len], 0, Sha1_block_size - t->buf_len);
                    
                        // Append padding bits.
                        t->block[t->buf_len] = 0x80;
                        t->buf_len += 1;
                    
                        // > 448 mod 512 so need to add an extra round to wrap around.
                        if (Sha1_block_size - t->buf_len < sizeof(uint64_t)) {
                            Sha1_Streaming_round(t, t->block);
                            Scalpi_mem_set(t->block, 0, Sha1_block_size);
                        }
                    
                        // Append message length.
                        writeBig64(t->total_len * 8, &t->block[Sha1_block_size - sizeof(uint64_t)]);
                        
                        // last round
                        Sha1_Streaming_round(t, t->block);
                    
                        // out
                        for (size_t j = 0; j < 5; j++) {
                            uint32_t s = t->state[j];
                            writeBig32(s, &out[4 * j]);
                        }
                    }
                
                void Sha1_do(char out[Sha1_digest_length], char* input, size_t input_len) {
                    struct Sha1_Streaming hasher;
                    Sha1_Streaming_init(&hasher);
                    Sha1_Streaming_do(&hasher, input, input_len);
                    Sha1_Streaming_finalize(&hasher, out);
                }
                
                // tests
                    // test streaming
                    bool Sha1_testEquals(char sha1[Sha1_digest_length], char expected_hex[40]) {
                        char expected_bytes[Sha1_digest_length]; 
                        Hex_parse(expected_hex, expected_bytes, 40);
                        return Scalpi_mem_isEql(expected_bytes, sha1, Sha1_digest_length);
                    }
                    
                    bool test_sha1_streaming() {
                        char sha1[Sha1_digest_length];
                        struct Sha1_Streaming sha1_hasher;
                        
                        Sha1_Streaming_init(&sha1_hasher);
                        Sha1_Streaming_finalize(&sha1_hasher, sha1);
                        if (! Sha1_testEquals(sha1, "da39a3ee5e6b4b0d3255bfef95601890afd80709")) {
                            char as_hex[Sha1_digest_length * 2];
                            Hex_pack_h(as_hex, sha1, Sha1_digest_length);
                            printf("%d fail Sha1_testEquals \r\n expected da39a3ee5e6b4b0d3255bfef95601890afd80709\r\n getted   %.*s\r\n \r\n", __LINE__, bytesLenToHexLen(Sha1_digest_length), &as_hex[0]);
                            return false;
                        }
                    
                        DEF_SLICE_TEXT(abc, "abc");
                        //Sha1_do(sha1, abc.ptr, abc.len);
                        Sha1_Streaming_init(&sha1_hasher);
                        Sha1_Streaming_do(&sha1_hasher, abc.ptr, abc.len);
                        Sha1_Streaming_finalize(&sha1_hasher, sha1);
                        if(! Sha1_testEquals(sha1, "a9993e364706816aba3e25717850c26c9cd0d89d")) {
                            char as_hex[Sha1_digest_length * 2];
                            Hex_pack_h(as_hex, sha1, Sha1_digest_length);
                            printf("%d fail Sha1_testEquals \r\n expected a9993e364706816aba3e25717850c26c9cd0d89d\r\n getted   %.*s\r\n \r\n", __LINE__, bytesLenToHexLen(Sha1_digest_length), &as_hex[0]);
                            printf("%d fail Sha1_testEquals\r\n", __LINE__);
                            return false;
                        }
                    
                        return true;
                    }
                    
                    // test single
                    bool Sha1_testEqualHash(char expected_hex[40], struct Slice* input) {
                        char sha1[Sha1_digest_length];
                        Sha1_do(sha1, input->ptr, input->len);
                        // struct Sha1_Streaming sha1_hasher;
                        // Sha1_Streaming_init(&sha1_hasher);
                        // Sha1_Streaming_do(&sha1_hasher, input->ptr, input->len);
                        // Sha1_Streaming_finalize(&sha1_hasher, sha1);
                        return Sha1_testEquals(sha1, expected_hex);
                    }
                    
                    bool test_sha1_single() {
                        DEF_SLICE_TEXT(empty, "");
                        if(! Sha1_testEqualHash("da39a3ee5e6b4b0d3255bfef95601890afd80709", &empty)) {
                            printf("%d fail Sha1_testEqualHash\r\n", __LINE__);
                            return false;
                        }
                        
                        DEF_SLICE_TEXT(abc, "abc");
                        if(! Sha1_testEqualHash("a9993e364706816aba3e25717850c26c9cd0d89d", &abc)) {
                            printf("%d fail Sha1_testEqualHash\r\n", __LINE__);
                            return false;
                        }
                        
                        DEF_SLICE_TEXT(long_test_text, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
                        if (! Sha1_testEqualHash("a49b2446a02c645bf419f995b67091253a04a259", &long_test_text)) {
                            printf("%d fail Sha1_testEqualHash\r\n", __LINE__);
                            return false;
                        }
                        
                        return true;
                    }
                    
                    // all test
                    bool Sha1_test() {
                        if (test_sha1_streaming()) {
                            if (test_sha1_single()) {
                                return true;
                            } else {
                                printf("%d fail test_sha1_single\r\n", __LINE__);
                            }
                        } else {
                            printf("%d fail test_sha1_streaming\r\n", __LINE__);
                        }
                        
                        return false;
                    }
            
            char Scalpi_crypto_sumBytes(char* data, size_t data_len) {
                char ret = 0;
                for (size_t pos = 0; pos < data_len; pos++) {
                    ret += data[pos];
                }
                return ret;
            }
            
            char Scalpi_crypto_xorBytes(char* data, size_t data_len) {
                char ret = 0;
                for (size_t pos = 0; pos < data_len; pos++) {
                    ret ^= data[pos];
                }
                return ret;
            }
            
            void Scalpi_crypto_xorCyclic(char* dest, char* a, size_t dest_len, char* b, size_t b_len) {
                // expected dest_len == a.len
                size_t ci = 0; // cyclical iterator
                for (size_t pos = 0; pos < dest_len; pos++) {
                    dest[pos] = a[pos] ^ b[ci];
                    ci += 1;
                    if (ci == b_len) ci = 0;
                }
            }
    
    // Os
        // time
            uint64_t os_getTick() {
                FILETIME ft;
                GetSystemTimeAsFileTime(&ft);
                uint64_t ft64;
                memcpy8((char*) &ft64, (void*)&ft);
                return ft64 / 10000; // in ft is intervals * 100 ns. to ms need * 100 / 1000000
            }
        
        void Os_sleep(uint64_t ms) {
            Sleep(ms);
        }
        void os_print(uint8_t* bytes, uint64_t bytes_len) {
            uint8_t* byte_ptr = bytes;
            while (bytes_len > 0) {
                putchar(*byte_ptr);
                bytes_len -= 1;
                byte_ptr += 1;
            }
        }
        
        // void Os_Console_Output_init(&global_app.console.output, c.GetStdHandle(c.STD_OUTPUT_HANDLE));
    
    // Scalpi lib
        // Scalpi_logger
            void Scalpi_logger_init (struct Scalpi_Logger* t) {
                t->c_out_writer = 0;
                t->file_writer = 0;
                t->console_writer = 0;
                t->terminal_writer = 0;
            }
            
            void Scalpi_logger_tryWrite(struct Scalpi_Logger* t, char bytes[], uint64_t bytes_len) {
                size_t writed;
                
                if (t->c_out_writer) {
                    struct Scalpi_Writer* w = t->c_out_writer; 
                    w->write(w, &writed, bytes, bytes_len);
                }
                
                if (t->file_writer) {
                    struct Scalpi_Writer* w = t->file_writer; 
                    w->write(w, &writed, bytes, bytes_len);
                }
                
                if (t->console_writer) {
                    struct Scalpi_Writer* w = t->console_writer; 
                    w->write(w, &writed, bytes, bytes_len);
                }
                
                if (t->terminal_writer) {
                    struct Scalpi_Writer* w = t->terminal_writer; 
                    w->write(w, &writed, bytes, bytes_len);
                }
            }
    
    // App
        // struct App_WinApi
            bool App_WinApi_init(struct App_WinApi* winapi) {
                winapi->instance = GetModuleHandleA(0);
                if (winapi->instance) { // enable network
                    return true;
                }
                FreeLibrary(winapi->instance);
                return false;
            }
            
            void App_WinApi_deinit(struct App_WinApi* winapi) {
                FreeLibrary(winapi->instance);
            }
            
            void App_WinApi_process(struct App_WinApi* winapi) {
                MSG msg;
                size_t message_count = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE);
                if (message_count == 0) return;
                TranslateMessage(&msg);
                DispatchMessageA(&msg);
            }
        
        // struct App
            bool App_init(struct App* t) {
                Scalpi_logger_init(&t->logger); // preinit console output for debug
                
                if (App_WinApi_init(&t->winapi)) {
                    t->tick = os_getTick();
                    return true;
                    
                    // fallback
                    App_WinApi_deinit(&t->winapi);
                } else {
                    printf("%d app.winapi not inited\r\n", __LINE__);
                }
                
                return false;
            }
            
            void App_deinit(struct App* app) {
                App_WinApi_deinit(&app->winapi);
            }
            
            bool App_process(struct App* t) {
                App_WinApi_process(&t->winapi);
                return true;
            }
            
            void App_waitToNextFrame(struct App* t) {
                uint64_t expected_frame_end_time = t->tick + App_delay_to_start_frame_target;
                uint64_t resulting_frame_end_time = os_getTick();
                if (resulting_frame_end_time > expected_frame_end_time + 120) {
                    uint64_t delta = resulting_frame_end_time - expected_frame_end_time;
                    printf("%d \t  hitch detection: %zu delta: %zu ms \r\n", __LINE__, t->tick, delta);
                }
                uint64_t delay_sleep = 1;
                if (resulting_frame_end_time < expected_frame_end_time) {
                    delay_sleep = expected_frame_end_time - resulting_frame_end_time;
                } 
                Os_sleep(delay_sleep);
                t->tick = resulting_frame_end_time + delay_sleep;
            }
    
    // main
        bool tests() {
            if ( true
                and cpu_checkNumberFormat()
                and Sha1_test()
                and test_rot()
                // and etc...
                // and etc...
            ) {
                return true;
            } else {
                printf("%d fail tests \r\n", __LINE__);
                return false;
            }
            
            return true;
        }
        
        bool real_main(int args_len, char** args_ptr) {
            if (tests()) {
                if (App_init(&global_app)) {
                    while(true) {
                        if (!App_process(&global_app)) break;
                        App_waitToNextFrame(&global_app);
                    }
                    App_deinit(&global_app);
                    return true;
                } else {
                    printf("%d fail: App_init\r\n", __LINE__);
                } 
            } else {
                printf("%d fail tests \r\n", __LINE__);
            }
            return false;
        }
        
        int main(int args_len, char** args_ptr) {
            if (real_main(args_len, args_ptr)) return 0;
            return 1;
        }