├── .gitignore ├── .vscode ├── c_cpp_properties.json └── settings.json ├── Makefile ├── Makefile.gcc ├── Makefile.inc ├── Makefile.inc.inc ├── make.exe ├── xbin.cc ├── xbin.h ├── xblk.cc ├── xblk.h ├── xcodecvt.cc ├── xcodecvt.h ├── xcodecvt_iconv.h ├── xcodecvt_std.h ├── xcodecvt_win.h ├── xcrc.cc ├── xcrc.h ├── xhexbin.cc ├── xhexbin.h ├── xhook.cc ├── xhook.h ├── xlib_test.cc ├── xlib_test.h ├── xlog.cc ├── xlog.h ├── xmsg.cc ├── xmsg.h ├── xrand.cc ├── xrand.h ├── xsig.cc ├── xsig.h ├── xswap.cc ├── xswap.h ├── xvarint.cc ├── xvarint.h ├── xxstring.cc └── xxstring.h /.gitignore: -------------------------------------------------------------------------------- 1 | x64 2 | x86 3 | gcc 4 | *.rar -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Win32", 5 | "includePath": [ 6 | "${workspaceFolder}/**" 7 | ], 8 | "defines": [ 9 | "_DEBUG", 10 | "UNICODE", 11 | "_UNICODE" 12 | ], 13 | "windowsSdkVersion": "10.0.22621.0", 14 | "cStandard": "c17", 15 | "cppStandard": "c++23", 16 | "intelliSenseMode": "windows-msvc-x64" 17 | } 18 | ], 19 | "version": 4 20 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "**/.git": true, 4 | "**/x64": true, 5 | "**/x86": true, 6 | "**/gcc": true, 7 | }, 8 | "files.associations": { 9 | "*.luax": "lua", 10 | "*.rc": "cpp", 11 | "xhash": "cpp", 12 | "xstring": "cpp", 13 | "xtree": "cpp", 14 | "xutility": "cpp", 15 | "algorithm": "cpp", 16 | "array": "cpp", 17 | "atomic": "cpp", 18 | "bit": "cpp", 19 | "bitset": "cpp", 20 | "cctype": "cpp", 21 | "clocale": "cpp", 22 | "cmath": "cpp", 23 | "compare": "cpp", 24 | "concepts": "cpp", 25 | "cstdarg": "cpp", 26 | "cstddef": "cpp", 27 | "cstdint": "cpp", 28 | "cstdio": "cpp", 29 | "cstdlib": "cpp", 30 | "cstring": "cpp", 31 | "ctime": "cpp", 32 | "cwchar": "cpp", 33 | "exception": "cpp", 34 | "functional": "cpp", 35 | "initializer_list": "cpp", 36 | "iomanip": "cpp", 37 | "ios": "cpp", 38 | "iosfwd": "cpp", 39 | "iostream": "cpp", 40 | "istream": "cpp", 41 | "iterator": "cpp", 42 | "limits": "cpp", 43 | "list": "cpp", 44 | "locale": "cpp", 45 | "map": "cpp", 46 | "memory": "cpp", 47 | "new": "cpp", 48 | "ostream": "cpp", 49 | "stdexcept": "cpp", 50 | "streambuf": "cpp", 51 | "string": "cpp", 52 | "system_error": "cpp", 53 | "tuple": "cpp", 54 | "type_traits": "cpp", 55 | "typeinfo": "cpp", 56 | "unordered_map": "cpp", 57 | "utility": "cpp", 58 | "vector": "cpp", 59 | "xfacet": "cpp", 60 | "xiosbase": "cpp", 61 | "xlocale": "cpp", 62 | "xlocbuf": "cpp", 63 | "xlocinfo": "cpp", 64 | "xlocmes": "cpp", 65 | "xlocmon": "cpp", 66 | "xlocnum": "cpp", 67 | "xloctime": "cpp", 68 | "xmemory": "cpp", 69 | "xstddef": "cpp", 70 | "xtr1common": "cpp", 71 | "charconv": "cpp", 72 | "chrono": "cpp", 73 | "condition_variable": "cpp", 74 | "deque": "cpp", 75 | "filesystem": "cpp", 76 | "format": "cpp", 77 | "forward_list": "cpp", 78 | "fstream": "cpp", 79 | "mutex": "cpp", 80 | "optional": "cpp", 81 | "random": "cpp", 82 | "ratio": "cpp", 83 | "sstream": "cpp", 84 | "stop_token": "cpp", 85 | "thread": "cpp", 86 | "any": "cpp", 87 | "regex": "cpp", 88 | "codecvt": "cpp" 89 | }, 90 | "C_Cpp.default.compilerPath": "cl.exe" 91 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 此 Makefile 要满足以下操作: 2 | # 1. 如果当前没有编译环境: 3 | # - 默认会分别编译 x64 和 x86 。 4 | # - 如果指定 x64/x86 ,则指定编译。 5 | # 2. 如果当前有编译环境: 6 | # - 如果判定需要新环境,会新建编译环境。 7 | # - 如果当前环境是 x64 ,默认编译 x64 。 8 | # - 如果指定 x86 ,必定新建编译环境编译 x86 。 9 | # - 如果当前环境是 x86 ,默认编译 x86 。 10 | # - 如果指定 x64 ,必定新建编译环境编译 x64 。 11 | # 3. 如果当前目录不是 Makefile 所在目录,也能正确执行。 12 | 13 | # 在 win7 下,可能默认使用 bash 。这里强制使用 cmd.exe 14 | SHELL = cmd.exe 15 | 16 | # 允许的参数:[x64,x86] 或 空。参数为空时,默认 all 包含 x64 和 x86 。 17 | 18 | .PHONY : all 19 | all : 20 | .PHONY : x64 21 | x64 : 22 | .PHONY : x86 23 | x86 : 24 | 25 | # all_make 让 Makefile.inc 指定生成的内容。 26 | .PHONY : all_make 27 | all_make : 28 | 29 | ################################################################ SRCPATH 30 | # 获取本 Makefile 的路径。 31 | MyPath := $(abspath $(dir $(firstword $(MAKEFILE_LIST)))) 32 | SRCPATH := $(MyPath) 33 | ## $(info SRCPATH = $(SRCPATH)) 34 | ifeq "$(SRCPATH)" "" 35 | $(error SRCPATH empty) 36 | endif 37 | 38 | ################################################################ VCPath 39 | VCPath := $(shell "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere" -latest -property installationPath 2>nul) 40 | ## $(info VCPath = $(VCPath)) 41 | ifeq "$(VCPath)" "" 42 | $(error VCPath empty) 43 | endif 44 | VCPath := $(VCPath)\VC\Auxiliary\Build 45 | vcvarsall := $(VCPath)\vcvarsall.bat 46 | 47 | ################################################################ ARCH 48 | # msvc 有效时,ARCH 有值。 49 | ifeq "$(shell cl >nul 2>&1 || echo yes)" "" 50 | ifeq "$(Platform)" "" 51 | $(error No Platfrom, prehaps < VS2017) 52 | endif 53 | ARCH := $(Platform) 54 | ## $(info ARCH = $(ARCH)) 55 | endif 56 | 57 | ################################################################ make_direct 58 | # 在当前的编译环境中直接编译 59 | 60 | # 注意到: :: 只是连接指令,指令都在依赖后执行,所以要求在依赖前执行输出, :: 并不符合要求。 61 | # 只能添加一个前置虚依赖,以使输出在其他依赖前执行。 62 | .PHONY : make_direct_pre 63 | make_direct_pre : 64 | @echo. 65 | @echo ==== ==== ==== ==== Building ($(ARCH)) ==== ==== ==== ==== 66 | @echo. 67 | .PHONY : make_direct 68 | make_direct : make_direct_pre all_make 69 | @echo. 70 | @echo ==== ==== ==== ==== Done ==== ==== ==== ==== 71 | 72 | ################################################################ make_x64_new & make_x86_new 73 | # 已在 x64/x86 编译环境下,新建编译环境编译。 74 | # 1. x64 编译环境时,要求编译 x86 。需要新环境。 75 | # 2. x86 编译环境时,要求编译 x64 。需要新环境。 76 | # 3. SDK 环境与要求不一致时,需要新环境。 77 | # 因为子环境会继承当前环境,所以需要用 start /I 以使新环境使用默认环境。 78 | # - 注意到,直接 start 会有问题,所以需要先 echo 一下。 79 | # - 因为是 start 新环境,所以无法得知新环境的 make 结果。 80 | # - 所以新开环境的 make 如果成功,则会默认直接退出。make 失败,则会暂停。 81 | .PHONY : make_x64_new 82 | make_x64_new : 83 | @echo. 84 | @echo ==== ==== ==== ==== Building (x64) at new environment ==== ==== ==== ==== 85 | @echo. 86 | @echo. & start "New x64" /I cmd /C \ 87 | "echo ==== ==== ==== ==== Prepare VS Environment for (x64) ... & \ 88 | "$(vcvarsall)" x64 >nul && \ 89 | cd /d "$(SRCPATH)" && \ 90 | "$(MAKE)" --no-print-directory -f "$(abspath $(firstword $(MAKEFILE_LIST)))" || pause>nul" 91 | .PHONY : make_x86_new 92 | make_x86_new : 93 | @echo. 94 | @echo ==== ==== ==== ==== Building (x86) at new environment ==== ==== ==== ==== 95 | @echo. 96 | @echo. & start "New x86" /I cmd /C \ 97 | "echo ==== ==== ==== ==== Prepare VS Environment for (x86) ... & \ 98 | "$(vcvarsall)" x86 >nul && \ 99 | cd /d "$(SRCPATH)" && \ 100 | "$(MAKE)" --no-print-directory -f "$(abspath $(firstword $(MAKEFILE_LIST)))" || pause>nul" 101 | 102 | ################################################################ make_x64 & make_x86 103 | # 当前没有编译环境,在子环境中建立编译环境,并编译。 104 | # - 因为当前没有编译环境,所以继承,对子环境无影响。 105 | # - 命令运行的是子环境,而子环境对当前环境无影响。 106 | .PHONY : make_x64 107 | make_x64 : 108 | @echo. 109 | @echo ==== ==== ==== ==== Prepare VS Environment for (x64) ... 110 | @"$(vcvarsall)" x64 >nul && \ 111 | cd /d "$(SRCPATH)" && \ 112 | "$(MAKE)" --no-print-directory -f "$(abspath $(firstword $(MAKEFILE_LIST)))" 113 | .PHONY : make_x86 114 | make_x86 : 115 | @echo. 116 | @echo ==== ==== ==== ==== Prepare VS Environment for (x86) ... 117 | @"$(vcvarsall)" x86 >nul && \ 118 | cd /d "$(SRCPATH)" && \ 119 | "$(MAKE)" --no-print-directory -f "$(abspath $(firstword $(MAKEFILE_LIST)))" 120 | 121 | ################################################################ 122 | ifeq "$(ARCH)" "" 123 | # 如果当前没有编译环境,默认编译 x64 和 x86 。 124 | all : x64 x86 125 | x64 : make_x64 126 | x86 : make_x86 127 | else ifeq "$(ARCH)" "x64" 128 | # 如果当前环境是 x64 ,默认编译 x64 。 129 | all : x64 130 | # 根据是否需要新环境,采用不同的规则。 131 | ifeq "$(NeedNewEnv)" "" 132 | x64 : make_direct 133 | else 134 | x64 : make_x64_new 135 | endif 136 | # 如果指定编译 x86 ,则需要启动新环境。 137 | x86 : make_x86_new 138 | else ifeq "$(ARCH)" "x86" 139 | # 如果当前环境是 x86 ,默认编译 x86 。 140 | all : x86 141 | # 根据是否需要新环境,采用不同的规则。 142 | ifeq "$(NeedNewEnv)" "" 143 | x86 : make_direct 144 | else 145 | x86 : make_x86_new 146 | endif 147 | # 如果指定编译 x64 ,则需要启动新环境。 148 | x64 : make_x64_new 149 | else 150 | $(error ARCH must be [x86,x64] : $(ARCH)) 151 | endif 152 | 153 | test : 154 | @echo tested 155 | 156 | ################################################################ 157 | DSTPATH := $(SRCPATH)/$(ARCH) 158 | 159 | CC := cl.exe 160 | LINK := link.exe 161 | AR := lib.exe 162 | 163 | ######## CFLAGS 164 | CFLAGS = /c /MP /GS- /Qpar /GL /analyze- /W4 /Gy /Zc:wchar_t /Zi /Gm- /Ox /Zc:inline /fp:precise /DWIN32 /DNDEBUG /D_UNICODE /DUNICODE /fp:except- /errorReport:none /GF /WX /Zc:forScope /GR- /Gd /Oy /Oi /MT /EHa /nologo /std:c++latest 165 | CFLAGS += /I"$(SRCPATH)" 166 | 167 | ifeq "$(ARCH)" "x86" 168 | CFLAGS += /D_USING_V110_SDK71_ 169 | endif 170 | 171 | CFLAGS += $(MyCFLAGS) 172 | 173 | ######## ARFLAGS 174 | ARFLAGS = /LTCG /ERRORREPORT:NONE /NOLOGO /MACHINE:$(ARCH) 175 | ARFLAGS += /LIBPATH:"$(DSTPATH)" 176 | 177 | ######## LDFLAGS 178 | LDFLAGS = /MANIFEST:NO /LTCG /NXCOMPAT /DYNAMICBASE "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /OPT:REF /INCREMENTAL:NO /OPT:ICF /ERRORREPORT:NONE /NOLOGO /MACHINE:$(ARCH) /DEBUG:FULL 179 | LDFLAGS += /LIBPATH:"$(DSTPATH)" 180 | 181 | ifeq "$(ARCH)" "x86" 182 | LDFLAGS_CONSOLE := /SAFESEH /SUBSYSTEM:CONSOLE",5.01" 183 | LDFLAGS_WINDOWS := /SAFESEH /SUBSYSTEM:WINDOWS",5.01" 184 | else 185 | LDFLAGS_CONSOLE := /SUBSYSTEM:CONSOLE 186 | LDFLAGS_WINDOWS := /SUBSYSTEM:WINDOWS 187 | endif 188 | 189 | vpath %.cc $(SRCPATH) 190 | vpath %.h $(SRCPATH) 191 | 192 | vpath %.o $(DSTPATH) 193 | vpath %.lib $(DSTPATH) 194 | vpath %.dll $(DSTPATH) 195 | vpath %.exe $(DSTPATH) 196 | 197 | $(DSTPATH) : 198 | @mkdir "$@" 199 | 200 | %.o : %.cc | $(DSTPATH) 201 | $(CC) $(CFLAGS) /Fd"$(DSTPATH)/" /Fo"$(DSTPATH)/$(@F)" "$<" 202 | 203 | include $(abspath $(dir $(firstword $(MAKEFILE_LIST))))/Makefile.inc -------------------------------------------------------------------------------- /Makefile.gcc: -------------------------------------------------------------------------------- 1 | # 这个 Makefile 用于使用 make & g++ 编译 xlib 。 2 | # make -f Makefile.gcc 3 | 4 | .PHONY : all 5 | all : 6 | 7 | DSTPATH := gcc 8 | 9 | CC := g++ 10 | 11 | ######## CFLAGS 12 | CFLAGS = -lc -O3 -Wall -lstdc++ -std=c++2a -fPIC 13 | 14 | CFLAGS += $(MyCFLAGS) 15 | 16 | vpath %.cc $(SRCPATH) 17 | vpath %.h $(SRCPATH) 18 | 19 | vpath %.o $(DSTPATH) 20 | vpath %.exe $(DSTPATH) 21 | 22 | $(DSTPATH) : 23 | @mkdir -p "$@" 24 | 25 | %.o : %.cc | $(DSTPATH) 26 | $(CC) -c $(CFLAGS) -o"$(DSTPATH)/$(@F)" "$<" 27 | 28 | include Makefile.inc.inc 29 | 30 | all : test.exe 31 | @echo make done. 32 | 33 | test.exe : $(OBJS) | $(DSTPATH) 34 | $(CC) $(CFLAGS) -o"$(DSTPATH)/$(@F)" $(addprefix $(DSTPATH)/,$(^F)) 35 | @"$(DSTPATH)/$(@F)" 36 | -------------------------------------------------------------------------------- /Makefile.inc: -------------------------------------------------------------------------------- 1 | include Makefile.inc.inc 2 | 3 | # 修正位置,取消注释,可测试使用 iconv 库。 4 | #CFLAGS += /I"$(SRCPATH)/../work/gnu/iconv/include" 5 | #LDFLAGS += /LIBPATH:"$(SRCPATH)/../work/gnu/iconv/$(ARCH)" iconv.lib 6 | 7 | all_make : test.exe 8 | @echo make done. 9 | 10 | test.exe : $(OBJS)| $(DSTPATH) 11 | $(LINK) $(LDFLAGS) $(LDFLAGS_CONSOLE) /OUT:"$(DSTPATH)/$(@F)" $(^F) 12 | @"$(DSTPATH)\\$(@F)" -------------------------------------------------------------------------------- /Makefile.inc.inc: -------------------------------------------------------------------------------- 1 | # 这个 Makefile 用于 编译 xlib 的文件包含 。 2 | 3 | XCODECVT_H := xcodecvt_iconv.h xcodecvt_std.h xcodecvt_win.h xcodecvt.h 4 | 5 | xlib_test.o : xlib_test.h 6 | xcrc.o : xlib_test.h xcrc.h 7 | xswap.o : xlib_test.h xswap.h 8 | xrand.o : xlib_test.h xrand.h 9 | xblk.o : xlib_test.h xblk.h 10 | xcodecvt.o : xlib_test.h $(XCODECVT_H) 11 | xmsg.o : xlib_test.h $(XCODECVT_H) xmsg.h 12 | xlog.o : xlib_test.h $(XCODECVT_H) xmsg.h xlog.h 13 | xhexbin.o : xlib_test.h xswap.h $(XCODECVT_H) xmsg.h xhexbin.h 14 | xvarint.o : xlib_test.h xvarint.h 15 | xbin.o : xlib_test.h xswap.h xvarint.h xbin.h 16 | xxstring.o : xlib_test.h $(XCODECVT_H) xmsg.h xxstring.h 17 | xhook.o : xlib_test.h xhook.h 18 | xsig.o : xlib_test.h xswap.h xblk.h $(XCODECVT_H) xmsg.h xlog.h xhexbin.h xvarint.h xbin.h xsig.h 19 | 20 | OBJS := xlib_test.o \ 21 | xcrc.o \ 22 | xswap.o \ 23 | xrand.o \ 24 | xblk.o \ 25 | xcodecvt.o \ 26 | xmsg.o \ 27 | xlog.o \ 28 | xhexbin.o \ 29 | xvarint.o \ 30 | xbin.o \ 31 | xxstring.o \ 32 | xhook.o \ 33 | xsig.o -------------------------------------------------------------------------------- /make.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pig4210/xlib/eb9c5a15bfd35f93a6865b70b8f82fd8a934a8d3/make.exe -------------------------------------------------------------------------------- /xbin.cc: -------------------------------------------------------------------------------- 1 | #include "xbin.h" 2 | 3 | #include "xlib_test.h" 4 | 5 | SHOW_TEST_INIT(xbin) 6 | 7 | xlib::lbin lb, aa; 8 | xlib::gbin gb, bb; 9 | xlib::vbin vb, cc; 10 | 11 | SHOW_TEST_HEAD(<< void*); 12 | lb.clear(); gb.clear(); vb.clear(); 13 | lb << (void*)0x12345678; 14 | gb << (const void*)0; 15 | vb << (void*)12345678; 16 | done = lb.size() == sizeof(void*) && *(void**)lb.data() == (void*)0x12345678 && 17 | gb.size() == sizeof(void*) && *(void**)gb.data() == (void*)0 && 18 | vb.size() == 4 && *(const uint32_t*)vb.data() == (uint32_t)0x05F1C2CE; 19 | SHOW_TEST_RESULT; 20 | 21 | SHOW_TEST_HEAD(<< bool); 22 | lb.clear(); gb.clear(); vb.clear(); 23 | lb << true; 24 | gb << false; 25 | vb << true; 26 | done = lb.size() == sizeof(bool) && *(bool*)lb.data() == true && 27 | gb.size() == sizeof(bool) && *(bool*)gb.data() == false && 28 | vb.size() == 1 && *(bool*)vb.data() == true; 29 | SHOW_TEST_RESULT; 30 | 31 | SHOW_TEST_HEAD(<< T*); 32 | lb.clear(); gb.clear(); vb.clear(); 33 | lb << "123456"; 34 | gb << L"123456"; 35 | vb << (char*)"123456"; 36 | done = lb.size() == 6 && 0 == memcmp(lb.data(), "123456", 6) && 37 | gb.size() == 7 * sizeof(wchar_t) && 0 == memcmp(gb.data(), L"123456", 6 * sizeof(wchar_t)) && 38 | vb.size() == 6 && 0 == memcmp(vb.data(), "123456", 6); 39 | SHOW_TEST_RESULT; 40 | 41 | SHOW_TEST_HEAD(<< xbin); 42 | lb.clear(); aa.clear(); 43 | gb.clear(); bb.clear(); 44 | vb.clear(); cc.clear(); 45 | aa << "123456"; lb << aa; 46 | bb << "123456"; gb << bb; 47 | cc << "123456"; vb << cc; 48 | done = lb.size() == sizeof(uint16_t) + 6 && *(const uint16_t*)lb.data() == 0x0006 && 49 | gb.size() == sizeof(uint16_t) + 7 && *(const uint16_t*)gb.data() == 0x0700 && 50 | vb.size() == 1 + 6 && *(const uint8_t*)vb.data() == 0x06; 51 | SHOW_TEST_RESULT; 52 | 53 | SHOW_TEST_HEAD(<< basic_string); 54 | lb.clear(); gb.clear(); vb.clear(); 55 | lb << std::string("123456"); 56 | gb << std::wstring(L"123456"); 57 | vb << std::string("123456"); 58 | done = lb.size() == 6 && 0 == memcmp(lb.data(), "123456", 6) && 59 | gb.size() == 6 * sizeof(wchar_t) && 0 == memcmp(gb.data(), L"123456", 6 * sizeof(wchar_t)) && 60 | vb.size() == 6 && 0 == memcmp(vb.data(), "123456", 6); 61 | SHOW_TEST_RESULT; 62 | 63 | SHOW_TEST_HEAD(<< unsigned); 64 | lb.clear(); gb.clear(); vb.clear(); 65 | lb << (uint8_t)0x11; 66 | gb << (uint32_t)0x11; 67 | vb << (uint64_t)0x11; 68 | done = lb.size() == 1 && *(uint8_t*)lb.data() == 0x11 && 69 | gb.size() == sizeof(uint32_t) && *(uint32_t*)gb.data() == (uint32_t)0x11000000 && 70 | vb.size() == 1 && *(uint8_t*)vb.data() == 0x11; 71 | SHOW_TEST_RESULT; 72 | 73 | SHOW_TEST_HEAD(<< signed); 74 | lb.clear(); gb.clear(); vb.clear(); 75 | lb << (int8_t)0x11; 76 | gb << (int32_t)0x11; 77 | vb << (int64_t)0x11; 78 | done = lb.size() == 1 && *(uint8_t*)lb.data() == 0x11 && 79 | gb.size() == sizeof(uint32_t) && *(uint32_t*)gb.data() == (uint32_t)0x11000000 && 80 | vb.size() == 1 && *(uint8_t*)vb.data() == 0x22; 81 | SHOW_TEST_RESULT; 82 | 83 | SHOW_TEST_HEAD(<< T&); 84 | lb.clear(); gb.clear(); vb.clear(); 85 | lb << (uint32_t)0x12345678; 86 | gb << (uint32_t)0x12345678; 87 | vb << (uint32_t)0x12345678; 88 | done = lb.size() == sizeof(uint32_t) && *(const uint32_t*)lb.data() == 0x12345678 && 89 | gb.size() == sizeof(uint32_t) && *(const uint32_t*)gb.data() == 0x78563412 && 90 | vb.size() == 5 && memcmp(aa.data(), "\xF8\xAC\xD1\x91\x01", 5); 91 | SHOW_TEST_RESULT; 92 | 93 | SHOW_TEST_HEAD(<< mkhead); 94 | lb.clear(); gb.clear(); vb.clear(); 95 | lb << "123456"; lb.mkhead(); 96 | gb << "123456"; gb.mkhead(); 97 | vb << "123456"; vb.mkhead(); 98 | done = lb.size() == sizeof(uint16_t) + 6 && *(const uint16_t*)lb.data() == 0x0006 && 99 | gb.size() == sizeof(uint16_t) + 7 && *(const uint16_t*)gb.data() == 0x0700 && 100 | vb.size() == 7 && *(const uint8_t*)vb.data() == 0x06; 101 | SHOW_TEST_RESULT; 102 | 103 | SHOW_TEST_HEAD(>> void*); 104 | lb.clear(); gb.clear(); vb.clear(); 105 | lb << (void*)0x12345678 << true << (uint8_t)0x11 << (int8_t)0x11 << "123456" << '\0'; 106 | gb << (void*)0x12345678 << false << (uint32_t)0x11 << (int32_t)0x11 << "123456"; 107 | vb << (void*)0x12345678 << true << (uint64_t)0x11 << (int64_t)0x11 << "123456" << '\0'; 108 | void* vva; 109 | void* vvb; 110 | void* vvc; 111 | lb >> vva; 112 | gb >> vvb; 113 | vb >> vvc; 114 | done = vva == (void*)0x12345678 && vvb == vva && vvc == vva; 115 | SHOW_TEST_RESULT; 116 | 117 | SHOW_TEST_HEAD(>> bool); 118 | bool vba; 119 | bool vbb; 120 | bool vbc; 121 | lb >> vba; 122 | gb >> vbb; 123 | vb >> vbc; 124 | done = vba == true && vbb == false && vbc == true; 125 | SHOW_TEST_RESULT; 126 | 127 | SHOW_TEST_HEAD(>> unsigned); 128 | uint8_t vua; 129 | uint32_t vub; 130 | uint64_t vuc; 131 | lb >> vua; 132 | gb >> vub; 133 | vb >> vuc; 134 | done = vua == 0x11 && vub == 0x11 && vuc == 0x11; 135 | SHOW_TEST_RESULT; 136 | 137 | SHOW_TEST_HEAD(>> signed); 138 | int8_t via; 139 | int32_t vib; 140 | int64_t vic; 141 | lb >> via; 142 | gb >> vib; 143 | vb >> vic; 144 | done = via == 0x11 && vib == 0x11 && vic == 0x11; 145 | SHOW_TEST_RESULT; 146 | 147 | SHOW_TEST_HEAD(>> T*); 148 | char abuf[0x10]; 149 | char bbuf[0x10]; 150 | char cbuf[0x10]; 151 | lb >> (char*)abuf >> '\0'; 152 | gb >> (char*)bbuf; 153 | vb >> (char*)cbuf >> '\0'; 154 | done = lb.empty() && 0 == memcmp(abuf, "123456", 6) && 155 | gb.empty() && 0 == memcmp(bbuf, "123456", 7) && 156 | vb.empty() && 0 == memcmp(cbuf, "123456", 6); 157 | SHOW_TEST_RESULT; 158 | 159 | SHOW_TEST_HEAD(>> xbin); 160 | lb.clear(); gb.clear(); vb.clear(); 161 | aa.clear(); bb.clear(); cc.clear(); 162 | lb << "123456"; lb.mkhead(); lb >> aa; 163 | gb << "123456"; gb.mkhead(); gb >> bb; 164 | vb << "123456"; vb.mkhead(); vb >> cc; 165 | done = lb.empty() && 0 == memcmp(aa.data(), "123456", 6) && 166 | gb.empty() && 0 == memcmp(bb.data(), "123456", 7) && 167 | vb.empty() && 0 == memcmp(cc.data(), "123456", 6); 168 | SHOW_TEST_RESULT; 169 | 170 | SHOW_TEST_HEAD(>> basic_string); 171 | std::string ls, gs, vs; 172 | lb.clear(); gb.clear(); vb.clear(); 173 | lb << "123456"; lb >> ls; 174 | gb << "123456"; gb >> gs; 175 | vb << "123456"; vb >> vs; 176 | done = lb.empty() && 0 == memcmp(ls.data(), "123456", 6) && 177 | gb.empty() && 0 == memcmp(gs.data(), "123456", 7) && 178 | vb.empty() && 0 == memcmp(vs.data(), "123456", 6); 179 | SHOW_TEST_RESULT; 180 | 181 | SHOW_TEST_HEAD(>> T); 182 | uint32_t li, gi, vi; 183 | lb.clear(); gb.clear(); vb.clear(); 184 | lb << (uint32_t)0x12345678; lb >> li; 185 | gb << (uint32_t)0x12345678; gb >> gi; 186 | vb << (uint32_t)0x12345678; vb >> vi; 187 | done = 0x12345678 == li && 0x12345678 == gi && 0x12345678 == vi; 188 | SHOW_TEST_RESULT; 189 | 190 | SHOW_TEST_HEAD(>> const T); 191 | lb.clear(); gb.clear(); vb.clear(); 192 | lb << '\0' >> '\0'; 193 | gb << L'\0' >> L'\0'; 194 | vb << L'\0' >> L'\0'; 195 | done = lb.empty() && gb.empty() && vb.empty(); 196 | SHOW_TEST_RESULT; 197 | 198 | SHOW_TEST_DONE; -------------------------------------------------------------------------------- /xbin.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file xbin.h 3 | \brief 定义了便捷数据组织的类,常用于封包的组织与解析。 4 | 5 | \version 2.1.1.230227 6 | 7 | \author triones 8 | \date 2010-03-26 9 | 10 | \section more 额外说明 11 | 12 | - 数据不足的情况下读取数据默认将抛出 runtime_error 异常。 13 | - 如不愿意抛出异常,请在包含前 #define XBIN_NOEXCEPT 。注意,此时将读出未知数据。 14 | - xbin 类似 struct 库。不采用 pack 、 unpack 形式,有如下理由: 15 | - C 的类型明确, pack 时无需再指定格式化。 16 | - C 无法在 unpack 时返回多值。 17 | 18 | \section history 版本记录 19 | 20 | - 2010-03-30 新加 end() 、 remain() 操作及 += 、 -= 操作。 0.0.1 。 21 | - 2010-04-02 增强 setstr() 功能。 22 | - 2010-04-03 增强 getstr() 功能。 0.0.2 。 23 | - 2010-04-12 新加复制构造函数,现在可以应用于容器。另对几个函数加上 const 以适应一些操作。 0.0.3 。 24 | - 2010-05-10 本要改成 TCHAR* 异常,但考虑特殊性,决定不采用,为记。 25 | - 2010-06-09 考虑到有初始时就需要非网络顺序,新加一个构造函数控制,而不去改变默认构造函数。 26 | - 2010-06-09 新增加一个功能 line& operator<<(line& (*pfn)(line&)) 。 0.0.4 。 27 | - 2010-10-08 增加一些成员函数以避免类型转换时可能导致困惑。 0.0.5 。 28 | - 2010-10-08 但考虑还是保留原类型转换以保持向下兼容。 29 | - 2010-11-06 移植入 LIB ,除了模板保留在头文件,其它小函数也不再 inline ,全部转移到 CPP 。 0.1 。 30 | - 2010-11-23 对 Ring0 支持。 Ring0 下,异常不抛出,但也暂时不知如何处理,结果无法预料。 0.2 。 31 | - 2010-11-23 实际改动只是移除异常抛出而已。 32 | - 2010-11-27 加进一些重载,增加灵活性。另外考虑隐式转换,重新调整函数声明顺序。 0.2.1 。 33 | - 2010-12-11 发现 bswap 对 bool 、结构体类型的支持上的 BUG ,修正之。 0.2.2 。 34 | - 2010-12-21 发现 isempty 函数错误,改正之。 0.2.3 。 35 | - 2010-12-21 发现 >> 重载与模板作用冲突,模板覆盖重载,暂时没有解决方案。 36 | - 2010-12-28 由于 setlen 函数名称的歧义,改造之并将原始功能定义为 lptolen 。 37 | - 2010-12-28 删除原内置模板 bswap 。采用外部 bswap 模板。 0.3 。 38 | - 2010-12-28 考虑把 >> 、 << 操作符转移到外部函数,考虑良久,暂不行动。为记。 39 | - 2011-02-11 改动异常的抛出为 TCHAR 。 0.3.1 。 40 | - 2011-02-18 提升 netline 为 line 类。 0.4 。 41 | - 2011-02-18 类中嵌入快捷缓冲(闪存),多数情况下能在堆栈直接分配空间,避免经常分配内存。 42 | - 2011-02-18 注意这次的改动,数据格式默认为非网络顺序。 43 | - 2011-02-18 去除数据类型转换操作,不再支持隐式转换。 44 | - 2011-02-18 一旦申请缓冲,所有数据都将会在缓冲中,不再使用闪存。 45 | - 2011-02-18 这次更新,缓冲只扩展,不再缩减。 46 | - 2011-02-18 优化 getbuf 、 end 、 getstr 、 setstr ,优化对指针的处理。 47 | - 2011-04-18 屡次考虑变化计数器为 us 以缩减类大小,但权衡下保持为 ui。为记。 48 | - 2011-04-18 决定缩减快捷缓冲大小以减小堆栈压力。 49 | - 2011-04-19 重新调整 net 、 fix 标志,并增加 zeroend 、 otherhead 、 headsize 标志。 0.5 。 50 | - 2011-04-19 为新加的标志提供实现功能; 0.6 。 51 | - 2011-04-19 本来考虑设置统一函数管理标志,这样导致代码不清晰及使用困难,作罢,为记; 52 | - 2011-04-19 重新调整构造函数,使 line 在构造时,能影响多个标志,保持了向下兼容的 line(true) 。 53 | - 2011-04-20 修正昨天功能升级后的处理指针流 BUG 。 54 | - 2011-04-20 顺便改了 setmax 的一些小细节,优化处理流程以提高运行效率。 0.6.1 。 55 | - 2011-04-20 正式移除 LIB 中的 netline 类。 56 | - 2011-05-26 增加成员函数 copystate 以复制状态。 0.6.2 。 57 | - 2011-06-18 修改 getstr 、 setstr 使其具有返回值,返回值即处理数据长度。 0.6.3 。 58 | - 2011-06-22 新加 cnull 宏,方便处理 ASCII 时追加结尾 0 。 0.6.4 。 59 | - 2011-07-15 调整 line_block , size_head ;增加构造初始化对 size_head 的控制; 60 | - 2011-07-15 调整构造函数;新增 hideerr 标志;调整 needmax 、 needlen 函数; 61 | - 2011-07-15 新增 initstate 函数;调整 init 函数; 62 | - 2011-07-15 去除 lptolen 这个危险函数;调整 setmax 函数;增加 snull 宏。 0.6.5 。 63 | - 2011-07-16 修改函数命名以贴近标准容器操作, getbuf == begin ; getlen == size ; isempty == empty ; 64 | - 2011-07-16 去除 operator=(const unsigned int) ;去除 operator+=(const unsigned int) ; 65 | - 2011-07-16 调整 operator=(const line& nline) ;调整 operator+=(const line& nline) ; 66 | - 2011-07-16 调整 setstr 、 getstr 函数;添加 operator>>(T const&) 。 0.7 。 67 | - 2011-07-20 修复 setstr 转入 CPP 造成的诡异无法解释的编码错误。 0.7.1 。 68 | - 2011-08-20 调整 line 类继承 mem_base_base 类。 0.8 。 69 | - 2011-08-20 此次封装提取了一些共同函数。同时删除了 fix 属性。 0.8.1 。 70 | - 2011-09-16 修复 setstr 前缀处理的 BUG 。 0.8.2 。 71 | - 2011-10-31 增加函数 mkhead 。 0.8.3 。 72 | - 2011-12-14 移入 COMMON ,由于 mem_base 的变化,删除两个不再需要的重载。 0.8.4 。 73 | - 2012-01-30 发现 mkhead 的一个 BUG ,已修正。 0.8.5 。 74 | - 2012-05-25 状态修改函数添加前缀 re 。 0.8.6 。 75 | - 2012-05-28 针对基类变化做了一些调整。 0.8.7 。 76 | - 2012-06-06 重新设计 line ,升级为 xline 。 1.0 。 77 | - 2012-06-06 此次升级,所有状态以模版参数设置,不再接受状态临时变化。 78 | - 2012-06-06 保留 line 的定义,同时增加 netline 的定义。 79 | - 2012-09-06 修复 >>(void*) 的隐藏 BUG 。 1.0.1 。 80 | - 2013-03-15 修复 <<(netline) 的 size 两次翻转的 BUG 。 1.0.2 。 81 | - 2013-09-30 转移 setstr 、 getstr 到 xvec 成为 put 、 pick 。 1.0.3 。 82 | - 2014-01-13 引入 SGISTL ,作适应性改动。 1.1 。 83 | - 2015-01-22 为数据不足情况补充可选择的异常机制。 1.2 。 84 | - 2016-11-15 适配 Linux g++ 。添加接口。 1.3 。 85 | - 2017-09-06 改进处理 >>(T*) 。 1.3.1 。 86 | - 2019-10-17 重构,升级为 xbin 。 2.0 。 87 | - 2019-11-06 再次重构,合并 vbin 。 2.1 。 88 | - 2020-03-13 适配 varint 优化。 2.1.1 。 89 | */ 90 | #ifndef _XLIB_XBIN_H_ 91 | #define _XLIB_XBIN_H_ 92 | 93 | #include 94 | 95 | #include 96 | #include 97 | 98 | #include "xswap.h" 99 | #include "xvarint.h" 100 | 101 | namespace xlib { 102 | 103 | /** 104 | xbin 用于便捷的数据组织操作。 105 | \param headtype 数据头类型, byte , word , dword , qword , void。 106 | \param headself 指示数据头是否包含自身长度。 107 | \param bigendian 输入输出的数据是否转换成大端序(默认小端)。 108 | \param zeroend 指示处理流时是否追加处理结尾 0 。 109 | */ 110 | template 111 | class xbin : public std::basic_string { 112 | public: 113 | /*======================== 数据输入 ========================*/ 114 | /** 115 | \code 116 | xbin << (void*)p; 117 | \endcode 118 | */ 119 | xbin& operator<<(const void* const p) { 120 | return operator<<((size_t)p); 121 | } 122 | /** 123 | \code 124 | xbin << true; 125 | \endcode 126 | */ 127 | xbin& operator<<(const bool b) { 128 | return operator<<((uint8_t)b); 129 | } 130 | /** 131 | \code 132 | xbin << "2121321"; 133 | xbin << L"12312"; 134 | \endcode 135 | */ 136 | template 137 | xbin& operator<<(const T* const str) { 138 | if (str == nullptr) return *this; 139 | 140 | size_t len = 0; 141 | while (str[len]) ++len; 142 | if constexpr (!std::is_void_v) len += zeroend ? 1 : 0; 143 | 144 | append((xbin::const_pointer)str, len * sizeof(T)); 145 | return *this; 146 | } 147 | /** 148 | \code 149 | xbin << xbin; 150 | \endcode 151 | */ 152 | xbin& operator<<(const xbin& bin) { 153 | if (&bin == this) return mkhead(); 154 | 155 | if constexpr (!std::is_void_v) { 156 | const size_t nlen = bin.size() + (headself ? sizeof(headtype) : 0); 157 | operator<<((headtype)nlen); 158 | } else { 159 | operator<<(bin.size()); 160 | } 161 | 162 | append(bin.begin(), bin.end()); 163 | return *this; 164 | } 165 | /** 166 | \code 167 | xbin << string("12345678"); 168 | \endcode 169 | */ 170 | template 171 | xbin& operator<<(const std::basic_string& s) { 172 | append((xbin::const_pointer)s.data(), s.size() * sizeof(T)); 173 | return *this; 174 | } 175 | /** 176 | \code 177 | xbin << dword << word << byte; 178 | \endcode 179 | */ 180 | template 181 | std::enable_if_t || std::is_enum_v, xbin>& 182 | operator<<(const T& argvs) { 183 | if constexpr (!std::is_void_v) { 184 | const auto v = bigendian ? bswap(argvs) : argvs; 185 | append((xbin::const_pointer)&v, sizeof(v)); 186 | } else { 187 | const xvarint v(argvs); 188 | append((xbin::const_pointer)v.data(), v.size()); 189 | } 190 | return *this; 191 | } 192 | 193 | xbin& mkhead() { 194 | if constexpr (!std::is_void_v) { 195 | const auto nlen = size() + (headself ? sizeof(headtype) : 0); 196 | const auto v = (bigendian ? bswap((headtype)nlen) : (headtype)nlen); 197 | insert(0, (xbin::const_pointer)&v, sizeof(v)); 198 | } else { 199 | const xvarint v(size()); 200 | insert(0, (xbin::const_pointer)v.data(), v.size()); 201 | } 202 | return *this; 203 | } 204 | 205 | public: 206 | /*======================== 数据输出 ========================*/ 207 | /** 208 | \code 209 | xbin >> (void*)p; 210 | \endcode 211 | */ 212 | xbin& operator>>(void*& p) { 213 | return operator>>((size_t&)p); 214 | } 215 | /** 216 | \code 217 | bool b; 218 | xbin >> b; 219 | \endcode 220 | */ 221 | xbin& operator>>(bool& b) { 222 | return operator>>(*(uint8_t*)&b); 223 | } 224 | /** 225 | \code 226 | xbin >> (char*)lpstr; 227 | \endcode 228 | \exception 数据不足时,抛出 runtime_error 异常。 229 | \note 允许空指针,这样将丢弃一串指定类型的数据。 230 | */ 231 | template 232 | xbin& operator>>(T* str) { 233 | const T* lpstr = (const T*)data(); 234 | 235 | size_t strlen = 0; 236 | while (lpstr[strlen]) ++strlen; 237 | if constexpr (!std::is_void_v) strlen += zeroend ? 1 : 0; 238 | strlen *= sizeof(T); 239 | 240 | #ifndef XBIN_NOEXCEPT 241 | if (strlen > size()) { 242 | throw std::runtime_error("xbin >> T* not enough data"); 243 | } 244 | #endif 245 | 246 | if (nullptr != str) memcpy(str, lpstr, strlen); 247 | 248 | erase(0, strlen); 249 | 250 | return *this; 251 | } 252 | /** 253 | \code 254 | xbin >> xbin; 255 | \endcode 256 | \exception 数据不足时,抛出 runtime_error 异常。 257 | \note 当操作自身时,按数据头长度截断。 258 | */ 259 | xbin& operator>>(xbin& bin) { 260 | size_t nlen; 261 | if constexpr (!std::is_void_v) { 262 | headtype xlen; 263 | operator>>(xlen); 264 | 265 | nlen = (headtype)xlen; 266 | nlen -= (headself ? sizeof(headtype) : 0); 267 | } else { 268 | operator>>(nlen); 269 | } 270 | 271 | #ifndef XBIN_NOEXCEPT 272 | if (nlen > size()) { 273 | throw std::runtime_error("xbin >> xbin& not enough data"); 274 | } 275 | #endif 276 | 277 | if (&bin == this) { 278 | erase(begin() + nlen, end()); 279 | return *this; 280 | } 281 | 282 | bin.assign((xbin::const_pointer)data(), nlen); 283 | erase(0, nlen); 284 | 285 | return *this; 286 | } 287 | /** 288 | 模板适用于标准库字符串。数据倾倒。 289 | 290 | \code 291 | xbin >> string; 292 | \endcode 293 | */ 294 | template 295 | xbin& operator>>(std::basic_string& s) { 296 | s.assign((const T*)data(), size() / sizeof(T)); 297 | clear(); 298 | return *this; 299 | } 300 | /** 301 | 模板适用于内置类型,结构体等。 302 | 303 | \code 304 | xbin >> dword >> word >> byte; 305 | \endcode 306 | \exception 数据不足时,抛出 runtime_error 异常。 307 | */ 308 | template 309 | inline std::enable_if_t || std::is_enum_v, xbin>& 310 | operator>>(T& argvs) { 311 | if constexpr (!std::is_void_v) { 312 | #ifndef XBIN_NOEXCEPT 313 | if (sizeof(T) > size()) { 314 | throw std::runtime_error("xbin >> T& not enough data"); 315 | } 316 | #endif 317 | memcpy(&argvs, data(), sizeof(T)); 318 | erase(0, sizeof(T)); 319 | argvs = bigendian ? bswap(argvs) : argvs; 320 | } else { 321 | const xvarint vi(data()); 322 | argvs = vi; 323 | const size_t typesize = vi.size(); 324 | #ifndef XBIN_NOEXCEPT 325 | if (typesize == 0 || typesize > size()) { 326 | throw std::runtime_error("xbin >> T& not enough data / data error"); 327 | } 328 | #endif 329 | erase(0, typesize); 330 | } 331 | 332 | return *this; 333 | } 334 | /** 335 | 目的用以跳过某些不需要的数据。 336 | 337 | \code 338 | xbin >> cnull >> snull >> 0; 339 | \endcode 340 | \exception 数据不足时,抛出 runtime_error 异常。 341 | */ 342 | template 343 | xbin& operator>>(const T&) { 344 | if constexpr (!std::is_void_v) { 345 | #ifndef XBIN_NOEXCEPT 346 | if (sizeof(T) > size()) { 347 | throw std::runtime_error("xbin >> xbin& not enough data"); 348 | } 349 | #endif 350 | erase(0, sizeof(T)); 351 | return *this; 352 | } else { 353 | T argvs; 354 | return operator>>(argvs); 355 | } 356 | } 357 | }; 358 | 359 | /// lbin 数据头为 word ,不包含自身,小端序,不处理结尾 0 。 360 | using lbin = xbin; 361 | /// gbin 数据头为 word ,不包含自身,大端顺序,处理结尾 0 。 362 | using gbin = xbin; 363 | /// vbin 为 xvarint 格式,忽略后继所有设置。 364 | using vbin = xbin; 365 | 366 | } // namespace xlib 367 | 368 | #endif // _XLIB_XBIN_H_ -------------------------------------------------------------------------------- /xblk.cc: -------------------------------------------------------------------------------- 1 | #include "xblk.h" 2 | 3 | #include "xlib_test.h" 4 | 5 | SHOW_TEST_INIT(xblk) 6 | 7 | void* const a = (void*)0x1000; 8 | void* const b = (void*)0x1100; 9 | const size_t size = 0x100; 10 | const xlib::xblk blk(b, a); 11 | 12 | SHOW_TEST_HEAD(xblk); 13 | done = blk.begin() == a && blk.end() == b && blk.size() == size; 14 | SHOW_TEST_RESULT; 15 | 16 | SHOW_TEST_HEAD(xblk NoIn); 17 | done = xlib::xblk::NoIn == blk.check((void*)0x1111); 18 | SHOW_TEST_RESULT; 19 | 20 | SHOW_TEST_HEAD(xblk WholeIn); 21 | done = xlib::xblk::WholeIn == blk.check((const void*)0x1005); 22 | SHOW_TEST_RESULT; 23 | 24 | SHOW_TEST_HEAD(xblk HeadIn); 25 | done = xlib::xblk::HeadIn == blk.check((char*)0x10FF, 3); 26 | SHOW_TEST_RESULT; 27 | 28 | SHOW_TEST_HEAD(xblk TailIn); 29 | done = xlib::xblk::TailIn == blk.check((const char*)0x0FFF, 3); 30 | SHOW_TEST_RESULT; 31 | 32 | SHOW_TEST_HEAD(xblk SubIn); 33 | done = xlib::xblk::SubIn == blk.check((void*)0x0FFF, 0x1101); 34 | SHOW_TEST_RESULT; 35 | 36 | SHOW_TEST_HEAD(overload); 37 | done = size == xlib::xblk(( void*)a, ( void*)b).size() && 38 | size == xlib::xblk((const void*)a, ( void*)b).size() && 39 | size == xlib::xblk((const void*)a, (const void*)b).size() && 40 | size == xlib::xblk(( void*)a, (const void*)b).size() && 41 | size == xlib::xblk(( char*)a, ( char*)b).size() && 42 | size == xlib::xblk((const char*)a, ( char*)b).size() && 43 | size == xlib::xblk((const char*)a, (const char*)b).size() && 44 | size == xlib::xblk(( char*)a, (const char*)b).size() && 45 | size == xlib::xblk(( void*)a, size).size() && 46 | size == xlib::xblk((const void*)a, size).size() && 47 | size == xlib::xblk((const char*)a, size).size() && 48 | size == xlib::xblk(( char*)a, size).size() && 49 | size == xlib::xblk(( uint16_t*)a, size / sizeof(uint16_t)).size(); 50 | SHOW_TEST_RESULT; 51 | 52 | SHOW_TEST_DONE; -------------------------------------------------------------------------------- /xblk.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file xblk.h 3 | \brief 定义了内存块比对操作的类。 4 | 5 | \version 2.0.1.230224 6 | 7 | \author triones 8 | \date 2012-09-12 9 | 10 | \section history 版本记录 11 | 12 | - 2012-09-12 有封装需要,决定封装此操作。 1.0 。 13 | - 2016-11-15 适配 Linux g++ 。 1.1 。 14 | - 2019-08-01 PosDcrpt 删除前缀。 1.1.1 。 15 | - 2019-09-20 重构 xblk 。 2.0 。 16 | */ 17 | #ifndef _XLIB_XBLK_H_ 18 | #define _XLIB_XBLK_H_ 19 | 20 | #include 21 | #include 22 | 23 | namespace xlib { 24 | 25 | class xblk { 26 | public: 27 | enum PosDcrpt { 28 | NoIn, //< 不在块内。 29 | WholeIn, //< 全部在块内。 30 | HeadIn, //< 前部在块内,尾部在块外。 31 | TailIn, //< 前部在块外,尾部在块内。 32 | SubIn, //< 中间部分在块内。 33 | }; 34 | 35 | public: 36 | /// 默认构造表示非法块。 37 | constexpr xblk() : _beg(nullptr), _end(nullptr), _size(0) {} 38 | // 没有复杂对象,不需要额外定义拷贝构造移动构造。 39 | /// 允许设置起始与结束位置初始化,自动识别起始与结束。 40 | template 41 | constexpr xblk(const T* const a, const T* const b) 42 | : _beg (((size_t)a < (size_t)b) ? a : b), 43 | _end (((size_t)a > (size_t)b) ? a : b), 44 | _size ((size_t)_end - (size_t)_beg) {} 45 | /// 允许设置起始位置与大小初始化,允许 diff 为负值。 46 | template 47 | constexpr xblk(const T* const a, const intptr_t diff = 1) 48 | : xblk(a, a + diff) {} 49 | constexpr xblk(const void* const a, const intptr_t diff = 1) 50 | : xblk((const char*)a, (const char*)a + diff) {} 51 | constexpr xblk(const void* const a, const void* const b) 52 | : xblk((const char*)a, (const char*)b) {} 53 | constexpr auto begin() const { return _beg; } 54 | constexpr auto end() const { return _end; } 55 | constexpr auto size() const { return _size; } 56 | constexpr auto data() const { return _beg; } 57 | /// 判定目标块与本块的关系。 58 | constexpr PosDcrpt check(const xblk& blk) const { 59 | const char* const s = (const char*)_beg; 60 | const char* const e = (const char*)_end; 61 | const char* const ss = (const char*)blk.begin(); 62 | const char* const ee = (const char*)blk.end(); 63 | if (ss < s) { 64 | if (ee < s) return NoIn; 65 | if (ee > e) return SubIn; 66 | return TailIn; 67 | } 68 | if (ss > e) return NoIn; 69 | if (ee > e) return HeadIn; 70 | return WholeIn; 71 | } 72 | template 73 | constexpr PosDcrpt check(const T* const a, const T* const b) const { 74 | return check(xblk(a, b)); 75 | } 76 | template 77 | constexpr PosDcrpt check(const T* const a, const intptr_t diff = 1) const { 78 | return check(xblk(a, diff)); 79 | } 80 | constexpr PosDcrpt check(const void* const a, const intptr_t diff = 1) const { 81 | return check(xblk(a, diff)); 82 | } 83 | constexpr PosDcrpt check(const void* const a, const void* b) const { 84 | return check(xblk(a, b)); 85 | } 86 | /// 比较操作。 87 | constexpr bool operator==(const xblk& blk) const { 88 | return (_beg == blk.begin()) && (_end == blk.end()); 89 | } 90 | constexpr bool operator!=(const xblk& blk) const { return !operator==(blk); } 91 | 92 | private: 93 | const void* _beg; //< 块首。 94 | const void* _end; //< 块尾。 95 | size_t _size; //< 块大小。 96 | }; 97 | 98 | } // namespace xlib 99 | 100 | #endif // _XLIB_XBLK_H_ -------------------------------------------------------------------------------- /xcodecvt.cc: -------------------------------------------------------------------------------- 1 | #include "xcodecvt.h" 2 | 3 | #include "xlib_test.h" 4 | 5 | #ifndef __cpp_char8_t 6 | #pragma message("xlib define char8_t") 7 | #endif 8 | 9 | #ifndef __cpp_lib_char8_t 10 | #pragma message("xlib define std::u8string") 11 | #endif 12 | 13 | #if __has_include() 14 | #pragma message("xlib use xcodecvt_iconv.h") 15 | #else 16 | #if defined(_WIN32) && _MSVC_LANG <= 202002L 17 | #pragma message("xlib use xcodecvt_win.h") 18 | #else 19 | #pragma message("xlib use xcodecvt_std.h") 20 | #endif 21 | #endif 22 | 23 | SHOW_TEST_INIT(xcodecvt) 24 | 25 | // 测试字符串:"AA转换测试BB\0CC" 。 g++ 默认编码 UTF8 ,为了通用, ASCII 书写使用硬编码。 26 | // 特意加上 0 编码用于完整转换测试。 27 | const std::string asbuf("AA\xD7\xAA\xBB\xBB\xB2\xE2\xCA\xD4\x42\x42\0CC", 15); 28 | const std::wstring wsbuf(L"AA转换测试BB\0CC", 11); 29 | const std::u8string u8buf((const char8_t*)u8"AA转换测试BB\0CC", 19); 30 | 31 | SHOW_TEST_HEAD(as2ws); 32 | done = wsbuf == xlib::as2ws(asbuf); 33 | SHOW_TEST_RESULT; 34 | 35 | SHOW_TEST_HEAD(ws2as); 36 | done = asbuf == xlib::ws2as(wsbuf); 37 | SHOW_TEST_RESULT; 38 | 39 | SHOW_TEST_HEAD(u82ws); 40 | done = wsbuf == xlib::u82ws(u8buf); 41 | SHOW_TEST_RESULT; 42 | 43 | SHOW_TEST_HEAD(ws2u8); 44 | done = u8buf == xlib::ws2u8(wsbuf); 45 | SHOW_TEST_RESULT; 46 | 47 | SHOW_TEST_HEAD(as2u8); 48 | done = u8buf == xlib::as2u8(asbuf); 49 | SHOW_TEST_RESULT; 50 | 51 | SHOW_TEST_HEAD(u82as); 52 | done = asbuf == xlib::u82as(u8buf); 53 | SHOW_TEST_RESULT; 54 | 55 | SHOW_TEST_HEAD(u82ws emoji); 56 | done = L"🚚" == xlib::u82ws((const char8_t*)u8"🚚"); 57 | SHOW_TEST_RESULT; 58 | 59 | SHOW_TEST_HEAD(ws2u8 emoji); 60 | done = (const char8_t*)u8"🚚" == xlib::ws2u8(L"🚚"); 61 | SHOW_TEST_RESULT; 62 | 63 | SHOW_TEST_DONE; -------------------------------------------------------------------------------- /xcodecvt.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file xcodecvt.h 3 | \brief 用于 ANSI 与 UNICODE 及 UTF8 等编码的本地化转换。 4 | 5 | \version 0.0.1.241126 6 | 7 | \author triones 8 | \date 2019-08-02 9 | 10 | \section more 额外说明 11 | 12 | - linux 下,注意查询本地化支持: `locale -a` (否则,会在 `std::locale` 时崩溃)。 13 | - 安装 GB2312 : `sudo locale-gen zh_CN` 。 14 | - 安装 UTF8 中文: `sudo apt-get install language-pack-zh-hans` 。 15 | - g++ 默认编码为 UTF8 ,如需以 ANSI 编译,需加入编译参数如: `-fexec-charset=GB2312` 。 16 | - 需要 gcc-9.2.0 及以上支持。 17 | - 当 iconv.h 存在时,优先使用 iconv 。 18 | - Windows 默认使用 Windows API 进行编码转换。 19 | - VS2017 对 codecvt 有各种局限 。 20 | - 在实际应用中发现,windows 下,codecvt 转换修改 crt 的 locale 环境,虽然多线程安全,但并非并发。 21 | - NT HOOK 回调中涉及 locale 时,目前发现在 Windows Defender 介入时,会因 locale 导致死锁。 22 | 23 | \section history 版本记录 24 | 25 | - 2024-11-26 重建 。 0.0.1 。 26 | */ 27 | #ifndef _XLIB_XCODECVT_H_ 28 | #define _XLIB_XCODECVT_H_ 29 | 30 | #include 31 | 32 | // 不支持 chat8_t 时,自制 char8_t 。 33 | #ifndef __cpp_char8_t 34 | using char8_t = unsigned char; 35 | #define XTEXT(__s) (const char8_t*)u8 ## __s 36 | #define XCHAR(__c) (char8_t)u8 ## __c 37 | #else // __cpp_char8_t 38 | #define XTEXT(__s) u8 ## __s 39 | #define XCHAR(__c) u8 ## __c 40 | #endif // __cpp_char8_t 41 | 42 | // 不支持 std::u8string 时,自制 std::u8string 。 43 | #ifndef __cpp_lib_char8_t 44 | // 违反规则,添加进 std ,以模拟实现。 45 | namespace std { 46 | using u8string = basic_string, allocator>; 47 | using u8string_view = basic_string_view>; 48 | } 49 | #endif 50 | 51 | namespace xlib { 52 | // 因为 isprint 与 isspace 不是 constexpr ,所以自制。 53 | constexpr bool inline is_easy_transcoding(const unsigned long c) { 54 | return (c >= 0x20 && c <= 0x7E) || (c >= 0x9 && c <= 0xD); 55 | } 56 | constexpr bool inline is_easy_transcoding(const char& c) { 57 | return is_easy_transcoding((unsigned long)c); 58 | } 59 | constexpr bool inline is_easy_transcoding(const char8_t& c) { 60 | return is_easy_transcoding((unsigned long)c); 61 | } 62 | constexpr bool inline is_easy_transcoding(const wchar_t& c) { 63 | return is_easy_transcoding((unsigned long)c); 64 | } 65 | } // namespace xlib 66 | 67 | // c++20 中,codecvt 被弃用。如果有 iconv.h ,则使用 iconv 。 68 | #if __has_include() 69 | #include "xcodecvt_iconv.h" 70 | #else 71 | #if defined(_WIN32) 72 | #include "xcodecvt_win.h" 73 | #else 74 | #include "xcodecvt_std.h" 75 | #endif 76 | #endif 77 | 78 | #endif // _XLIB_XCODECVT_H_ -------------------------------------------------------------------------------- /xcodecvt_iconv.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file xcodecvt_iconv.h 3 | \brief 用于 ANSI 与 UNICODE 及 UTF8 等编码的本地化转换。使用 gnu libiconv 库。 4 | 5 | \version 0.0.1.241126 6 | 7 | \author triones 8 | \date 2024-11-26 9 | 10 | \section history 版本记录 11 | 12 | - 2024-11-26 新建 。 0.0.1 。 13 | */ 14 | #ifndef _XLIB_XCODECVT_ICONV_H_ 15 | #define _XLIB_XCODECVT_ICONV_H_ 16 | 17 | #include 18 | 19 | #include 20 | 21 | namespace xlib { 22 | 23 | /// 允许通过设置 LOCALE_AS_WS 宏,改变默认 ANSI 编码。 24 | #ifndef LOCALE_AS_WS 25 | #define LOCALE_AS_WS ("GB2312") 26 | #endif 27 | 28 | /// 允许通过设置 LOCALE_WS_U8 宏,改变默认 UTF8 编码。 29 | #ifndef LOCALE_WS_U8 30 | #define LOCALE_WS_U8 ("UTF-8") 31 | #endif 32 | 33 | #ifdef _WIN32 34 | using iconv_from_type = const char**; 35 | #else 36 | using iconv_from_type = char**; 37 | #endif 38 | 39 | /** 40 | ANSI 串转换 UNICODE 串。 41 | \param as 需要转换的 ANSI 串 42 | \param lpread 返回转换 ANSI 字符数。指针默认为 nullptr 时,采用贪婪模式,转换失败的字符,以 ? 代替。 43 | \return 转换后的对应 UNICODE 串。 44 | 45 | \code 46 | auto ws = as2ws("文字"); 47 | \endcode 48 | */ 49 | inline std::wstring as2ws(const std::string& as, size_t* const lpread = nullptr) { 50 | if (as.empty()) return {}; 51 | 52 | size_t rd; 53 | size_t& read = (lpread == nullptr) ? rd : *lpread; 54 | read = 0; 55 | 56 | size_t write = 0; 57 | 58 | #ifdef _WIN32 59 | auto cvt = iconv_open("UTF-16LE", LOCALE_AS_WS); 60 | #else 61 | auto cvt = iconv_open("WCHAR_T", LOCALE_AS_WS); 62 | #endif 63 | if (cvt == (iconv_t)-1) return {}; 64 | 65 | std::wstring ws(as.size(), L'\0'); 66 | 67 | while (read < as.size()) { 68 | auto from = as.data() + read; 69 | size_t from_left = as.size() - read; 70 | const auto from_next = from_left; 71 | 72 | auto to = ws.data() + write; 73 | size_t to_left = (ws.size() - write) * sizeof(wchar_t); 74 | const auto to_next = to_left; 75 | 76 | const auto result = iconv(cvt, (iconv_from_type)&from, &from_left, (char**)&to, &to_left); 77 | 78 | read += from_next - from_left; 79 | write += (to_next - to_left) / sizeof(wchar_t); 80 | 81 | if (nullptr != lpread) break; 82 | if (result == (size_t)-1) { 83 | ++read; 84 | *(ws.data() + write) = L'?'; 85 | ++write; 86 | } 87 | } 88 | 89 | iconv_close(cvt); 90 | 91 | ws.resize(write); 92 | return ws; 93 | } 94 | 95 | /** 96 | UNICODE 串转换 ANSI 串。 97 | \param ws 需要转换的 UNICODE 串。 98 | \param lpread 返回转换 ANSI 字符数。指针默认为 nullptr 时,采用贪婪模式,转换失败的字符,以 ? 代替。 99 | \return 转换后的对应 ANSI 串。 100 | 101 | \code 102 | auto as = ws2as(L"文字"); 103 | \endcode 104 | */ 105 | inline std::string ws2as(const std::wstring& ws, size_t* const lpread = nullptr) { 106 | if (ws.empty()) return {}; 107 | 108 | size_t rd; 109 | size_t& read = (nullptr == lpread) ? rd : *lpread; 110 | read = 0; 111 | 112 | size_t write = 0; 113 | 114 | #ifdef _WIN32 115 | auto cvt = iconv_open(LOCALE_AS_WS, "UTF-16LE"); 116 | #else 117 | auto cvt = iconv_open(LOCALE_AS_WS, "WCHAR_T"); 118 | #endif 119 | 120 | std::string as(ws.size() * 6, '\0'); 121 | 122 | while (read < ws.size()) { 123 | auto from = ws.data() + read; 124 | size_t from_left = (ws.size() - read) * sizeof(wchar_t); 125 | const auto from_next = from_left; 126 | 127 | auto to = as.data() + write; 128 | size_t to_left = as.size() - write; 129 | const auto to_next = to_left; 130 | 131 | const auto result = iconv(cvt, (iconv_from_type)&from, &from_left, &to, &to_left); 132 | 133 | read += (from_next - from_left) / sizeof(wchar_t); 134 | write += to_next - to_left; 135 | 136 | if (nullptr != lpread) break; 137 | if (result == (size_t)-1) { 138 | ++read; 139 | *(as.data() + write) = '?'; 140 | ++write; 141 | } 142 | } 143 | 144 | iconv_close(cvt); 145 | 146 | as.resize(write); 147 | return as; 148 | } 149 | 150 | /** 151 | UTF8 串转换 UNICODE 串。 152 | \param u8 需要转换的 UTF8 串。 153 | \param lpread 返回转换 ANSI 字符数。指针默认为 nullptr 时,采用贪婪模式,转换失败的字符,以 ? 代替。 154 | \return 转换后的对应 UNICODE 串。 155 | 156 | \code 157 | auto ws(u82ws(u8"文字")); 158 | \endcode 159 | */ 160 | inline std::wstring u82ws(const std::u8string& u8, size_t* const lpread = nullptr) { 161 | if (u8.empty()) return {}; 162 | 163 | size_t rd; 164 | size_t& read = (nullptr == lpread) ? rd : *lpread; 165 | read = 0; 166 | 167 | size_t write = 0; 168 | 169 | #ifdef _WIN32 170 | auto cvt = iconv_open("UTF-16LE", LOCALE_WS_U8); 171 | #else 172 | auto cvt = iconv_open("WCHAR_T", LOCALE_WS_U8); 173 | #endif 174 | 175 | std::wstring ws(u8.size(), L'\0'); 176 | 177 | while (read < u8.size()) { 178 | auto from = u8.data() + read; 179 | size_t from_left = u8.size() - read; 180 | const auto from_next = from_left; 181 | 182 | auto to = ws.data() + write; 183 | size_t to_left = (ws.size() - write) * sizeof(wchar_t); 184 | const auto to_next = to_left; 185 | 186 | const auto result = iconv(cvt, (iconv_from_type)&from, &from_left, (char**)&to, &to_left); 187 | 188 | read += from_next - from_left; 189 | write += (to_next - to_left) / sizeof(wchar_t); 190 | 191 | if (nullptr != lpread) break; 192 | if (result == (size_t)-1) { 193 | ++read; 194 | *(ws.data() + write) = L'?'; 195 | ++write; 196 | } 197 | } 198 | 199 | iconv_close(cvt); 200 | 201 | ws.resize(write); 202 | return ws; 203 | } 204 | 205 | /** 206 | UNICODE 串转换 UTF8 串。 207 | \param ws 需要转换的 UNICODE 串。 208 | \param lpread 返回转换 ANSI 字符数。指针默认为 nullptr 时,采用贪婪模式,转换失败的字符,以 ? 代替。 209 | \return 转换后的对应 UTF8 串。 210 | 211 | \code 212 | auto u8(ws2u8(L"文字")); 213 | \endcode 214 | */ 215 | inline std::u8string ws2u8(const std::wstring& ws, size_t* const lpread = nullptr) { 216 | if (ws.empty()) return {}; 217 | 218 | size_t rd; 219 | size_t& read = (nullptr == lpread) ? rd : *lpread; 220 | read = 0; 221 | 222 | size_t write = 0; 223 | 224 | #ifdef _WIN32 225 | auto cvt = iconv_open(LOCALE_WS_U8, "UTF-16LE"); 226 | #else 227 | auto cvt = iconv_open(LOCALE_WS_U8, "WCHAR_T"); 228 | #endif 229 | 230 | std::u8string u8(ws.size() * 6, char8_t('\0')); 231 | 232 | while (read < ws.size()) { 233 | auto from = ws.data() + read; 234 | size_t from_left = (ws.size() - read) * sizeof(wchar_t); 235 | const auto from_next = from_left; 236 | 237 | auto to = u8.data() + write; 238 | size_t to_left = u8.size() - write; 239 | const auto to_next = to_left; 240 | 241 | const auto result = iconv(cvt, (iconv_from_type)&from, &from_left, (char**)&to, &to_left); 242 | 243 | read += (from_next - from_left) / sizeof(wchar_t); 244 | write += to_next - to_left; 245 | 246 | if (nullptr != lpread) break; 247 | if (result == (size_t)-1) { 248 | ++read; 249 | *(u8.data() + write) = char8_t(u8'?'); 250 | ++write; 251 | } 252 | } 253 | 254 | iconv_close(cvt); 255 | 256 | u8.resize(write); 257 | return u8; 258 | } 259 | 260 | /** 261 | ANSI 串转换 UTF8 串。 262 | \param as 需要转换的 ANSI 串。 263 | \return 转换后的对应 UTF8 串。 264 | 265 | \code 266 | auto u8(as2u8("文字")); 267 | \endcode 268 | */ 269 | inline std::u8string as2u8(const std::string& as, size_t* const lpread = nullptr) { 270 | for (const auto& c : as) { 271 | if (!is_easy_transcoding(c)) return ws2u8(as2ws(as, lpread)); 272 | } 273 | // 纯英文字符,无需转换。 274 | size_t rd; 275 | size_t& read = (nullptr == lpread) ? rd : *lpread; 276 | read = as.size(); 277 | return std::u8string((const char8_t*)as.data(), as.size()); 278 | } 279 | 280 | /** 281 | UTF8 串转换 ANSI 串。 282 | \param u8 需要转换的 UTF8 串。 283 | \return 转换后的对应 ANSI 串。 284 | 285 | \code 286 | auto as(u82as(u8"文字")); 287 | \endcode 288 | */ 289 | inline std::string u82as(const std::u8string& u8, size_t* const lpread = nullptr) { 290 | for (const auto& c : u8) { 291 | if (!is_easy_transcoding(c)) return ws2as(u82ws(u8, lpread)); 292 | } 293 | // 纯英文字符,无需转换。 294 | size_t rd; 295 | size_t& read = (nullptr == lpread) ? rd : *lpread; 296 | read = u8.size(); 297 | return std::string((const char*)u8.data(), u8.size()); 298 | } 299 | 300 | #undef LOCALE_AS_WS 301 | #undef LOCALE_WS_U8 302 | 303 | } // namespace xlib 304 | 305 | #endif // _XLIB_XCODECVT_ICONV_H_ -------------------------------------------------------------------------------- /xcodecvt_std.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file xcodecvt_std.h 3 | \brief 用于 ANSI 与 UNICODE 及 UTF8 等编码的本地化转换,使用 标准库。 4 | 5 | \version 2.4.1.241126 6 | 7 | \author triones 8 | \date 2019-08-02 9 | 10 | \section more 额外说明 11 | 12 | - C++20 中,指出 codecvt 被弃用,虽然 VS2022 仍然支持,但这里独立成文件,以便替换。 13 | 14 | \section history 版本记录 15 | 16 | - 2019-08-05 新建。 0.1 。 17 | - 2019-09-25 使用 codecvt 重构。 1.0 。 18 | - 2019-11-03 引入 u8string ,再次重构。 2.0 。 19 | - 2020-05-14 加入贪婪处理方式。 2.1 。 20 | - 2021-08-04 处理 ws 转换 emoji 不正确的 BUG。 2.2 。 21 | - 2022-01-05 支持 c++17 。 2.3 。 22 | - 2022-08-25 支持替换为三方编码转换。 23 | */ 24 | #ifndef _XLIB_XCODECVT_STD_H_ 25 | #define _XLIB_XCODECVT_STD_H_ 26 | 27 | #include 28 | #include 29 | 30 | namespace xlib { 31 | 32 | /// 允许通过设置 LOCALE_AS_WS 宏,改变默认 ANSI 编码。 33 | #ifndef LOCALE_AS_WS 34 | #ifdef _WIN32 35 | #define LOCALE_AS_WS ("zh-CN") 36 | #else 37 | #define LOCALE_AS_WS ("zh_CN.GB2312") 38 | #endif 39 | #endif 40 | 41 | /// 允许通过设置 LOCALE_WS_U8 宏,改变默认 UTF8 编码。 42 | #ifndef LOCALE_WS_U8 43 | #ifdef _WIN32 44 | #define LOCALE_WS_U8 ("zh-CN.UTF8") 45 | #else 46 | #define LOCALE_WS_U8 ("zh_CN.UTF8") 47 | #endif 48 | #endif 49 | 50 | /** 51 | ANSI 串转换 UNICODE 串。 52 | \param as 需要转换的 ANSI 串 53 | \param lpread 返回转换 ANSI 字符数。指针默认为 nullptr 时,采用贪婪模式,转换失败的字符,以 ? 代替。 54 | \return 转换后的对应 UNICODE 串。 55 | 56 | \code 57 | auto ws = as2ws("文字"); 58 | \endcode 59 | */ 60 | inline std::wstring as2ws(const std::string& as, size_t* const lpread = nullptr) { 61 | if (as.empty()) return {}; 62 | 63 | size_t rd; 64 | size_t& read = (nullptr == lpread) ? rd : *lpread; 65 | read = 0; 66 | 67 | size_t write = 0; 68 | 69 | using XCODECVT = std::codecvt; 70 | std::locale locale(LOCALE_AS_WS); 71 | auto& cvt = std::use_facet(locale); 72 | 73 | std::wstring ws(as.size(), L'\0'); 74 | 75 | while (read < as.size()) { 76 | std::mbstate_t state{}; 77 | 78 | auto from = as.data() + read; 79 | auto from_end = as.data() + as.size(); 80 | auto from_next = from_end; 81 | 82 | auto to = ws.data() + write; 83 | auto to_end = ws.data() + ws.size(); 84 | auto to_next = to_end; 85 | 86 | const auto result = cvt.in( 87 | state, 88 | from, from_end, from_next, 89 | to, to_end, to_next); 90 | 91 | read += from_next - from; 92 | write += to_next - to; 93 | 94 | if (nullptr != lpread) break; 95 | if (XCODECVT::ok != result) { 96 | ++read; 97 | *(ws.data() + write) = L'?'; 98 | ++write; 99 | } 100 | } 101 | 102 | ws.resize(write); 103 | return ws; 104 | } 105 | 106 | /** 107 | UNICODE 串转换 ANSI 串。 108 | \param ws 需要转换的 UNICODE 串。 109 | \param lpread 返回转换 ANSI 字符数。指针默认为 nullptr 时,采用贪婪模式,转换失败的字符,以 ? 代替。 110 | \return 转换后的对应 ANSI 串。 111 | 112 | \code 113 | auto as = ws2as(L"文字"); 114 | \endcode 115 | */ 116 | inline std::string ws2as(const std::wstring& ws, size_t* const lpread = nullptr) { 117 | if (ws.empty()) return {}; 118 | 119 | size_t rd; 120 | size_t& read = (nullptr == lpread) ? rd : *lpread; 121 | read = 0; 122 | 123 | size_t write = 0; 124 | 125 | using XCODECVT = std::codecvt; 126 | std::locale locale(LOCALE_AS_WS); 127 | auto& cvt = std::use_facet(locale); 128 | 129 | std::string as(ws.size() * cvt.max_length(), '\0'); 130 | 131 | while (read < ws.size()) { 132 | std::mbstate_t state{}; 133 | 134 | auto from = ws.data() + read; 135 | auto from_end = ws.data() + ws.size(); 136 | auto from_next = from_end; 137 | 138 | auto to = as.data() + write; 139 | auto to_end = as.data() + as.size(); 140 | auto to_next = to_end; 141 | 142 | const auto result = cvt.out( 143 | state, 144 | from, from_end, from_next, 145 | to, to_end, to_next); 146 | 147 | read += from_next - from; 148 | write += to_next - to; 149 | 150 | if (nullptr != lpread) break; 151 | if (XCODECVT::ok != result) { 152 | ++read; 153 | *(as.data() + write) = '?'; 154 | ++write; 155 | } 156 | } 157 | 158 | as.resize(write); 159 | return as; 160 | } 161 | 162 | /** 163 | UTF8 串转换 UNICODE 串。 164 | \param u8 需要转换的 UTF8 串。 165 | \param lpread 返回转换 ANSI 字符数。指针默认为 nullptr 时,采用贪婪模式,转换失败的字符,以 ? 代替。 166 | \return 转换后的对应 UNICODE 串。 167 | 168 | \code 169 | auto ws(u82ws(u8"文字")); 170 | \endcode 171 | */ 172 | inline std::wstring u82ws(const std::u8string& u8, size_t* const lpread = nullptr) { 173 | if (u8.empty()) return {}; 174 | 175 | size_t rd; 176 | size_t& read = (nullptr == lpread) ? rd : *lpread; 177 | read = 0; 178 | 179 | size_t write = 0; 180 | 181 | #ifdef _WIN32 182 | using WCHART = char16_t; 183 | #else 184 | using WCHART = char32_t; 185 | #endif 186 | #ifndef __cpp_lib_char8_t 187 | using U8CHART = char; 188 | #else 189 | using U8CHART = char8_t; 190 | #endif 191 | #pragma warning(push) 192 | #pragma warning(disable:4996) 193 | using XCODECVT = std::codecvt; 194 | std::locale locale(LOCALE_WS_U8); 195 | auto& cvt = std::use_facet(locale); 196 | 197 | std::wstring ws(u8.size(), L'\0'); 198 | 199 | while (read < u8.size()) { 200 | std::mbstate_t state{}; 201 | 202 | auto from = (const U8CHART*)u8.data() + read; 203 | auto from_end = (const U8CHART*)u8.data() + u8.size(); 204 | auto from_next = from_end; 205 | 206 | auto to = (WCHART*)ws.data() + write; 207 | auto to_end = (WCHART*)ws.data() + ws.size(); 208 | auto to_next = to_end; 209 | 210 | const auto result = cvt.in( 211 | state, 212 | from, from_end, from_next, 213 | to, to_end, to_next); 214 | 215 | read += from_next - from; 216 | write += to_next - to; 217 | 218 | if (nullptr != lpread) break; 219 | if (XCODECVT::ok != result) { 220 | ++read; 221 | *(ws.data() + write) = L'?'; 222 | ++write; 223 | } 224 | } 225 | #pragma warning(pop) 226 | ws.resize(write); 227 | return ws; 228 | } 229 | 230 | /** 231 | UNICODE 串转换 UTF8 串。 232 | \param ws 需要转换的 UNICODE 串。 233 | \param lpread 返回转换 ANSI 字符数。指针默认为 nullptr 时,采用贪婪模式,转换失败的字符,以 ? 代替。 234 | \return 转换后的对应 UTF8 串。 235 | 236 | \code 237 | auto u8(ws2u8(L"文字")); 238 | \endcode 239 | */ 240 | inline std::u8string ws2u8(const std::wstring& ws, size_t* const lpread = nullptr) { 241 | if (ws.empty()) return {}; 242 | 243 | size_t rd; 244 | size_t& read = (nullptr == lpread) ? rd : *lpread; 245 | read = 0; 246 | 247 | size_t write = 0; 248 | 249 | #ifdef _WIN32 250 | using WCHART = char16_t; 251 | #else 252 | using WCHART = char32_t; 253 | #endif 254 | #ifndef __cpp_lib_char8_t 255 | using U8CHART = char; 256 | #else 257 | using U8CHART = char8_t; 258 | #endif 259 | #pragma warning(push) 260 | #pragma warning(disable:4996) 261 | using XCODECVT = std::codecvt; 262 | std::locale locale(LOCALE_WS_U8); 263 | auto& cvt = std::use_facet(locale); 264 | 265 | std::u8string u8(ws.size() * cvt.max_length(), char8_t('\0')); 266 | 267 | while (read < ws.size()) { 268 | std::mbstate_t state{}; 269 | 270 | auto from = (const WCHART*)ws.data() + read; 271 | auto from_end = (const WCHART*)ws.data() + ws.size(); 272 | auto from_next = from_end; 273 | 274 | auto to = (U8CHART*)u8.data() + write; 275 | auto to_end = (U8CHART*)u8.data() + u8.size(); 276 | auto to_next = to_end; 277 | 278 | const auto result = cvt.out( 279 | state, 280 | from, from_end, from_next, 281 | to, to_end, to_next); 282 | 283 | read += from_next - from; 284 | write += to_next - to; 285 | 286 | if (nullptr != lpread) break; 287 | if (XCODECVT::ok != result) { 288 | ++read; 289 | *(u8.data() + write) = char8_t(u8'?'); 290 | ++write; 291 | } 292 | } 293 | #pragma warning(pop) 294 | u8.resize(write); 295 | return u8; 296 | } 297 | 298 | /** 299 | ANSI 串转换 UTF8 串。 300 | \param as 需要转换的 ANSI 串。 301 | \return 转换后的对应 UTF8 串。 302 | 303 | \code 304 | auto u8(as2u8("文字")); 305 | \endcode 306 | */ 307 | inline std::u8string as2u8(const std::string& as, size_t* const lpread = nullptr) { 308 | for (const auto& c : as) { 309 | if (!is_easy_transcoding(c)) return ws2u8(as2ws(as, lpread)); 310 | } 311 | // 纯英文字符,无需转换。 312 | size_t rd; 313 | size_t& read = (nullptr == lpread) ? rd : *lpread; 314 | read = as.size(); 315 | return std::u8string((const char8_t*)as.data(), as.size()); 316 | } 317 | 318 | /** 319 | UTF8 串转换 ANSI 串。 320 | \param u8 需要转换的 UTF8 串。 321 | \return 转换后的对应 ANSI 串。 322 | 323 | \code 324 | auto as(u82as(u8"文字")); 325 | \endcode 326 | */ 327 | inline std::string u82as(const std::u8string& u8, size_t* const lpread = nullptr) { 328 | for (const auto& c : u8) { 329 | if (!is_easy_transcoding(c)) return ws2as(u82ws(u8, lpread)); 330 | } 331 | // 纯英文字符,无需转换。 332 | size_t rd; 333 | size_t& read = (nullptr == lpread) ? rd : *lpread; 334 | read = u8.size(); 335 | return std::string((const char*)u8.data(), u8.size()); 336 | } 337 | 338 | #undef LOCALE_AS_WS 339 | #undef LOCALE_WS_U8 340 | 341 | } // namespace xlib 342 | 343 | #endif // _XLIB_XCODECVT_STD_H_ -------------------------------------------------------------------------------- /xcodecvt_win.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file xcodecvt_win.h 3 | \brief 用于 ANSI 与 UNICODE 及 UTF8 等编码的本地化转换,使用 WindowsAPI 。 4 | 5 | \version 0.0.2.231129 6 | 7 | \author triones 8 | \date 2022-08-24 9 | 10 | \section history 版本记录 11 | 12 | - 2022-08-24 新建 。 0.0.1 。 13 | - 2022-11-29 修正 旧系统 编码转换失败的问题。 0.0.2 。 14 | */ 15 | #ifndef _XLIB_XCODECVT_WIN_H_ 16 | #define _XLIB_XCODECVT_WIN_H_ 17 | 18 | #include 19 | 20 | #define WIN32_LEAN_AND_MEAN 21 | #define NOMINMAX 22 | #include 23 | #undef NOMINMAX 24 | #undef WIN32_LEAN_AND_MEAN 25 | 26 | #ifndef _WIN32 27 | #error "xcodecvt_win.h only for windows !" 28 | #endif 29 | 30 | namespace xlib { 31 | 32 | /// 允许通过设置 LOCALE_AS_WS 宏,改变默认 ANSI 编码。 33 | #ifndef LOCALE_AS_WS 34 | #define LOCALE_AS_WS CP_ACP 35 | #endif 36 | 37 | /// 允许通过设置 LOCALE_WS_U8 宏,改变默认 UTF8 编码。 38 | #ifndef LOCALE_WS_U8 39 | #define LOCALE_WS_U8 CP_UTF8 40 | #endif 41 | 42 | /** 43 | ANSI 串转换 UNICODE 串。 44 | \param as 需要转换的 ANSI 串 45 | \param lpread 返回转换 ANSI 字符数。指针默认为 nullptr 时,采用贪婪模式,转换失败的字符,以 ? 代替。 46 | \return 转换后的对应 UNICODE 串。 47 | 48 | \code 49 | auto ws = as2ws("文字"); 50 | \endcode 51 | */ 52 | inline std::wstring as2ws(const std::string& as, size_t* const lpread = nullptr) { 53 | if (as.empty()) return {}; 54 | 55 | size_t rd; 56 | size_t& read = (nullptr == lpread) ? rd : *lpread; 57 | read = 0; 58 | 59 | std::wstring ws(as.size(), L'\0'); 60 | 61 | const DWORD dwFlags = (nullptr == lpread) ? 0 : MB_ERR_INVALID_CHARS; 62 | 63 | for (rd = as.size(); rd != 0; --rd) { 64 | #pragma warning(push) 65 | #pragma warning(disable : 4267) 66 | const auto write = MultiByteToWideChar(LOCALE_AS_WS, dwFlags, 67 | as.data(), rd, 68 | ws.data(), ws.size()); 69 | #pragma warning(pop) 70 | if (write > 0) { 71 | read = rd; 72 | ws.resize(write); 73 | return ws; 74 | } 75 | } 76 | read = 0; 77 | return {}; 78 | } 79 | 80 | /** 81 | UNICODE 串转换 ANSI 串。 82 | \param ws 需要转换的 UNICODE 串。 83 | \param lpread 返回转换 ANSI 字符数。指针默认为 nullptr 时,采用贪婪模式,转换失败的字符,以 ? 代替。 84 | \return 转换后的对应 ANSI 串。 85 | 86 | \code 87 | auto as = ws2as(L"文字"); 88 | \endcode 89 | */ 90 | inline std::string ws2as(const std::wstring& ws, size_t* const lpread = nullptr) { 91 | if (ws.empty()) return {}; 92 | 93 | size_t rd; 94 | size_t& read = (nullptr == lpread) ? rd : *lpread; 95 | read = 0; 96 | 97 | std::string as(ws.size() * sizeof(wchar_t), '\0'); 98 | 99 | const DWORD dwFlags = (nullptr == lpread) ? 0 : WC_NO_BEST_FIT_CHARS; 100 | 101 | for (rd = ws.size(); rd != 0; --rd) { 102 | #pragma warning(push) 103 | #pragma warning(disable : 4267) 104 | const auto write = WideCharToMultiByte(LOCALE_AS_WS, dwFlags, 105 | ws.data(), rd, 106 | as.data(), as.size(), 107 | nullptr, nullptr); 108 | #pragma warning(pop) 109 | if (write > 0) { 110 | read = rd; 111 | as.resize(write); 112 | return as; 113 | } 114 | } 115 | read = 0; 116 | return {}; 117 | } 118 | 119 | /** 120 | UTF8 串转换 UNICODE 串。 121 | \param u8 需要转换的 UTF8 串。 122 | \param lpread 返回转换 ANSI 字符数。指针默认为 nullptr 时,采用贪婪模式,转换失败的字符,以 ? 代替。 123 | \return 转换后的对应 UNICODE 串。 124 | 125 | \code 126 | auto ws(u82ws(u8"文字")); 127 | \endcode 128 | */ 129 | inline std::wstring u82ws(const std::u8string& u8, size_t* const lpread = nullptr) { 130 | if (u8.empty()) return {}; 131 | 132 | size_t rd; 133 | size_t& read = (nullptr == lpread) ? rd : *lpread; 134 | read = 0; 135 | 136 | std::wstring ws(u8.size(), L'\0'); 137 | 138 | const DWORD dwFlags = (nullptr == lpread) ? 0 : MB_ERR_INVALID_CHARS; 139 | 140 | for (rd = u8.size(); rd != 0; --rd) { 141 | #pragma warning(push) 142 | #pragma warning(disable : 4267) 143 | const auto write = MultiByteToWideChar(LOCALE_WS_U8, dwFlags, 144 | (const char*)u8.data(), rd, 145 | ws.data(), ws.size()); 146 | #pragma warning(pop) 147 | if (write > 0) { 148 | read = rd; 149 | ws.resize(write); 150 | return ws; 151 | } 152 | } 153 | read = 0; 154 | return {}; 155 | } 156 | 157 | /** 158 | UNICODE 串转换 UTF8 串。 159 | \param ws 需要转换的 UNICODE 串。 160 | \param lpread 返回转换 ANSI 字符数。指针默认为 nullptr 时,采用贪婪模式,转换失败的字符,以 ? 代替。 161 | \return 转换后的对应 UTF8 串。 162 | 163 | \code 164 | auto u8(ws2u8(L"文字")); 165 | \endcode 166 | */ 167 | inline std::u8string ws2u8(const std::wstring& ws, 168 | size_t* const lpread = nullptr) { 169 | if (ws.empty()) return {}; 170 | 171 | size_t rd; 172 | size_t& read = (nullptr == lpread) ? rd : *lpread; 173 | read = 0; 174 | 175 | std::u8string u8(ws.size() * 6, '\0'); 176 | 177 | const DWORD dwFlags = (nullptr == lpread) ? 0 : WC_NO_BEST_FIT_CHARS; 178 | 179 | for (rd = ws.size(); rd != 0; --rd) { 180 | #pragma warning(push) 181 | #pragma warning(disable : 4267) 182 | const auto write = WideCharToMultiByte(LOCALE_WS_U8, dwFlags, 183 | ws.data(), rd, 184 | (char*)u8.data(), u8.size(), 185 | nullptr, nullptr); 186 | #pragma warning(pop) 187 | if (write > 0) { 188 | read = rd; 189 | u8.resize(write); 190 | return u8; 191 | } 192 | } 193 | read = 0; 194 | return {}; 195 | } 196 | 197 | /** 198 | ANSI 串转换 UTF8 串。 199 | \param as 需要转换的 ANSI 串。 200 | \return 转换后的对应 UTF8 串。 201 | 202 | \code 203 | auto u8(as2u8("文字")); 204 | \endcode 205 | */ 206 | inline std::u8string as2u8(const std::string& as, size_t* const lpread = nullptr) { 207 | for (const auto& c : as) { 208 | if (!is_easy_transcoding(c)) return ws2u8(as2ws(as, lpread)); 209 | } 210 | // 纯英文字符,无需转换。 211 | size_t rd; 212 | size_t& read = (nullptr == lpread) ? rd : *lpread; 213 | read = as.size(); 214 | return std::u8string((const char8_t*)as.data(), as.size()); 215 | } 216 | 217 | /** 218 | UTF8 串转换 ANSI 串。 219 | \param u8 需要转换的 UTF8 串。 220 | \return 转换后的对应 ANSI 串。 221 | 222 | \code 223 | auto as(u82as(u8"文字")); 224 | \endcode 225 | */ 226 | inline std::string u82as(const std::u8string& u8, size_t* const lpread = nullptr) { 227 | for (const auto& c : u8) { 228 | if (!is_easy_transcoding(c)) return ws2as(u82ws(u8, lpread)); 229 | } 230 | // 纯英文字符,无需转换。 231 | size_t rd; 232 | size_t& read = (nullptr == lpread) ? rd : *lpread; 233 | read = u8.size(); 234 | return std::string((const char*)u8.data(), u8.size()); 235 | } 236 | 237 | #undef LOCALE_AS_WS 238 | #undef LOCALE_WS_U8 239 | 240 | } // namespace xlib 241 | 242 | #endif // _XLIB_XCODECVT_WIN_H_ -------------------------------------------------------------------------------- /xcrc.cc: -------------------------------------------------------------------------------- 1 | #include "xcrc.h" 2 | 3 | #include "xlib_test.h" 4 | 5 | SHOW_TEST_INIT(xcrc) 6 | 7 | SHOW_TEST_HEAD(crc16); 8 | done = 0xC57A == xlib::crc16("1234567890", 10); 9 | SHOW_TEST_RESULT; 10 | 11 | SHOW_TEST_HEAD(crc32); 12 | done = 0x261DAEE5 == xlib::crc32(std::string("1234567890")); 13 | SHOW_TEST_RESULT; 14 | 15 | SHOW_TEST_HEAD(crc64); 16 | done = 0xB1CB31BBB4A2B2BE == xlib::crc64("1234567890"); 17 | SHOW_TEST_RESULT; 18 | 19 | SHOW_TEST_HEAD(crcccitt); 20 | done = 0x4D53 == xlib::crcccitt(std::array{'1', '2'}); 21 | SHOW_TEST_RESULT; 22 | 23 | SHOW_TEST_HEAD(overload); 24 | done = 25 | 0x2A2F0E859495CAED == xlib::crc64(( void*)"1", 1) && 26 | 0x2A2F0E859495CAED == xlib::crc64((const void*)"1", 1) && 27 | 0x2A2F0E859495CAED == xlib::crc64(( char*)"1", 1) && 28 | 0x2A2F0E859495CAED == xlib::crc64((const char*)"1", 1) && 29 | 0x2A2F0E859495CAED == xlib::crc64(std::string("1")) && 30 | 0x2A2F0E859495CAED == xlib::crc64("1"); 31 | SHOW_TEST_RESULT; 32 | 33 | SHOW_TEST_HEAD(constexpr); 34 | constexpr auto c0 = xlib::crc32("1234567890"); 35 | constexpr auto c1 = xlib::crc64(L"1"); 36 | constexpr auto c2 = xlib::crc16(u8"1234567890"); 37 | done = (c0 == 0x261DAEE5) && 38 | #ifdef _WIN32 39 | (c1 == 0x7635B8617CE753D8) && 40 | #else 41 | (c1 == 0x3D1B1331AA7B3B58) && 42 | #endif 43 | (c2 == 0xC57A); 44 | SHOW_TEST_RESULT; 45 | 46 | SHOW_TEST_DONE; -------------------------------------------------------------------------------- /xcrc.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file xcrc.h 3 | \brief 定义了 CRC 算法模板。支持 crc16 、 crc32 、 crc64 、crcccitt 。 4 | 5 | \version 3.2.1.230307 6 | 7 | \author triones 8 | \date 2013-03-19 9 | 10 | \section history 版本记录 11 | 12 | - 2013-03-19 新建 crc32 函数。 0.1 。 13 | - 2013-03-20 修正溢出 Bug 及优化。 0.2 。 14 | - 2013-11-30 新增 crc64 函数。 0.3 。 15 | - 2014-02-18 新增 crc16 函数。 0.4 。 16 | - 2016-12-16 适配 Linux g++ 。新增 crcccitt 函数。 1.0 。 17 | - 2017-07-24 改进函数定义。 1.1 。 18 | - 2019-09-19 修改为模板,使用了简单的单例。 2.0 。 19 | - 2019-09-29 引入新特性重构,解决线程安全的问题。 3.0 。 20 | - 2020-03-06 引入可变参数模板,表的生成重新设计。 3.1 。 21 | - 2020-05-09 扩大模板匹配,匹配多数顺序容器。优化接口 3.2 。 22 | */ 23 | #ifndef _XLIB_XCRC_H_ 24 | #define _XLIB_XCRC_H_ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | namespace xlib { 32 | 33 | /// 用于编译期计算 CRC 表单个值。 34 | template 35 | constexpr T inline XCrcTableValue(const T i) noexcept { 36 | T crc = i; 37 | for (size_t j = 0; j < CHAR_BIT; ++j) { 38 | crc = (crc >> 1) ^ ((crc & 1) ? N : 0); 39 | } 40 | return crc; 41 | } 42 | 43 | /// 用于编译期生成 CRC 表。 44 | template 45 | constexpr auto inline XCrcTable(std::index_sequence) noexcept { 46 | return std::array{XCrcTableValue(I)...}; 47 | } 48 | 49 | /// CRC 计算模板。 50 | template 51 | T XCRC(const void* const data, const size_t size) { 52 | // 将在编译期生成 CRC 表。 53 | constexpr auto CrcTable = XCrcTable(std::make_index_sequence<0x100>{}); 54 | T ret = V; 55 | const size_t len = (nullptr == data) ? 0 : size; 56 | const uint8_t* const p = (const uint8_t*)data; 57 | for (size_t i = 0; i < len; ++i) { 58 | ret = CrcTable[(ret & 0xFF) ^ p[i]] ^ (ret >> 8); 59 | } 60 | return R ? ~ret : ret; 61 | } 62 | 63 | /** 64 | CRC 计算模板。用于 编译期计算。 65 | 66 | 注意到,const T* const 模板无法生成 constexpr 结果。 67 | */ 68 | template constexpr 69 | T XCRC(TC const(&data)[size]) { 70 | // 将在编译期生成 CRC 表。 71 | constexpr auto CrcTable = XCrcTable(std::make_index_sequence<0x100>{}); 72 | constexpr auto st = sizeof(TC); 73 | T ret = V; 74 | const size_t len = (size - 1) * st; 75 | for (size_t i = 0; i < len; ++i) { 76 | const auto ch = data[i / st] >> ((i % st) * CHAR_BIT); 77 | ret = CrcTable[(ret & 0xFF) ^ (ch & 0xFF)] ^ (ret >> 8); 78 | } 79 | return R ? ~ret : ret; 80 | } 81 | 82 | ////////////////////////////////////////////////////////////////////////// 83 | /** 84 | 生成指定数据的 crc 。 85 | \param data 指定需要计算 crc 的数据。 86 | \param size 指定需要计算 crc 的数据长度(以相应类型字计)。 87 | \return 返回 crc 值。 88 | 89 | \code 90 | // 接受指定长度数据。 91 | auto x = crc((void*)"12", 2); 92 | auto x = crc(L"12", 2); 93 | // 接受顺序容器。 94 | auto x = crc(std::string("12")); 95 | auto x = crc(std::array{'1'}); 96 | // 接受字符串字面量。 97 | auto x = crc("12"); 98 | auto x = crc(L"12"); 99 | \endcode 100 | */ 101 | 102 | #define CRCX(FUNC, TT, NN, VV, RR) \ 103 | inline auto FUNC(const void* const data, const size_t size) { \ 104 | return XCRC(data, size); \ 105 | } \ 106 | template \ 107 | inline auto FUNC(const T* const data, const size_t size) { \ 108 | return FUNC((const void*)data, size * sizeof(T)); \ 109 | } \ 110 | template \ 111 | inline auto FUNC(const T& o) \ 112 | ->std::enable_if_t, TT> { \ 113 | return FUNC(o.data(), o.size()); \ 114 | } \ 115 | template constexpr \ 116 | inline auto FUNC(T const(&data)[size]) { \ 117 | return XCRC(data); \ 118 | } 119 | 120 | CRCX(crc16, uint16_t, 0xA001, 0, false); 121 | CRCX(crc32, uint32_t, 0xEDB88320, 0xFFFFFFFF, true); 122 | CRCX(crc64, uint64_t, 0xC96C5795D7870F42, 0xFFFFFFFFFFFFFFFF, true); 123 | CRCX(crcccitt, uint16_t, 0x8408, 0xFFFF, false); 124 | 125 | #undef CRCX 126 | 127 | } // namespace xlib 128 | 129 | #endif // _XLIB_XCRC_H_ -------------------------------------------------------------------------------- /xhexbin.cc: -------------------------------------------------------------------------------- 1 | #include "xhexbin.h" 2 | 3 | #include "xlib_test.h" 4 | 5 | SHOW_TEST_INIT(xhexbin) 6 | 7 | SHOW_TEST_HEAD(bin2hex as); 8 | done = "3132333435363738" == xlib::bin2hex(std::string("12345678")); 9 | SHOW_TEST_RESULT; 10 | 11 | SHOW_TEST_HEAD(bin2hex ws); 12 | #ifdef _WIN32 13 | done = "3100320033003400" == xlib::bin2hex(std::wstring(L"1234")); 14 | #else 15 | done = "3100000032000000" == xlib::bin2hex(std::wstring(L"12")); 16 | #endif 17 | SHOW_TEST_RESULT; 18 | 19 | SHOW_TEST_HEAD(hex2value as); 20 | done = 0x12345678 == xlib::hex2value(std::string("12345678")); 21 | SHOW_TEST_RESULT; 22 | 23 | SHOW_TEST_HEAD(hex2value ws); 24 | done = 0x12345678 == xlib::hex2value(std::wstring(L"123 456MM78")); 25 | SHOW_TEST_RESULT; 26 | 27 | SHOW_TEST_HEAD(hex2bin as); 28 | done = "\x12\x34\x56\x78" == xlib::hex2bin(std::string("12345678")); 29 | SHOW_TEST_RESULT; 30 | 31 | SHOW_TEST_HEAD(hex2bin ws); 32 | done = "\x12\x34\x56\x78" == xlib::hex2bin(std::wstring(L"12345678")); 33 | SHOW_TEST_RESULT; 34 | 35 | SHOW_TEST_HEAD(escape as); 36 | done = "12\'\"\?\\\a\b\f\n\r\t\v\041\41K\x41-NDCBA!0A4xK" == xlib::escape(std::string( 37 | R"(12\'\"\?\\\a\b\f\n\r\t\v\041\41K\x41\u4E2D\U41424344\410\x414\xK)")); 38 | SHOW_TEST_RESULT; 39 | 40 | SHOW_TEST_HEAD(escape ws); 41 | #ifdef _WIN32 42 | done = L"12\'\"\?\\\a\b\f\n\r\t\v!!0!KAA0AKxK文中文" == xlib::escape(std::wstring( 43 | LR"(12\'\"\?\\\a\b\f\n\r\t\v\041\410\41K\x0041\x00410\x0041K\xK\u6587\U65874E2D)")); 44 | #else 45 | done = L"12\'\"\?\\\a\b\f\n\r\t\v!!0!KAA0AKxK文" == xlib::escape(std::wstring( 46 | LR"(12\'\"\?\\\a\b\f\n\r\t\v\041\410\41K\x00000041\x000000410\x00000041K\xK\u6587)")); 47 | #endif 48 | SHOW_TEST_RESULT; 49 | 50 | SHOW_TEST_HEAD(showbin as); 51 | const std::string as("HIJKLMNOPQRSTUV\xD7\xAA\xBB\xBB\xB2\xE2\xCA\xD4\x42\x42\0CC", 0x1C); 52 | const auto lpas0 = xlib::bswap((size_t)as.data()); 53 | const auto lpas1 = xlib::bswap((size_t)as.data() + 0x10); 54 | const auto asshowbin = 55 | xlib::xmsg() 56 | << xlib::bin2hex(&lpas0, 1) 57 | << L" |48 49 4A 4B|4C 4D 4E 4F|50 51 52 53|54 55 56 D7| HIJKLMNOPQRSTUV转\r\n" 58 | << xlib::bin2hex(&lpas1, 1) 59 | << L" |AA BB BB B2|E2 CA D4 42|42 00 43 43| | 换测试BB.CC\r\n"; 60 | done = asshowbin == xlib::showbin(as); 61 | SHOW_TEST_RESULT; 62 | 63 | SHOW_TEST_HEAD(showbin u8); 64 | const std::u8string us((const char8_t*)u8"HIJKLMNOPQRSTUV转换测试BB\0CC", 0x20); 65 | const auto lpus0 = xlib::bswap((size_t)us.data()); 66 | const auto lpus1 = xlib::bswap((size_t)us.data() + 0x10); 67 | const auto usshowbin = 68 | xlib::xmsg() 69 | << xlib::bin2hex(&lpus0, 1) 70 | << L" |48 49 4A 4B|4C 4D 4E 4F|50 51 52 53|54 55 56 E8| HIJKLMNOPQRSTUV转\r\n" 71 | << xlib::bin2hex(&lpus1, 1) 72 | << L" |BD AC E6 8D|A2 E6 B5 8B|E8 AF 95 42|42 00 43 43| 换测试BB.CC\r\n"; 73 | done = usshowbin == xlib::showbin(us); 74 | SHOW_TEST_RESULT; 75 | 76 | SHOW_TEST_HEAD(showbin ws); 77 | #ifdef _WIN32 78 | const std::wstring ws0(L"1234567"); 79 | const std::wstring ws1(L"拿转换测试"); 80 | const std::string was = std::string((const char*)ws0.data(), ws0.size() * sizeof(wchar_t)) + 81 | std::string("\xFF", 1) + std::string((const char*)ws1.data(), ws1.size() * sizeof(wchar_t)) + 82 | std::string("\0", 1); 83 | const std::wstring ws((const wchar_t*)was.data(), was.size() / sizeof(wchar_t)); 84 | const auto lpws0 = xlib::bswap((size_t)ws.data()); 85 | const auto lpws1 = xlib::bswap((size_t)ws.data() + 0x10); 86 | const auto wsshowbin = 87 | xlib::xmsg() 88 | << xlib::bin2hex(&lpws0, 1) 89 | << L" |31 00 32 00|33 00 34 00|35 00 36 00|37 00 FF FF| 1234567.拿\r\n" 90 | << xlib::bin2hex(&lpws1, 1) 91 | << L" |62 6C 8F 62|63 4B 6D D5|8B 00 | | 转换测试.\r\n"; 92 | #else 93 | const std::wstring ws(L"123转换测试\0", 8); 94 | const auto lpws0 = xlib::bswap((size_t)ws.data()); 95 | const auto lpws1 = xlib::bswap((size_t)ws.data() + 0x10); 96 | const auto wsshowbin = 97 | xlib::xmsg() 98 | << xlib::bin2hex(&lpws0, 1) 99 | << L" |31 00 00 00|32 00 00 00|33 00 00 00|6C 8F 00 00| 123转\r\n" 100 | << xlib::bin2hex(&lpws1, 1) 101 | << L" |62 63 00 00|4B 6D 00 00|D5 8B 00 00|00 00 00 00| 换测试.\r\n"; 102 | #endif 103 | done = wsshowbin == xlib::showbin(ws); 104 | SHOW_TEST_RESULT; 105 | 106 | SHOW_TEST_DONE; -------------------------------------------------------------------------------- /xhexbin.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file xhex_bin.h 3 | \brief 定义了 hex 与 bin 的转换操作。 4 | 5 | \version 3.2.0.230227 6 | \note For All 7 | 8 | \author triones 9 | \date 2010-03-03 10 | 11 | \section history 版本记录 12 | 13 | - 2010-12-09 添加结构体 hex_character ,并对 hex2str 与 str2hex 函数进行相应改造,去除强制移位计算。 14 | - 2010-12-29 hex&str 独立剥离,新建文件。 0.1 。 15 | - 2011-04-20 hex2str 新加默认参数,提升 str2hex 功能。 0.2 。 16 | - 2011-06-02 新加重载的 hex2str 、 str2hex 函数,引入 line 类的返回。 0.3 。 17 | - 2011-08-29 新加一组读取各种进制 ASCII 字符的转换函数,新加单个 HEX 值转换 ASCII 函数。 0.4 。 18 | - 2011-08-29 引入 hexvalue 模板。 0.5 。 19 | - 2011-12-20 撤销之前添加的一组转换函数,同时撤销 hexvalue 模板。另设计一个函数以替换之。 0.6 。 20 | - 2012-02-27 发现 str2hex 的一个 BUG ,已修正。 0.6.1 。 21 | - 2012-06-07 删除 Hex2Ascii 。 0.7 。 22 | - 2013-03-05 修改返回 xstr 为返回 xmsg 。 0.8 。 23 | - 2013-03-20 新增 str2hexs 函数。 0.9 。 24 | - 2013-10-24 改进 hex2show 对 ASCII 中文输出的优化。 0.10 。 25 | - 2013-11-13 改进 hex2show 可以输出 UTF8 。 0.11 。 26 | - 2013-12-10 改进 str2hex 以适应 x64 。 0.12 。 27 | - 2014-01-13 引入 SGISTL ,做适应性修改。 1.0 。 28 | - 2014-02-19 修正 str2hex 的一个小 BUG 。 1.0.1 。 29 | - 2014-04-09 修正 str2hex 的一个小 BUG 。 1.0.2 。 30 | - 2014-04-18 改进 hex2show 的字符集显示算法。 1.1 。 31 | - 2014-05-08 细节改进。 1.1.1 。 32 | - 2014-07-24 新加 hex2show 模版。 1.2 。 33 | - 2014-08-13 修正 str2hexs 的一个严重 BUG 。 1.3 。 34 | - 2016-11-15 适配 Linux g++ 。 1.4 。 35 | - 2017-01-03 简化接口。 1.5 。 36 | - 2017-07-14 修改名称以符合通用称呼。 1.6 。 37 | - 2018-02-07 改进 showbin 的内核调用方式,使得参数进出明确。 1.7 。 38 | - 2018-02-13 修正 hex2bin 的一个返回错误。 1.7.1 。 39 | - 2019-06-20 放弃 escape 模板。 1.7.2 。 40 | - 2019-10-14 重构。 2.0 。 41 | - 2019-11-06 改进定义。 2.1 。 42 | - 2020-03-13 改进 showbin 返回 wstring 。 3.0 。 43 | - 2020-11-12 改进 showbin 返回 xmsg 。 3.1 。 44 | - 2021-06-20 改进 showbin 的前缀类型。 3.2 。 45 | */ 46 | #ifndef _XLIB_XHEXBIN_H_ 47 | #define _XLIB_XHEXBIN_H_ 48 | 49 | #include 50 | #include 51 | 52 | #include "xmsg.h" 53 | #include "xswap.h" 54 | 55 | namespace xlib { 56 | 57 | /** 58 | 十六进制值的结构体。主要体现 BYTE 与十六进制字符间的关系。 59 | 60 | \code 61 | BIN_VALUE_STRUCT bins = {1, 4}; // bins 为字符 'A' 。 62 | \endcode 63 | */ 64 | #pragma pack(push, 1) 65 | struct BIN_VALUE_STRUCT { 66 | uint8_t low : 4; 67 | uint8_t high : 4; 68 | }; 69 | #pragma pack(pop) 70 | 71 | /// 十六进制 ASCII 表现形式的结构体。 72 | struct HEX_VALUE_STRUCT { 73 | int8_t high; 74 | int8_t low; 75 | }; 76 | 77 | /** 78 | 指定 BIN 串转换为 HEX 格式。 79 | \param bin 源 BIN 串。 80 | \param size 源 BIN 串大小(以类型字大小计)。 81 | \param isup 指定转换后的 HEX 大小写,默认小写。 82 | \return 返回转换后的 HEX 串对象。 83 | 84 | \code 85 | std::cout << "bin2hex:" << bin2hex(string("\xAB\xCD\xEF")); // 将输出 "abcdef" 。 86 | \endcode 87 | */ 88 | inline std::string bin2hex(const void* const bin, 89 | const size_t size, 90 | const bool isup = false) { 91 | std::string hex(size * 2, '\0'); 92 | auto s = (const BIN_VALUE_STRUCT*)bin; 93 | const auto e = s + size; 94 | const auto fmt = isup ? "0123456789ABCDEF" : "0123456789abcdef"; 95 | for (auto p = hex.data(); s < e; ++s) { 96 | *p = fmt[s->high]; ++p; 97 | *p = fmt[s->low]; ++p; 98 | } 99 | return hex; 100 | } 101 | template 102 | std::string bin2hex(const T* const bin, 103 | const size_t size, 104 | const bool isup = false) { 105 | return bin2hex((const void*)bin, size * sizeof(T), isup); 106 | } 107 | template 108 | auto bin2hex(const T& bin, const bool isup = false) 109 | -> std::enable_if_t, std::string> { 110 | return bin2hex(bin.data(), bin.size(), isup); 111 | } 112 | 113 | /** 114 | 指定 HEX 串转换为值。 115 | \param hex 源 HEX 串。 116 | \param size 源串大小(以类型字大小计)。 117 | \param lpreadlen 成功读取 HEX 串时,返回读取的字符个数。\n 118 | lpreadlen 可以为 nullptr ,此时不返回结果。\n 119 | 转换失败, *lpreadlen == 0 。 120 | \param wantlen 需要处理的 \b 有效数据 大小 ( 0 < wantlen <= sizeof(R) * 2 )。\n 超出范围,一律视 == sizeof(R) * 2 。 121 | \param errexit 指定转换未结束前遭遇非 HEX 字符,是否视作失败处理。\n 122 | 默认不作失败处理。当此标志为 true 时,忽略 errbreak 。 123 | \param errbreak 指定转换未结束前遭遇非 HEX 字符,是否中止处理。默认跳过。 124 | \return 成功转换则为 HEX 串对应的 数值。\n 125 | 判定转换失败,应该通过 lpreadlen 的返回结果判定。 126 | 127 | \code 128 | size_t readlen = 0; 129 | const auto value = hex2value("12345678", readlen); 130 | // 返回 value == 0x12345678; readlen == 8; 131 | const auto value = hex2value("1234 56|78", readlen); 132 | // 返回 value == 0x12345678; readlen == 10; 133 | const auto value = hex2value("1234 56|78", readlen, 8, true); 134 | // 返回 value == 0; readlen == 0; 135 | const auto value = hex2value("1234 56|78", readlen, 8, false, ture); 136 | // 返回 value == 0x1234; readlen == 4; 137 | \endcode 138 | */ 139 | template 140 | std::enable_if_t, R> hex2value( 141 | const T* const hex, 142 | const size_t size, 143 | size_t* lpreadlen = nullptr, 144 | size_t wantlen = 0, 145 | const bool errexit = false, 146 | const bool errbreak = false) { 147 | R values = 0; 148 | size_t readlen = 0; 149 | 150 | if (lpreadlen == nullptr) lpreadlen = &readlen; 151 | *lpreadlen = 0; 152 | 153 | const auto wl = sizeof(R) * sizeof(HEX_VALUE_STRUCT); 154 | 155 | if ((wantlen == 0) || (wantlen > wl)) wantlen = wl; 156 | 157 | for (size_t i = 0; i < size; ++i) { 158 | const auto& ch = hex[i]; 159 | uint8_t tmpC = ch & 0xF; 160 | switch (ch) { 161 | case 'A': case 'B': case 'C': case 'D': 162 | case 'E': case 'F': 163 | case 'a': case 'b': case 'c': case 'd': 164 | case 'e': case 'f': 165 | tmpC += 9; // 注意这里没有 break 。 166 | case '0': case '1': case '2': case '3': 167 | case '4': case '5': case '6': case '7': 168 | case '8': case '9': 169 | --wantlen; 170 | values = values << 0x4; 171 | values += tmpC; 172 | if (0 == wantlen) { 173 | ++(*lpreadlen); 174 | return values; 175 | } 176 | break; 177 | default: 178 | if (errexit) { 179 | *lpreadlen = 0; 180 | return 0; 181 | } 182 | if (errbreak) return values; 183 | break; 184 | } 185 | ++(*lpreadlen); 186 | } 187 | return values; 188 | } 189 | 190 | template 191 | std::enable_if_t, R> hex2value( 192 | const void* const hex, 193 | const size_t size, 194 | size_t* lpreadlen = nullptr, 195 | size_t wantlen = 0, 196 | const bool errexit = false, 197 | const bool errbreak = false) { 198 | return hex2value((const char*)hex, size, lpreadlen, wantlen, errexit, errbreak); 199 | } 200 | template 201 | auto hex2value( 202 | const T& hex, 203 | size_t* lpreadlen = nullptr, 204 | size_t wantlen = 0, 205 | const bool errexit = false, 206 | const bool errbreak = false) 207 | -> std::enable_if_t && std::is_pointer_v, R> { 208 | return hex2value(hex.data(), hex.size(), lpreadlen, wantlen, errexit, errbreak); 209 | } 210 | 211 | /** 212 | 指定 HEX 串转换为 BIN 串。 213 | 214 | HEX 字符应成对,如最后字符不成对,将忽略最后不成对的字符。 215 | \param hex 源 HEX 串。 216 | \param size 源串大小(以字类型大小计)。 217 | \param lpreadlen 成功读取 HEX 串时,返回读取的字符个数。\n 218 | lpreadlen 可以为 nullptr ,此时不返回结果。\n 219 | 转换失败, *lpreadlen == 0。 220 | \param errexit 指定转换未结束前遭遇非 HEX 字符,是否视作失败处理。\n 221 | 默认不作失败处理。当此标志为 true 时,忽略 errbreak 。 222 | \param errbreak 指定转换未结束前遭遇非 HEX 字符,是否中止处理。默认跳过。 223 | \return 成功转换则为 HEX 串对应的 BIN 数据。\n 224 | 判定转换失败,应该通过 lpreadlen 的返回结果判定。 225 | */ 226 | template 227 | std::string hex2bin( 228 | const T* const hex, 229 | const size_t size, 230 | size_t* lpreadlen = nullptr, 231 | const bool errexit = false, 232 | const bool errbreak = false) { 233 | std::string rets; 234 | 235 | size_t readlen = 0; 236 | if (lpreadlen == nullptr) lpreadlen = &readlen; 237 | *lpreadlen = 0; 238 | 239 | bool pick_high = true; // 指示当前提取的是高位还是低位。 240 | uint8_t readch = 0; // 存放临时的提取值。 241 | size_t realreadlen = 0; // 存放实际读取数。 242 | 243 | for (size_t i = 0; i < size; ++i) { 244 | const auto& ch = hex[i]; 245 | uint8_t tmpC = ch & 0xF; 246 | switch (ch) { 247 | case 'A': case 'B': case 'C': case 'D': 248 | case 'E': case 'F': 249 | case 'a': case 'b': case 'c': case 'd': 250 | case 'e': case 'f': 251 | tmpC += 9; // 注意这里没有 break 。 252 | case '0': case '1': case '2': case '3': 253 | case '4': case '5': case '6': case '7': 254 | case '8': case '9': 255 | if (pick_high) { 256 | readch = tmpC << 0x4; 257 | } else { 258 | readch = (readch & 0xF0) + tmpC; 259 | rets.push_back(readch); 260 | realreadlen = *(lpreadlen) + 1; 261 | } 262 | pick_high = !pick_high; 263 | break; 264 | default: 265 | if (errexit) { 266 | *lpreadlen = 0; 267 | rets.clear(); 268 | return rets; 269 | } 270 | if (errbreak) { 271 | // 读取不完整。 272 | if (!pick_high) { 273 | *(lpreadlen) = realreadlen; 274 | } 275 | return rets; 276 | } 277 | break; 278 | } 279 | ++(*lpreadlen); 280 | } 281 | 282 | return rets; 283 | } 284 | inline std::string hex2bin( 285 | const void* const hex, 286 | const size_t size, 287 | size_t* lpreadlen = nullptr, 288 | const bool errexit = false, 289 | const bool errbreak = false) { 290 | return hex2bin((const char*)hex, size, lpreadlen, errexit, errbreak); 291 | } 292 | template 293 | auto hex2bin( 294 | const T& hex, 295 | size_t* lpreadlen = nullptr, 296 | const bool errexit = false, 297 | const bool errbreak = false) 298 | -> std::enable_if_t, std::string> { 299 | return hex2bin(hex.data(), hex.size(), lpreadlen, errexit, errbreak); 300 | } 301 | 302 | /** 303 | 指定分析转义字符。 304 | 305 | 可转义的字符及相关说明参考 。 306 | 307 | 注意: \u \U 不强制长度,不进行对应编码转换。 308 | 309 | \param str 源字符串。 310 | \param size 源字符串大小(以字类型大小计)。 311 | \return 返回转换后的字符串。 312 | */ 313 | template 314 | std::basic_string escape(const T* const str, const size_t size) { 315 | const T* s = str; 316 | const T* const e = s + size; 317 | std::basic_string ret; 318 | for (; s < e; ++s) { 319 | if (*s != '\\') { 320 | ret.push_back(*s); 321 | continue; 322 | } 323 | ++s; 324 | if (s >= e) { 325 | ret.push_back('\\'); 326 | break; 327 | } 328 | switch (*s) { 329 | case '\'': ret.push_back('\''); break; 330 | case '\"': ret.push_back('\"'); break; 331 | case '\?': ret.push_back('\?'); break; 332 | case '\\': ret.push_back('\\'); break; 333 | case 'a': ret.push_back('\a'); break; 334 | case 'b': ret.push_back('\b'); break; 335 | case 'f': ret.push_back('\f'); break; 336 | case 'n': ret.push_back('\n'); break; 337 | case 'r': ret.push_back('\r'); break; 338 | case 't': ret.push_back('\t'); break; 339 | case 'v': ret.push_back('\v'); break; 340 | case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': { 341 | uint32_t tmpC = 0; 342 | for (auto i = 0; i < 3; ++i) { 343 | if ((s >= e) || (*s < '0') || (*s > '7')) break; 344 | const auto ch = tmpC * 8 + (*s & 0x7); 345 | if (0x100 <= ch) break; 346 | tmpC = ch; 347 | ++s; 348 | } 349 | ret.push_back((uint8_t)tmpC); 350 | --s; 351 | } break; 352 | case 'x': { 353 | size_t readed = 0; 354 | ++s; 355 | const auto v = hex2value(s, e - s, &readed, 0, true, true); 356 | ret.push_back((0 == readed) ? 'x' : v); 357 | s += readed; 358 | --s; 359 | } break; 360 | case 'u': { 361 | size_t readed = 0; 362 | ++s; 363 | const auto v = hex2value(s, e - s, &readed, 0, true, true); 364 | if (0 == readed) { 365 | ret.push_back('u'); 366 | } else { 367 | constexpr size_t x = sizeof(v) / sizeof(T); 368 | if constexpr (0 == x) { 369 | ret.push_back((T)v); 370 | } else { 371 | for (size_t i = 0; i < x; ++i) { 372 | ret.push_back(((const T*)&v)[i]); 373 | } 374 | } 375 | } 376 | s += readed; 377 | --s; 378 | } break; 379 | case 'U': { 380 | size_t readed = 0; 381 | ++s; 382 | const auto v = hex2value(s, e - s, &readed, 0, true, true); 383 | if (0 == readed) { 384 | ret.push_back('U'); 385 | } else { 386 | constexpr size_t x = sizeof(v) / sizeof(T); 387 | if constexpr (0 == x) { 388 | ret.push_back((T)v); 389 | } else { 390 | for (size_t i = 0; i < x; ++i) { 391 | ret.push_back(((const T*)&v)[i]); 392 | } 393 | } 394 | } 395 | s += readed; 396 | --s; 397 | } break; 398 | default: 399 | ret.push_back('\\'); 400 | --s; 401 | break; 402 | } 403 | } 404 | return ret; 405 | } 406 | inline std::string escape(const void* const str, const size_t size) { 407 | return escape((std::string::const_pointer)str, size); 408 | } 409 | template 410 | std::basic_string escape(const std::basic_string& str) { 411 | return escape(str.data(), str.size()); 412 | } 413 | 414 | //////////////////////////////////////////////////////////////// showbin 415 | enum ShowBinCode { SBC_ANSI, SBC_UNICODE, SBC_UTF8 }; 416 | 417 | constexpr bool LocaleCheck() { 418 | #if defined(_WIN32) && !defined(__cpp_char8_t) 419 | return true; 420 | #else 421 | return (uint8_t) * u8"文" != (uint8_t) * "文"; 422 | #endif 423 | } 424 | 425 | template 426 | constexpr ShowBinCode inline CheckBinCode() { 427 | if constexpr (std::is_same_v) { 428 | return SBC_UTF8; 429 | } 430 | if constexpr (std::is_same_v) { 431 | return SBC_UNICODE; 432 | } 433 | if constexpr (std::is_same_v) { 434 | return SBC_ANSI; 435 | } 436 | if constexpr (std::is_class_v) { 437 | return CheckBinCode(); 438 | } else { 439 | return LocaleCheck() ? SBC_ANSI : SBC_UTF8; 440 | } 441 | } 442 | 443 | #define __SBTS(text) (const char8_t*)u8 ## text 444 | 445 | /** 446 | 指定 BIN 串,格式化显示。 447 | \param bin BIN 串。 448 | \param len BIN 串长度。 449 | \param code 指明内容编码(默认编码自动选择 ANSI 或 UTF8 )。 450 | \param prews 前缀。 451 | \param offset 地址模式。采用真实地址 == false / 从 0 开始计算 == true 。 452 | \param isup HEX 格式大小写控制。 453 | \return 格式化后的内容。 454 | 455 | \code 456 | std::string ss = "123456789aasdfsdhcf"; 457 | std::wcout << showbin(ss); 458 | // 0012FF54┃31 32 33 34|35 36 37 38|39 61 61 73|64 66 73 64┃123456789aasdfsd 459 | // 0012FF64┃68 63 66 00| | | ┃hcf. 460 | \endcode 461 | */ 462 | template 463 | xmsg showbin( 464 | const T* const bin, 465 | const size_t len, 466 | const ShowBinCode code = CheckBinCode(), 467 | const xmsg& prews = xmsg(), 468 | const bool offset = false, 469 | const bool isup = true) { 470 | xmsg ret; 471 | const auto fmt = isup ? __SBTS("0123456789ABCDEF") : __SBTS("0123456789abcdef"); 472 | 473 | size_t used = 0; 474 | const uint8_t* data = (const uint8_t*)bin; 475 | size_t size = len * sizeof(T); 476 | 477 | // 每行显示数据个数。 478 | constexpr size_t k_max_line_byte = 0x10; 479 | 480 | // 输出 前缀格式化数据。 481 | auto prefix = [&] { 482 | ret.append(prews); // 前缀。 483 | const auto p = bswap(offset ? (data - (const uint8_t*)bin) : (size_t)data); 484 | ret << bin2hex(&p, 1); // 地址前缀。 485 | ret.append(__SBTS(" |")); 486 | 487 | for (size_t i = 0; i < k_max_line_byte; ++i) { 488 | if (i < size) { // HEX 格式化输出。 489 | ret.push_back(fmt[(data[i] >> 4) & 0xF]); 490 | ret.push_back(fmt[(data[i] >> 0) & 0xF]); 491 | } else { // 无数据补齐。 492 | ret.append(__SBTS(" ")); 493 | } 494 | switch (i) { 495 | case 3: case 7: case 11: 496 | ret.push_back('|'); break; 497 | case 15: 498 | ret.append(__SBTS("| ")); break; 499 | default: 500 | ret.push_back(' '); 501 | } 502 | } 503 | }; 504 | 505 | // 使 UNICODE 字符输出可视化,返回 true 表示接受字符,否则不接受,一律输出 '.' 。 506 | auto check_unicode_visualization = [](const wchar_t wc) -> std::wstring { 507 | // 控制字符一律输出 '.' 。 508 | if (wc < L' ' || (wc >= 0x7F && wc <= 0xA0)) { 509 | return std::wstring(1, L'.'); 510 | } 511 | 512 | const std::wstring ch(1, wc); 513 | if (wc == L'?') return ch; 514 | 515 | // 尝试转换 可视化。 516 | size_t read; 517 | if constexpr (LocaleCheck()) { 518 | const auto s = ws2as(ch, &read); 519 | if (s.empty()) return {}; 520 | if (1 == s.size() && '?' == *s.begin()) return {}; 521 | return ch; 522 | } else { 523 | const auto s = ws2u8(ch, &read); 524 | if (s.empty()) return {}; 525 | if (1 == s.size() && '?' == (char)(*s.begin())) return {}; 526 | return ch; 527 | } 528 | }; 529 | 530 | using checkfunc = std::wstring (*)(const wchar_t); 531 | using fixfunc = xmsg (*)(const void* const data, 532 | size_t& used, 533 | const size_t size, 534 | checkfunc check); 535 | 536 | fixfunc fix_unicode = [](const void* const data, 537 | size_t& used, 538 | const size_t size, 539 | checkfunc check) -> xmsg { 540 | // 无法进行向后匹配完整字符。 541 | if ((used + sizeof(wchar_t)) > size) { 542 | return {}; 543 | } 544 | 545 | const auto ws = check(*(wchar_t*)((const uint8_t*)data + used)); 546 | 547 | if (ws.empty()) return {}; 548 | 549 | used += sizeof(wchar_t); 550 | return ws; 551 | }; 552 | 553 | fixfunc fix_ansi = [](const void* const data, 554 | size_t& used, 555 | const size_t size, 556 | checkfunc check) -> xmsg { 557 | for (size_t i = 1; i <= 2; ++i) { 558 | // 无法进行向后匹配完整字符。 559 | if (used + i > size) break; 560 | size_t read; 561 | const auto ws = as2ws(std::string((const char*)data + used, i), &read); 562 | // 转换失败,尝试扩展匹配。 563 | if (ws.empty()) continue; 564 | 565 | const auto s = check(*ws.begin()); 566 | // 可视化失败,返回。 567 | if (s.empty()) break; 568 | used += i; 569 | return s; 570 | } 571 | return {}; 572 | }; 573 | 574 | auto fix_utf8 = [](const void* const data, 575 | size_t& used, 576 | const size_t size, 577 | checkfunc check) -> xmsg { 578 | for (size_t i = 1; i <= 6; ++i) { 579 | // 无法进行向后匹配完整字符。 580 | if (used + i > size) break; 581 | size_t read; 582 | const auto ws = u82ws(std::u8string((const char8_t*)data + used, i), &read); 583 | // 转换失败,尝试扩展匹配。 584 | if (ws.empty()) continue; 585 | const auto s = check(*ws.begin()); 586 | // 可视化失败,返回。 587 | if (s.empty()) break; 588 | used += i; 589 | return s; 590 | } 591 | return {}; 592 | }; 593 | 594 | fixfunc fix = (SBC_UNICODE == code) ? fix_unicode : ((SBC_UTF8 == code) ? fix_utf8 : fix_ansi); 595 | 596 | do { 597 | prefix(); 598 | // 用 std::minmax 而不是 std::min ,避免 min 宏的干扰。 599 | const size_t fix_len = std::minmax(size, k_max_line_byte).first; 600 | 601 | while (used < fix_len) { 602 | const auto s = fix(data, used, size, check_unicode_visualization); 603 | if (s.empty()) { 604 | used += sizeof(char); 605 | ret.push_back('.'); 606 | } else { 607 | ret.append(s); 608 | } 609 | } 610 | used -= fix_len; 611 | ret.push_back('\r'); 612 | ret.push_back('\n'); 613 | 614 | data += k_max_line_byte; 615 | size -= fix_len; 616 | } while (size != 0); 617 | return ret; 618 | } 619 | 620 | #undef __SBTS 621 | 622 | inline auto showbin( 623 | const void* const bin, 624 | const size_t len, 625 | const ShowBinCode code = CheckBinCode(), 626 | const xmsg& prews = xmsg(), 627 | const bool offset = false, 628 | const bool isup = true) { 629 | return showbin((const char*)bin, len, code, prews, offset, isup); 630 | } 631 | 632 | template 633 | auto showbin( 634 | const T& data, 635 | const ShowBinCode code, 636 | const xmsg& prews, 637 | const bool offset, 638 | const bool isup) 639 | -> std::enable_if_t, xmsg> { 640 | return showbin(data.data(), data.size(), code, prews, offset, isup); 641 | } 642 | 643 | template 644 | auto showbin(const T& data) 645 | -> std::enable_if_t, xmsg> { 646 | return showbin(data.data(), data.size()); 647 | } 648 | 649 | template 650 | auto showbin(const T& data, const ShowBinCode code) 651 | -> std::enable_if_t, xmsg> { 652 | return showbin(data.data(), data.size(), code); 653 | } 654 | 655 | template 656 | auto showbin(const T& data, const xmsg& prews) 657 | -> std::enable_if_t, xmsg> { 658 | return showbin(data.data(), data.size(), CheckBinCode(), prews); 659 | } 660 | 661 | } // namespace xlib 662 | 663 | #endif // _XLIB_XHEXBIN_H_ -------------------------------------------------------------------------------- /xhook.cc: -------------------------------------------------------------------------------- 1 | #include "xhook.h" 2 | 3 | #include "xlib_test.h" 4 | 5 | #ifdef _WIN32 6 | #pragma code_seg(".text") 7 | __declspec(allocate(".text")) 8 | static const uint8_t hook_test_shellcode[] = { 9 | "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" 10 | "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" 11 | "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" 12 | "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" 13 | "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" 14 | "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" 15 | "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" 16 | "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" 17 | "\x33\xC0\xC3"}; 18 | #pragma code_seg() 19 | 20 | using hook_test_function = int(*)(void); 21 | // 注意,不能设置成 const ,编译器会优化不访问,致使测试出现错误结果。 22 | static auto hook_test = (hook_test_function)&hook_test_shellcode; 23 | 24 | static void HookCalling Routine(xlib::CPU_ST* lpcpu) { 25 | lpcpu->regXax += 1; 26 | } 27 | 28 | SHOW_TEST_INIT(xhook) 29 | 30 | hook_test = (hook_test_function)&hook_test_shellcode; 31 | 32 | SHOW_TEST_HEAD(Crack); 33 | done = 0 == hook_test(); 34 | xlib::Crack(hook_test_shellcode, "\x33\xC0\xFE\xC0\xC3"); 35 | done = done && (1 == hook_test()); 36 | SHOW_TEST_RESULT; 37 | 38 | SHOW_TEST_HEAD(shellcodes); 39 | xlib::shellcodes shellcode("\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x33\xC0\xFE\xC0\xC3"); 40 | done = 1 == ((hook_test_function)shellcode.data())(); 41 | SHOW_TEST_RESULT; 42 | 43 | SHOW_TEST_HEAD(hook 2 byte); 44 | xlib::Crack(hook_test_shellcode, "\x33\xC0\xC3"); 45 | auto h2 = new xlib::xHook(&hook_test_shellcode, 2, &Routine, false); 46 | done = 1 == hook_test(); 47 | delete h2; 48 | done = done && (0 == hook_test()); 49 | SHOW_TEST_RESULT; 50 | 51 | SHOW_TEST_HEAD(hook 5 byte); 52 | xlib::Crack(hook_test_shellcode, "\x33\xC0\xFE\xC0\x90\xC3"); 53 | auto h5 = new xlib::xHook(&hook_test_shellcode, 5, &Routine, false); 54 | done = 2 == hook_test(); 55 | delete h5; 56 | done = done && (1 == hook_test()); 57 | SHOW_TEST_RESULT; 58 | 59 | SHOW_TEST_HEAD(hook 6 byte); 60 | xlib::Crack(hook_test_shellcode, "\x33\xC0\xFE\xC0\xFE\xC0\xC3"); 61 | auto h6 = new xlib::xHook(&hook_test_shellcode, 6, &Routine, false); 62 | done = 3 == hook_test(); 63 | delete h6; 64 | done = done && (2 == hook_test()); 65 | SHOW_TEST_RESULT; 66 | 67 | SHOW_TEST_HEAD(hook 15 byte); 68 | xlib::Crack(hook_test_shellcode, "\x33\xC0\xFE\xC0\xFE\xC0\xFE\xC0\x90\x90\x90\x90\x90\x90\x90\x90\xC3"); 69 | auto h15 = new xlib::xHook(&hook_test_shellcode, 15, &Routine, false); 70 | done = 4 == hook_test(); 71 | delete h15; 72 | done = done && (3 == hook_test()); 73 | SHOW_TEST_RESULT; 74 | 75 | SHOW_TEST_HEAD(hook call table); 76 | xlib::Crack(hook_test_shellcode, "\x33\xC0\xFE\xC0\xC3"); 77 | auto hc = new xlib::xHook(&hook_test, &Routine, true, false); 78 | done = 2 == hook_test(); 79 | delete hc; 80 | done = done && (1 == hook_test()); 81 | SHOW_TEST_RESULT; 82 | 83 | SHOW_TEST_HEAD(hook offset); 84 | xlib::Crack(hook_test_shellcode, "\xE8\x01\x00\x00\x00\xC3\x33\xC0\xFE\xC0\xC3", 11); 85 | #ifndef _WIN64 86 | auto ho = new xlib::xHook(&hook_test_shellcode[1], &Routine, false, false); 87 | #else 88 | auto ho = new xlib::xHook(&hook_test_shellcode[1], &Routine, false, false, (void*)&hook_test_shellcode[0x10]); 89 | #endif 90 | done = 2 == hook_test(); 91 | delete ho; 92 | done = done && (1 == hook_test()); 93 | SHOW_TEST_RESULT; 94 | 95 | SHOW_TEST_DONE; 96 | 97 | #endif //_WIN32 -------------------------------------------------------------------------------- /xlib_test.cc: -------------------------------------------------------------------------------- 1 | #include "xlib_test.h" 2 | 3 | static int g_error_count = 0; 4 | 5 | bool xlib_test(xlib_test_routine routine) { 6 | try { 7 | g_error_count += routine(); 8 | return true; 9 | } catch(...) { 10 | ++g_error_count; 11 | std::cerr << std::endl << "======== exception !!!" << std::endl; 12 | return false; 13 | } 14 | } 15 | 16 | #ifdef _WIN32 17 | #include 18 | int _tmain(int , _TCHAR*) 19 | #else 20 | int main() 21 | #endif 22 | { 23 | std::cout << std::endl << "xlib test done." << std::endl; 24 | return g_error_count; 25 | } -------------------------------------------------------------------------------- /xlib_test.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file xlib_test.h 3 | \brief 定义了 xlib 校验环境。 4 | 5 | \version 2.0.0.190919 6 | 7 | \author triones 8 | \date 2013-03-19 9 | 10 | \section history 版本记录 11 | 12 | - 2013-03-19 新建 xlib_test 模块,用于校验 xlib 的正确性。 1.0 。 13 | - 2019-09-19 重构 xlib_test 模块。 2.0 。 14 | */ 15 | #ifndef _XLIB_TEST_H_ 16 | #define _XLIB_TEST_H_ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | using xlib_test_routine = int (*)(void); 23 | 24 | bool xlib_test(xlib_test_routine); 25 | 26 | #define SHOW_TEST_INIT(name) \ 27 | static const auto gkxtb = xlib_test( \ 28 | [] { \ 29 | std::cout << std::endl << "================ test " #name << std::endl; \ 30 | int error_count = 0; \ 31 | auto done = false; 32 | #define SHOW_TEST_DONE \ 33 | std::cout << std::endl; \ 34 | return error_count; \ 35 | } \ 36 | ); 37 | // SHOW_TEST_HEAD 在测试项执行前输出测试项名称。执行 (name, bool) 无意义,而执行 (name, [&]{}) 则繁琐。 38 | #define SHOW_TEST_HEAD(head) \ 39 | std::cout << std::setiosflags(std::ios::left) << std::setw(41) << #head; 40 | #define SHOW_TEST_RESULT \ 41 | if (!done) ++error_count; \ 42 | std::cout << " : " << (done ? "ok" : "fail !!!") << std::endl; 43 | 44 | #endif // _XLIB_TEST_H_ -------------------------------------------------------------------------------- /xlog.cc: -------------------------------------------------------------------------------- 1 | #include "xlog.h" 2 | 3 | #undef xlog_static_lvl 4 | #define xlog_static_lvl xlib::xlog::warn 5 | 6 | #include "xlib_test.h" 7 | 8 | class xlog_ex : public xlib::xmsg, public xlib::xlog_out { 9 | public: 10 | ~xlog_ex() { do_out(*this); } 11 | virtual void raw_out(const xlib::xmsg& msg) { 12 | check = msg; 13 | } 14 | static xlib::xmsg check; 15 | }; 16 | xlib::xmsg xlog_ex::check; 17 | 18 | class xxlog : public xlib::xmsg, public xlib::xlog_out { 19 | public: 20 | ~xxlog() { do_out(*this, 200); } 21 | virtual void raw_out(const xlib::xmsg& msg) { 22 | std::wcout << msg.tows() << std::endl; 23 | } 24 | }; 25 | 26 | #undef xlog_do 27 | #define xlog_do(v) if constexpr ((v) <= xlog_static_lvl) xxlog() 28 | 29 | SHOW_TEST_INIT(xlog) 30 | 31 | SHOW_TEST_HEAD(xlog); 32 | xlog_ex() << XTEXT("xlog ex"); 33 | done = xlog_ex::check == xlib::xmsg(XTEXT("xlog ex")); 34 | SHOW_TEST_RESULT; 35 | 36 | xxlog() << XTEXT("xlog msg 200"); 37 | 38 | xtrace << L"xlog trace xxxxxxxxx"; 39 | xfail << "xlog fail ok"; 40 | 41 | SHOW_TEST_DONE; -------------------------------------------------------------------------------- /xlog.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file xlog.h 3 | \brief 定义了日志组织与输出相关的类。 4 | 5 | \version 2.5.0.250116 6 | 7 | \author triones 8 | \date 2011-07-22 9 | 10 | \section history 版本记录 11 | 12 | - 2011-07-22 新建 xlog 。 0.1 。 13 | - 2011-07-30 增加 xlog::dynamic_lvl 用以控制动态输出等级。 0.1.1 。 14 | - 2011-07-30 增加 xlog_static_lvl 宏用以控制静态输出等级。 15 | - 2011-07-30 新加 endx 函数,及相应操作符。 16 | - 2011-07-30 基本实现完全替换 dbgout 的功能,故从 LIB 中去除 dbgout 。 17 | - 2011-08-20 引入 mem_base_base 使 xlog 继承之。 0.2 。 18 | - 2011-09-08 修复 prt 函数中自动扩展的 BUG 。 0.2.1 。 19 | - 2011-10-17 将 xlog 的格式化处理提取形成 xlog_base ,方便继承用作其它用途。 0.3 。 20 | - 2011-12-12 移植入 COMMON ,对 xlog 新增一个静态变量用以类型控制,同时增加一批相关控制宏。 21 | - 2011-12-12 自此引入类型控制输出功能。 0.4 。 22 | - 2012-04-24 对 xlog_base 新加一个输入函数 operator<<(const xlog_base& v) 。 0.5 。 23 | - 2012-06-06 把 xlog_base 分离出新文件,重命名为 xmsg 。 xlog 继承之。 0.6 。 24 | - 2012-09-25 决定三类输出控制分别独立。 1.0 。 25 | - 2012-10-24 由于 xmsg 包含结尾 0 , out 函数不再追加。 1.0.1 。 26 | - 2016-11-15 适配 Linux g++ 。 1.1 。 27 | - 2019-07-05 添加消息分段功能,以应对 DebugView 在多条消息过长时,出现卡顿。 1.1.1 。 28 | - 2019-09-26 重构 xlog 。去除动态控制与类型控制。 2.0 。 29 | - 2019-11-05 改进声明。 2.1 。 30 | - 2020-11-12 适配 xmsg 升级。 2.2 。 31 | - 2021-08-05 分段输出改进。 2.3 。 32 | - 2023-02-08 改进输出,使重载后的输出更加灵活。 2.4 。 33 | - 2025-01-16 从 xlog 分离出 xlog_out ,以便重载输出。 2.5 。 34 | */ 35 | #ifndef _XLIB_XLOG_H_ 36 | #define _XLIB_XLOG_H_ 37 | 38 | #ifdef _WIN32 39 | #define WIN32_LEAN_AND_MEAN 40 | #define NOMINMAX 41 | #include 42 | #undef NOMINMAX 43 | #undef WIN32_LEAN_AND_MEAN 44 | #else 45 | #include 46 | #endif 47 | 48 | #include "xmsg.h" 49 | 50 | /* 51 | 放弃 XLOGOUT 外部预定义 void XLogout(const xmsg& msg) 行为, 52 | 因可能多处包含 xlog.h ,但 XLOGOUT 可能不全局,将导致多处 .o 文件默认定义。 53 | 也可能导致不同 .o 的 xlog 行为不一致。 54 | 所以改变 xlog 行为建议重载 xlog 实现。 55 | */ 56 | namespace xlib { 57 | 58 | /** 59 | xlog_out 用于 输出控制。 60 | 61 | - 重载 raw_out 以实现输出重定向。 62 | - 重载 do_out 以实现输出行为控制。 63 | - do_out 不采用虚函数形式。节省虚表。 64 | - do_out 不分离 line_max 未设置的情况,实际编译后的代码没有更优,反而额外重写 vft ,额外有判断分支。 65 | */ 66 | class xlog_out { 67 | public: 68 | virtual void raw_out(const xmsg& msg) { 69 | #ifdef _WIN32 70 | OutputDebugStringA(msg.toas().data()); 71 | #else 72 | std::wcout << msg.tows() << std::endl; 73 | #endif 74 | }; 75 | void do_out(xmsg& msg, const size_t line_max = 0) { 76 | const auto size = msg.size(); 77 | if (0 == size) return; 78 | if (0 == line_max || size <= line_max) { 79 | raw_out(msg); 80 | msg.clear(); 81 | return; 82 | } 83 | const auto begin = msg.begin(); 84 | const auto end = msg.end(); 85 | size_t ss = 0; 86 | size_t ll = 0; 87 | for (size_t i = ss; i < size;) { 88 | if (ll >= line_max) { 89 | // raw_out(std::u8string(begin() + ss, begin() + i)); 90 | // raw_out(substr(begin() + ss, begin() + i)); 91 | // 以上两种写法,都会因为 std::u8string 转换 xmsg,多一层移动构造与一层析构。 92 | raw_out(xmsg(begin + ss, begin + i)); 93 | ss = i; 94 | ll = 0; 95 | } 96 | const uint8_t ch = *(begin + i); 97 | // 如果内部自带换行,则避免过多切分。 98 | if(ch == '\n') { ++i; ll = 0; continue; } 99 | if(ch <= 0x7F) { ++i; ++ll; continue; } 100 | // 忽略首字节非法。 101 | if(ch < 0xC0) { ++i; ++ll; continue; } 102 | if(ch < 0xE0) { i += 2; ll += 2; continue; } 103 | if(ch < 0xF0) { i += 3; ll += 3; continue; } 104 | if(ch < 0xF8) { i += 4; ll += 4; continue; } 105 | if(ch < 0xFC) { i += 5; ll += 5; continue; } 106 | if(ch < 0xFE) { i += 6; ll += 6; continue; } 107 | ++i; ++ll; 108 | } 109 | if (ss < size) { 110 | raw_out(xmsg(begin + ss, end)); 111 | } 112 | msg.clear(); 113 | } 114 | }; 115 | 116 | /** 117 | xlog 用于基本的调试信息输出。 118 | 119 | - 一般不直接使用,而是通过宏定义间接使用。\n 120 | - **注意** 类本身没有输出控制(节省资源,加快运行),需要通过宏完成。(宏的具体操作参见之后说明) 121 | - 如果需要扩展功能,如输出到文件等,可选择继承之。建议仿造实现之。 122 | */ 123 | class xlog : public xmsg, public xlog_out { 124 | public: 125 | enum level { 126 | off, ///< 屏蔽输出。 127 | fatal, ///< 致命错误,程序无法继续执行。 128 | error, ///< 反映错误,例如一些 API 的调用失败。 129 | warn, ///< 反映某些需要注意的可能有潜在危险的情况,可能会造成崩溃或逻辑错误之类。 130 | info, ///< 表示程序进程的信息。 131 | debug, ///< 普通的调试信息,这类信息发布时一般不输出。 132 | trace, ///< 最精细的调试信息,多用于定位错误,查看某些变量的值。 133 | on, ///< 全输出。 134 | }; 135 | public: 136 | ~xlog() { do_out(*this); } 137 | }; 138 | 139 | /** 140 | 控制静态编译结果。**注意** 宏的作用是局部的,不同 CPP 可以设置不同的静态控制等级。 141 | 142 | - 前置设置控制等级: 143 | \code 144 | #define xlog_static_lvl 1 // 静态控制等级为 fatal ,只输出最严重的错误。 145 | #include "xlog.h" 146 | \endcode 147 | 148 | - 后置修改控制等级: 149 | \code 150 | #include "xlog.h" 151 | #undef xlog_static_lvl 152 | #define xlog_static_lvl xlog::warn 153 | \endcode 154 | */ 155 | #ifndef xlog_static_lvl 156 | #define xlog_static_lvl xlib::xlog::on 157 | #endif 158 | 159 | /** 160 | 以下宏用于分级静态控制编译结果,根据 xlog_static_lvl 以决定指定调试信息是否被编译。 161 | 162 | \code 163 | xerr << "xerr"; // 当 xlog_static_lvl < xlog::error 时,此句不被编译。 164 | xfail << "xfail"; // 除非 xlog_static_lvl == xlog::off ,否则此句输出。 165 | \endcode 166 | */ 167 | #define xlog_do(v) if constexpr ((v) <= xlog_static_lvl) xlib::xlog() 168 | 169 | #define xtrace xlog_do(xlib::xlog::trace) 170 | #define xdbg xlog_do(xlib::xlog::debug) 171 | #define xinfo xlog_do(xlib::xlog::info) 172 | #define xwarn xlog_do(xlib::xlog::warn) 173 | #define xerr xlog_do(xlib::xlog::error) 174 | #define xfail xlog_do(xlib::xlog::fatal) 175 | 176 | /** 177 | 便捷宏,用于便捷插入函数名及行号。 178 | 179 | \code 180 | xerr << xfuninfo << "这里出错"; 181 | \endcode 182 | */ 183 | #define xfuninfo "[" __FUNCTION__ "][" << __LINE__ << "]: " 184 | /** 185 | 便捷宏,用于便捷插入异常产生的函数。 186 | 187 | \code 188 | xerr << xfunexpt; 189 | \endcode 190 | */ 191 | #define xfunexpt "[" __FUNCTION__ "]: exception." 192 | 193 | } // namespace xlib 194 | 195 | #endif // _XLIB_XLOG_H_ -------------------------------------------------------------------------------- /xmsg.cc: -------------------------------------------------------------------------------- 1 | #include "xmsg.h" 2 | 3 | #include "xlib_test.h" 4 | 5 | #define XMSGS(text) std::u8string((const char8_t*)u8 ## text) 6 | #define XMSGWS(v) xlib::ws2u8(v) 7 | 8 | SHOW_TEST_INIT(xmsg) 9 | 10 | SHOW_TEST_HEAD(constructor as); 11 | done = xlib::xmsg(std::string("AA\xD7\xAA\xBB\xBB\xB2\xE2\xCA\xD4\x42\x42")) == XMSGWS(L"AA转换测试BB"); 12 | SHOW_TEST_RESULT; 13 | 14 | SHOW_TEST_HEAD(constructor ws); 15 | done = xlib::xmsg(L"AA转换测试BB") == XMSGWS(L"AA转换测试BB"); 16 | SHOW_TEST_RESULT; 17 | 18 | SHOW_TEST_HEAD(constructor u8); 19 | done = xlib::xmsg(XMSGS("AA转换测试BB")) == XMSGWS(L"AA转换测试BB"); 20 | SHOW_TEST_RESULT; 21 | 22 | SHOW_TEST_HEAD(prt u8); 23 | done = xlib::xmsg().prt((const char8_t*)u8"%s", (const char8_t*)u8"AA转换测试BB") == XMSGWS(L"AA转换测试BB"); 24 | SHOW_TEST_RESULT; 25 | 26 | SHOW_TEST_HEAD(prt as); 27 | done = xlib::xmsg().prt("%s", "AA\xD7\xAA\xBB\xBB\xB2\xE2\xCA\xD4\x42\x42") == XMSGWS(L"AA转换测试BB"); 28 | SHOW_TEST_RESULT; 29 | 30 | SHOW_TEST_HEAD(int8_t); 31 | done = (xlib::xmsg() << (int8_t)-1) == XMSGS("-1"); 32 | SHOW_TEST_RESULT; 33 | 34 | SHOW_TEST_HEAD(uint8_t); 35 | done = (xlib::xmsg() << (uint8_t)1) == XMSGS("01"); 36 | SHOW_TEST_RESULT; 37 | 38 | SHOW_TEST_HEAD(int16_t); 39 | done = (xlib::xmsg() << (int16_t)-1) == XMSGS("-1"); 40 | SHOW_TEST_RESULT; 41 | 42 | SHOW_TEST_HEAD(uint16_t); 43 | done = (xlib::xmsg() << (uint16_t)255) == XMSGS("00FF"); 44 | SHOW_TEST_RESULT; 45 | 46 | SHOW_TEST_HEAD(int32_t); 47 | done = (xlib::xmsg() << (int32_t)-2) == XMSGS("-2"); 48 | SHOW_TEST_RESULT; 49 | 50 | SHOW_TEST_HEAD(uint32_t); 51 | done = (xlib::xmsg() << (uint32_t)4294967294) == XMSGS("FFFFFFFE"); 52 | SHOW_TEST_RESULT; 53 | 54 | SHOW_TEST_HEAD(int64_t); 55 | done = (xlib::xmsg() << (int64_t)0x112210F47DE98115) == XMSGS("1234567890123456789"); 56 | SHOW_TEST_RESULT; 57 | 58 | SHOW_TEST_HEAD(uint64_t); 59 | done = (xlib::xmsg() << (uint64_t)1234567890123456789) == XMSGS("112210F47DE98115"); 60 | SHOW_TEST_RESULT; 61 | 62 | SHOW_TEST_HEAD(void*); 63 | #if defined(_WIN64) || defined(__amd64) 64 | done = (xlib::xmsg() << (void*)0x12345678) == XMSGS("0000000012345678"); 65 | #else 66 | done = (xlib::xmsg() << (void*)0x12345678) == XMSGS("12345678"); 67 | #endif 68 | SHOW_TEST_RESULT; 69 | 70 | SHOW_TEST_HEAD(bool); 71 | done = (xlib::xmsg() << true) == XMSGS("true"); 72 | SHOW_TEST_RESULT; 73 | 74 | SHOW_TEST_HEAD(char); 75 | done = (xlib::xmsg() << '1') == XMSGS("1"); 76 | SHOW_TEST_RESULT; 77 | 78 | SHOW_TEST_HEAD(char*); 79 | done = (xlib::xmsg() << "123") == XMSGS("123"); 80 | SHOW_TEST_RESULT; 81 | 82 | SHOW_TEST_HEAD(string); 83 | done = (xlib::xmsg() << std::string("123")) == XMSGS("123"); 84 | SHOW_TEST_RESULT; 85 | 86 | SHOW_TEST_HEAD(wchar_t); 87 | done = (xlib::xmsg() << L'1') == XMSGS("1"); 88 | SHOW_TEST_RESULT; 89 | 90 | SHOW_TEST_HEAD(wchar_t*); 91 | done = (xlib::xmsg() << L"123") == XMSGS("123"); 92 | SHOW_TEST_RESULT; 93 | 94 | SHOW_TEST_HEAD(wstring); 95 | done = (xlib::xmsg() << std::wstring(L"123")) == XMSGS("123"); 96 | SHOW_TEST_RESULT; 97 | 98 | #ifdef __cpp_char8_t 99 | SHOW_TEST_HEAD(char8_t); 100 | done = (xlib::xmsg() << char8_t(u8'1')) == XMSGS("1"); 101 | SHOW_TEST_RESULT; 102 | #endif 103 | 104 | SHOW_TEST_HEAD(char8_t*); 105 | done = (xlib::xmsg() << (const char8_t*)u8"123") == XMSGS("123"); 106 | SHOW_TEST_RESULT; 107 | 108 | SHOW_TEST_HEAD(std::u8string); 109 | done = (xlib::xmsg() << std::u8string((const char8_t*)u8"123")) == XMSGS("123"); 110 | SHOW_TEST_RESULT; 111 | 112 | SHOW_TEST_HEAD(float); 113 | done = (xlib::xmsg() << (float)1.0) == XMSGS("1.000000"); 114 | SHOW_TEST_RESULT; 115 | 116 | SHOW_TEST_HEAD(double); 117 | done = (xlib::xmsg() << (double)1.0) == XMSGS("1.000000"); 118 | SHOW_TEST_RESULT; 119 | 120 | SHOW_TEST_HEAD(xmsg); 121 | done = (xlib::xmsg() << xlib::xmsg("123")) == XMSGS("123"); 122 | SHOW_TEST_RESULT; 123 | 124 | std::u8string u8move((const char8_t*)u8"123"); 125 | xlib::xmsg msgmove(std::move(u8move)); 126 | SHOW_TEST_HEAD(u8&&); 127 | done = msgmove == XMSGS("123") && u8move.empty(); 128 | SHOW_TEST_RESULT; 129 | 130 | SHOW_TEST_HEAD(xmsg&&); 131 | std::u8string u8move2 = std::move(msgmove); 132 | done = u8move2 == XMSGS("123") && msgmove.empty(); 133 | SHOW_TEST_RESULT; 134 | 135 | SHOW_TEST_HEAD(prt check); 136 | xlib::xmsg prtchk; 137 | prtchk.prt("123456781234567"); 138 | prtchk.prt("123456781234567"); 139 | done = prtchk == XMSGS("123456781234567123456781234567"); 140 | SHOW_TEST_RESULT; 141 | 142 | SHOW_TEST_DONE; 143 | 144 | #undef XMSGS 145 | #undef XMSGWS -------------------------------------------------------------------------------- /xmsg.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file xmsg.h 3 | \brief 定义了信息组织的基本类,类似标准库的 ostreamstring 。 4 | 5 | \version 5.3.1.250421 6 | \note For All 7 | 8 | \author triones 9 | \date 2011-07-22 10 | 11 | \section history 版本记录 12 | 13 | - 2012-06-06 从 xlog 中分离,重新定义为 xmsg 。 0.1 。 14 | - 2012-06-06 考虑 xmsg 继承 mem_buffer ,需要新建不少函数,暂不施行。 15 | - 2012-07-19 优化 prt 。 0.1.1 16 | - 2012-10-09 新增对 int64 的支持。其它格式化作了些优化。 0.2 。 17 | - 2012-10-23 使 xmsg 信息组织后数据包含结尾 0 。 xmsg 初始时为 "\0" 而非空串。 18 | - 2012-10-23 重载 end 、 empty 。 0.3 。 19 | - 2013-03-05 重载 clear 以修复缓冲清空后的小 BUG 。 0.3.1 。 20 | - 2014-01-11 引入 SGISTL ,修改适应标准库。 1.0 。 21 | - 2014-04-09 修改一些数据的输出格式。 1.1 。 22 | - 2016-07-20 添加 xmsg 构造。 1.2 。 23 | - 2016-11-15 适配 Linux g++ 。处理不再附加结尾 0 。 1.3 。 24 | - 2019-09-25 重构 xmsg 。 2.0 。 25 | - 2019-11-03 引入 char8_t 、 u8string 。 2.1 。 26 | - 2020-03-09 改变基类为 wstring 。 3.0 。 27 | - 2020-11-12 改变基类可选,改变数值输出为模板 。 4.0 。 28 | - 2021-06-20 基类固定为 u8string 。 5.0 。 29 | - 2022-01-05 向下兼容 c++17 。 30 | - 2024-12-25 增加移动语义支持,优化特定情况下高效转换。 5.2 。 31 | - 2025-03-25 优化格式化输出。 5.3 。 32 | */ 33 | #ifndef _XLIB_XMSG_H_ 34 | #define _XLIB_XMSG_H_ 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include "xcodecvt.h" 43 | 44 | namespace xlib { 45 | 46 | #ifndef __cpp_char8_t 47 | #define XMSGT(__t) (const char8_t*)u8 ## __t 48 | #else 49 | #define XMSGT(__t) u8 ## __t 50 | #endif 51 | #define XMSGAS(__v) xlib::as2u8(__v) 52 | #define XMSGWS(__v) xlib::ws2u8(__v) 53 | 54 | class xmsg : public std::u8string { 55 | public: 56 | using std::u8string::u8string; 57 | 58 | public: 59 | xmsg() = default; 60 | xmsg(const xmsg&) = default; 61 | xmsg& operator=(const xmsg&) = default; 62 | xmsg(xmsg&&) = default; 63 | xmsg& operator=(xmsg&&) = default; 64 | 65 | // 注意:u8 = std::move(xmsg); / std::u8string(std::move(xmsg)); 移动语义都不成立。 66 | // std::u8string u8 = std::move(xmsg); 定义时,移动语义成立。 67 | // return std::move(xmsg); 返回时,移动语义成立。 68 | // 注意:转换基类无需额外定义 operator std::u8string() 。 69 | public: 70 | xmsg(const std::u8string& u8) : std::u8string(u8) {} 71 | xmsg& operator=(const std::u8string& u8) { 72 | std::u8string::operator=(u8); 73 | return *this; 74 | } 75 | xmsg(std::u8string&& u8) : std::u8string(std::move(u8)) {} 76 | xmsg& operator=(std::u8string&& u8) { 77 | std::u8string::operator=(std::move(u8)); 78 | return *this; 79 | } 80 | 81 | public: 82 | xmsg(const std::string& as) : std::u8string(XMSGAS(as)) {} 83 | xmsg(const std::wstring& ws) : std::u8string(XMSGWS(ws)) {} 84 | 85 | public: 86 | xmsg& push_me(const char8_t& v) { 87 | push_back(v); 88 | return *this; 89 | } 90 | xmsg& push_me(const char8_t* v) { 91 | if (nullptr != v) append(v); 92 | return *this; 93 | } 94 | xmsg& push_me(const std::u8string& v) { 95 | append(v); 96 | return *this; 97 | } 98 | xmsg& push_me(const std::u8string_view& v) { 99 | append(v); 100 | return *this; 101 | } 102 | 103 | public: 104 | xmsg& push(const char8_t& u8) { return push_me(u8); } 105 | xmsg& push(const char8_t* u8) { return push_me(u8); } 106 | xmsg& push(const std::u8string& u8) { return push_me(u8); } 107 | xmsg& push(const std::u8string_view& u8) { return push_me(u8); } 108 | 109 | xmsg& push(const char& as) { 110 | if (!is_easy_transcoding(as)) { 111 | return push_me(XMSGAS(std::string(1, as))); 112 | } 113 | return push_me((char8_t)as); 114 | } 115 | xmsg& push(const char* as) { 116 | if (nullptr == as) return *this; 117 | for (auto p = as; '\0' != *p; ++p) { 118 | if (!is_easy_transcoding(*p)) { 119 | return push_me(XMSGAS(as)); 120 | } 121 | } 122 | return push_me((const char8_t*)as); 123 | } 124 | xmsg& push(const std::string& as) { 125 | for (const auto& c : as) { 126 | if (!is_easy_transcoding(c)) { 127 | return push_me(XMSGAS(as)); 128 | } 129 | } 130 | return push_me(*(const std::u8string*)&as); 131 | } 132 | xmsg& push(const std::string_view& as) { 133 | for (const auto& c : as) { 134 | if (!is_easy_transcoding(c)) { 135 | return push_me(XMSGAS(std::string(as))); 136 | } 137 | } 138 | 139 | return push_me(*(const std::u8string_view*)&as); 140 | } 141 | 142 | xmsg& push(const wchar_t& ws) { 143 | if (!is_easy_transcoding(ws)) { 144 | return push_me(XMSGWS(std::wstring(1, ws))); 145 | } 146 | return push_me((char8_t)ws); 147 | } 148 | xmsg& push(const wchar_t* ws) { 149 | if (nullptr == ws) return *this; 150 | for (auto p = ws; L'\0' != *p; ++p) { 151 | if (!is_easy_transcoding(*p)) { 152 | return push_me(XMSGWS(ws)); 153 | } 154 | } 155 | std::u8string buf(wcslen(ws), (char8_t)u8'0'); 156 | for (size_t i = 0; i < buf.size(); ++i) { 157 | buf[i] = (char8_t)ws[i]; 158 | } 159 | return push_me(buf); 160 | } 161 | xmsg& push(const std::wstring& ws) { 162 | for (const auto& c : ws) { 163 | if (!is_easy_transcoding(c)) { 164 | return push_me(XMSGWS(ws)); 165 | } 166 | } 167 | std::u8string buf(ws.size(), (char8_t)u8'0'); 168 | for (size_t i = 0; i < buf.size(); ++i) { 169 | buf[i] = (char8_t)ws[i]; 170 | } 171 | return push_me(buf); 172 | } 173 | xmsg& push(const std::wstring_view& ws) { 174 | for (const auto& c : ws) { 175 | if (!is_easy_transcoding(c)) { 176 | return push_me(XMSGWS(std::wstring(ws))); 177 | } 178 | } 179 | std::u8string buf(ws.size(), (char8_t)u8'0'); 180 | for (size_t i = 0; i < buf.size(); ++i) { 181 | buf[i] = (char8_t)ws[i]; 182 | } 183 | return push_me(buf); 184 | } 185 | 186 | public: 187 | /// 188 | /** 指定格式输出。UTF-8 ,格式化最高效。 189 | \details 设计思路: 190 | - 直接写进本对象缓冲,效率最高。 191 | - 如果缓冲区未满,则用缓冲区剩余空间尝试。争取一次完成格式化。 192 | - 因 capacity() 不包含结尾 0 , vsnprintf 返回值 不包含结尾 0 ,但 参数 count 需要包含结尾 0 ,所以 remain + 1 ,没有溢出风险。 193 | - 比如 vsnprintf(p, 2, "AA"); 返回 2 ,但实际写入 1 个字符,所以需要 + 1 。vsnprintf(p, 3, "AA"); 才能正确写入 2 个字符。 194 | - 首次格式化,预先 resize 的方法,没有更优。因为 resize 剩余缓冲可能比格式化的数据更多,此时 resize 有多余的写入。 195 | - 二次格式化时,resize 更优。 196 | - 经简单测试,优化后的格式化: 197 | - 在缓冲区足够的情况下,性能提升显著。 198 | - 在缓冲区不足的情况下,性能或提升或下降,但与之前差异不大。 199 | */ 200 | xmsg& prt(const char8_t* const fmt, ...) { 201 | if (nullptr == fmt) return *this; 202 | // 直接写进本对象缓冲,效率最高。 203 | const auto now = size(); 204 | const auto remain = capacity() - now + 1; 205 | va_list ap; 206 | va_start(ap, fmt); 207 | const auto need = std::vsnprintf((char*)data() + now, remain, (const char*)fmt, ap); 208 | va_end(ap); 209 | if (0 >= need) return *this; 210 | if ((size_t)need < remain) { 211 | // 一次完成格式化。用简单覆写代替二次复杂格式化。注意不能用 resize 。 212 | append(data() + now, need); 213 | return *this; 214 | } 215 | // 如果缓冲区不足,则直接用 resize ,这样格式化后无需覆写。采用重写最少,最优的方案。 216 | resize(now + need); 217 | // 缓冲区不足时,写入被截断,才需要第二次格式化。 218 | va_start(ap, fmt); 219 | std::vsnprintf((char*)data() + now, need + 1, (const char*)fmt, ap); 220 | va_end(ap); 221 | return *this; 222 | } 223 | /// 针对 UTF-8 对齐问题, %s 格式化建议使用 char* 。 224 | xmsg& prt(const char* const fmt, ...) { 225 | if (nullptr == fmt) return *this; 226 | // 注意,因为需要编码转换,所以不能直接写进本对象缓冲。 227 | std::string buffer; 228 | const auto remain = buffer.capacity() + 1; 229 | va_list ap; 230 | va_start(ap, fmt); 231 | const auto need = std::vsnprintf(buffer.data(), remain, fmt, ap); 232 | va_end(ap); 233 | if (0 >= need) return *this; 234 | if ((size_t)need < remain) { 235 | buffer.append(buffer.data(), need); 236 | } else { 237 | buffer.resize(need); 238 | va_start(ap, fmt); 239 | std::vsnprintf(buffer.data(), need + 1, fmt, ap); 240 | va_end(ap); 241 | } 242 | push(buffer); 243 | return *this; 244 | } 245 | // 因为 vswprintf 在 unix 下有些问题,干脆不支持。为记。 246 | 247 | public: 248 | void need_remain(const size_t need) { 249 | const auto x = size() + need; 250 | if (x > capacity()) { 251 | reserve(x); 252 | } 253 | } 254 | 255 | // 对于不定长的格式化,不预先 reserve 。 256 | public: 257 | /// 输出 dec 值。 258 | template std::enable_if_t, xmsg&> 259 | operator<<(const T& v) { 260 | return prt(XMSGT("%hhi"), v); 261 | } 262 | /// 输出 hex(XX)。 263 | template std::enable_if_t || std::is_enum_v), xmsg&> 264 | operator<<(const T& v) { 265 | need_remain(2); 266 | return prt(XMSGT("%02X"), v); 267 | } 268 | /// 输出 dec 值。 269 | template std::enable_if_t, xmsg&> 270 | operator<<(const T& v) { 271 | return prt(XMSGT("%hi"), v); 272 | } 273 | /// 输出 hex(XXXX)。 274 | template std::enable_if_t || std::is_enum_v), xmsg&> 275 | operator<<(const T& v) { 276 | need_remain(4); 277 | return prt(XMSGT("%04X"), v); 278 | } 279 | /// 输出 dec 值。 280 | template std::enable_if_t, xmsg&> 281 | operator<<(const T& v) { 282 | return prt(XMSGT("%i"), v); 283 | } 284 | /// 输出 hex(XXXXXXXX)。 285 | template std::enable_if_t || std::is_enum_v), xmsg&> 286 | operator<<(const T& v) { 287 | need_remain(8); 288 | return prt(XMSGT("%08X"), v); 289 | } 290 | /// 输出 dec 值。 291 | template std::enable_if_t, xmsg&> 292 | operator<<(const T& v) { 293 | return prt(XMSGT("%lli"), v); 294 | } 295 | /// 输出 hex(XXXXXXXXXXXXXXXX)。 296 | template std::enable_if_t || std::is_enum_v), xmsg&> 297 | operator<<(const T& v) { 298 | need_remain(16); 299 | return prt(XMSGT("%08X%08X"), (uint32_t)(v >> (CHAR_BIT * sizeof(uint32_t))), (uint32_t)v); 300 | } 301 | /// 输出 hex 指针。 302 | xmsg& operator<<(const void* const v) { 303 | return operator<<((uintptr_t)v); 304 | } 305 | /// 输出 true 或 false。 306 | xmsg& operator<<(const bool& v) { 307 | return operator<<(v ? XMSGT("true") : XMSGT("false")); 308 | } 309 | /// 输出 ANSI 字符 转换。 310 | xmsg& operator<<(const char& v) { return push(v); } 311 | /// 输出 ANSI 字符串 转换。 312 | xmsg& operator<<(const char* const v) { return push(v); } 313 | /// 输出 ASNI 字符串 转换。 314 | xmsg& operator<<(const std::string& v) { return push(v); } 315 | /// 输出 ASNI 字符串 转换。 316 | xmsg& operator<<(const std::string_view& v) { return push(v); } 317 | /// 输出 UNICCODE 字符 转换。 318 | xmsg& operator<<(const wchar_t& v) { return push(v); } 319 | /// 输出 UNICCODE 字符串 转换。 320 | xmsg& operator<<(const wchar_t* const v) { return push(v); } 321 | /// 输出 UNICCODE 字符串 转换。 322 | xmsg& operator<<(const std::wstring& v) { return push(v); } 323 | /// 输出 UNICCODE 字符串 转换。 324 | xmsg& operator<<(const std::wstring_view& v) { return push(v); } 325 | #ifdef __cpp_char8_t 326 | /// 输出 UTF-8 字符 转换。 327 | xmsg& operator<<(const char8_t& v) { return push(v); } 328 | #endif 329 | /// 输出 UTF-8 字符串 转换。 330 | xmsg& operator<<(const char8_t* v) { return push(v); } 331 | /// 输出 UTF-8 字符串 转换。 332 | xmsg& operator<<(const std::u8string& v) { return push(v); } 333 | /// 输出 UTF-8 字符串 转换。 334 | xmsg& operator<<(const std::u8string_view& v) { return push(v); } 335 | /// 输出 dec 浮点数。 336 | xmsg& operator<<(const float& v) { 337 | return prt(XMSGT("%f"), v); 338 | } 339 | /// 输出 dec 浮点数。 340 | xmsg& operator<<(const double& v) { 341 | return prt(XMSGT("%f"), v); 342 | } 343 | /// 输出 内容。 344 | xmsg& operator<<(const xmsg& v) { 345 | append(v); 346 | return *this; 347 | } 348 | /// 转换 ASCII 。 349 | std::string toas() const { 350 | return u82as(*this); 351 | } 352 | /// 转换 UNICODE 。 353 | std::wstring tows() const { 354 | return u82ws(*this); 355 | } 356 | }; 357 | 358 | #undef XMSGT 359 | #undef XMSGAS 360 | #undef XMSGWS 361 | 362 | } // namespace xlib 363 | 364 | #endif // _XLIB_XMSG_H_ -------------------------------------------------------------------------------- /xrand.cc: -------------------------------------------------------------------------------- 1 | #include "xrand.h" 2 | 3 | #include "xlib_test.h" 4 | 5 | #ifndef __cpp_lib_bitops 6 | #pragma message("xlib without ") 7 | #endif 8 | 9 | SHOW_TEST_INIT(xrand) 10 | 11 | SHOW_TEST_HEAD(xrand); 12 | done = xlib::xrand(0x10000) < 0x10000; 13 | SHOW_TEST_RESULT; 14 | 15 | std::cout << xlib::xrand() << std::endl; 16 | std::cout << xlib::xrand() << std::endl; 17 | std::cout << xlib::xrand() << std::endl; 18 | std::cout << xlib::xrand() << std::endl; 19 | 20 | SHOW_TEST_DONE; -------------------------------------------------------------------------------- /xrand.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file xrand.h 3 | \brief 定义了随机数的生成模板。(随机平均率无法保证、刻意忽略线程安全。) 4 | 5 | \version 3.0.2.220105 6 | 7 | \author triones 8 | \date 2013-01-08 9 | 10 | \section history 版本记录 11 | 12 | - 2013-01-11 新建 xrand 函数 。 0.1 13 | - 2013-11-29 改进 xrand 函数以适应 x64 。 1.0 。 14 | - 2016-07-19 优化 xrand 。 1.1 。 15 | - 2016-11-14 适配 Linux g++ 。 1.2 。 16 | - 2019-09-20 重构 xrand 。允许 x86 下获取 64 bit 随机值。 2.0 。 17 | - 2019-09-29 再次重构 xrand 。放弃单例。 3.0 。 18 | - 2021-06-20 VS2019 支持 bit 库,引入改造之。 19 | - 2022-01-05 引入旧代码,向下兼容 c++17 。 20 | */ 21 | #ifndef _XLIB_XRAND_H_ 22 | #define _XLIB_XRAND_H_ 23 | 24 | #ifdef _WIN32 25 | #include 26 | #else 27 | #include 28 | #endif 29 | 30 | #include 31 | #include 32 | 33 | #ifdef __cpp_lib_bitops 34 | #include 35 | #endif 36 | 37 | namespace xlib { 38 | 39 | /** 40 | 用于生成随机数 41 | \param mod 指定取模。默认为 0 ,不取模。 42 | \return 返回随机数。 43 | 44 | \code 45 | auto randvalue = xrand(); 46 | \endcode 47 | */ 48 | inline uint64_t xrand(const uint64_t mod = 0) { 49 | static auto seed = __rdtsc(); // 经验证 seed 全局唯一。 50 | const auto r = __rdtsc(); 51 | const int l = r % (CHAR_BIT * sizeof(size_t)); 52 | #ifdef __cpp_lib_bitops 53 | seed += std::rotr(r, l); 54 | #else 55 | // x86 下的 _lrotr 行为一致。 56 | // x64 下的 _lrotr , windows 使用 32 bit , linux 使用 64 bit 。 57 | // x64 下的 unsigned long , windows 使用 32 bit , linux 使用 64 bit 。 58 | // 对于不支持 标准库 bit 的,采用此做法。 59 | seed += (r << (sizeof(uint32_t) * CHAR_BIT)) + _lrotr((unsigned long)r, l); 60 | #endif 61 | return (0 != mod) ? (seed % mod) : (seed); 62 | } 63 | 64 | } // namespace xlib 65 | 66 | #endif // _XLIB_XRAND_H_ -------------------------------------------------------------------------------- /xsig.cc: -------------------------------------------------------------------------------- 1 | #include "xlib_test.h" 2 | #include "xlog.h" 3 | 4 | class xxlog : public xlib::xmsg, public xlib::xlog_out { 5 | public: 6 | ~xxlog() { do_out(*this); } 7 | virtual void raw_out(const xlib::xmsg& msg) { 8 | std::cout << msg.toas() << std::endl; 9 | } 10 | }; 11 | 12 | #define xslog xxlog() 13 | #define xsig_need_debug 14 | #include "xsig.h" 15 | 16 | 17 | xlib::xsig operator"" _sig(const char* signature, std::size_t) { 18 | xlib::xsig s; 19 | s.make_lexs(signature); 20 | return s; 21 | } 22 | 23 | SHOW_TEST_INIT(xsig) 24 | 25 | xlib::xsig::dbglog = false; 26 | 27 | const std::string ss(xlib::hex2bin(std::string( 28 | "FF50C745E800000000E80000C745FC00000000E8" 29 | ))); 30 | 31 | auto sig = "0000C745FC00000000E8"_sig; 32 | 33 | SHOW_TEST_HEAD(xsig); 34 | 35 | done = sig.match({xlib::xblk(ss.data(), ss.size())}); 36 | 37 | if (done) { 38 | const auto rep = sig.report(nullptr); 39 | done = rep.begin()->second.p == (ss.data() + 10); 40 | } 41 | 42 | SHOW_TEST_RESULT; 43 | 44 | SHOW_TEST_HEAD(xsig bin); 45 | 46 | sig = "11223344..778800"_sig; 47 | auto bs = sig.to_bin(); 48 | xlib::xsig sigx; 49 | sigx.from_bin(bs); 50 | 51 | done = bs.empty(); 52 | 53 | SHOW_TEST_RESULT; 54 | 55 | SHOW_TEST_DONE; -------------------------------------------------------------------------------- /xsig.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file xsig.h 3 | \brief 用于特征码定位。 4 | 5 | \version 0.0.1.230208 6 | 7 | \author triones 8 | \date 2023-02-07 9 | 10 | \details 11 | 12 | - regex 中的 '.' 在 VS 中,不匹配 换行符。 13 | - 使用 (?:.|\r|\n) 用于匹配任意字符。 14 | - 注意不能使用 [.|\r|\n] ,[] 里的 . 是 '.' 符号自身,而不是正则意义的 '.' 。 15 | - (?:) 用于放弃捕获。 16 | - 建议使用 [\s\S] 来捕获任意字符,因为上面的方法在后面存在 () 空捕获时,会出现 栈溢出。 17 | - 大概看了 regex 的匹配细节,貌似没有更优,定长字符串也是逐字符判断。 18 | - regex 也不符合复杂特征匹配语法的实现。比如 & 操作,同名值判定。 19 | 20 | \section history 版本记录 21 | 22 | - 2023-02-07 新建 xsig 。 0.1 。 23 | */ 24 | #ifndef _XLIB_XSIG_H_ 25 | #define _XLIB_XSIG_H_ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #ifdef _WIN32 35 | #define WIN32_LEAN_AND_MEAN 36 | #define NOMINMAX 37 | #include 38 | #undef NOMINMAX 39 | #undef WIN32_LEAN_AND_MEAN 40 | #endif 41 | 42 | #include "xbin.h" 43 | #include "xblk.h" 44 | #include "xcodecvt.h" 45 | #include "xhexbin.h" 46 | #include "xlog.h" 47 | #include "xmsg.h" 48 | #include "xswap.h" 49 | #include "xvarint.h" 50 | 51 | /* 52 | 默认不编译 dbg 信息。 53 | 如需要编译 dbg 信息,请添加 xsig_need_debug 宏。 54 | 默认 dbg 信息不输出。如果需输出,请设置 xsig::dbglog = true; 55 | 56 | 设置宏 xslog 以改变日志输出行为。 57 | */ 58 | 59 | #ifndef xslog 60 | #define xslog xlib::xlog() 61 | #endif 62 | 63 | #ifdef xsig_need_debug 64 | #define xsdbg \ 65 | if (xlib::xsig::dbglog) xslog 66 | #else 67 | #define xsdbg \ 68 | if constexpr (false) xslog 69 | #endif 70 | #define xserr xslog 71 | 72 | #ifdef _WIN32 73 | #ifdef _WIN64 74 | #define xsig_is_x64 75 | #endif 76 | #else 77 | // 非 windows 一律认为是 x64 78 | #define xsig_is_x64 79 | #endif 80 | 81 | namespace xlib { 82 | 83 | class xsig { 84 | public: 85 | xsig() = default; 86 | xsig(const std::string& sig) { make_lexs(sig.data()); } 87 | //////////////////////////////////////////////////////////////// value 结构 88 | struct value { 89 | char t; //< qdwbp n(错误)。 90 | union { 91 | uint64_t q; 92 | uint32_t d; 93 | uint16_t w; 94 | uint8_t b; 95 | void* p; 96 | }; 97 | }; 98 | 99 | private: 100 | //////////////////////////////////////////////////////////////// Sign 类 101 | class Sign { 102 | public: 103 | Sign(const char* const sig) : s(sig), p(0) {} 104 | /// 返回当前处理的字符。 105 | inline char operator()() const { return s[p]; } 106 | /// 向后移动一个字符。 107 | inline void operator++() { ++p; } 108 | 109 | public: 110 | /// 计算当前位置行列信息,指示位置。 111 | inline xmsg operator*() const { 112 | intptr_t row = 1; 113 | intptr_t col = 1; 114 | const intptr_t lp = p; 115 | for (intptr_t i = 0; i < lp; ++i) { 116 | switch (s[i]) { 117 | case '\n': col = 1; ++row; break; 118 | case '\0': return xmsg() << "[" << row << "][" << col << "][overflow]"; 119 | default : ++col; break; 120 | } 121 | } 122 | return xmsg() << "[" << row << "][" << col << "]"; 123 | } 124 | 125 | private: 126 | const char* s; //< 存放特征码字符串指针。 127 | intptr_t p; //< 指示当前解析字符起始索引。 128 | }; 129 | 130 | private: 131 | //////////////////////////////////////////////////////////////// Range 类 132 | struct Range { 133 | using Type = intptr_t; //< 范围类型。 134 | Type Min; //< 最小匹配次数。 135 | Type Max; //< 最大匹配次数。 136 | /// 用以标识最大范围指示值。 137 | static inline constexpr Type MaxType = INTPTR_MAX; 138 | /// 用以标识错误范围指示值。 139 | static inline constexpr Type ErrType = -1; 140 | /// 用以标识初始范围指示值。 141 | static inline constexpr Type InitType = -1; 142 | /// 故意删除默认初始化,请用 其他构造 或 {} 构造。 143 | Range() = delete; 144 | /// 简单初始化。 145 | constexpr Range(const Type min, const Type max) : Min(min), Max(max) {} 146 | /// 指定固定范围。 147 | constexpr Range(const Type min) : Min(min), Max(min) {} 148 | /// 比较范围是否相同。 149 | bool operator==(const Range& r) const { 150 | return (Min == r.Min) && (Max == r.Max); 151 | } 152 | /// 范围融合。 153 | Range& operator+=(const Range& r) { 154 | const auto min = Min + r.Min; 155 | Min = (min < r.Min) ? MaxType : min; 156 | 157 | const auto max = Max + r.Max; 158 | Max = (max < r.Max) ? MaxType : max; 159 | 160 | if (Min > Max) std::swap(Min, Max); 161 | return *this; 162 | } 163 | /// 尝试重新组织并输出特征串。 164 | xmsg sig() const { 165 | // 1 范围不输出。 166 | if (*this == Range(1, 1)) return {}; 167 | if (*this == Range(0, 1)) return xmsg() << '?'; 168 | if (*this == Range(0, MaxType)) return xmsg() << '*'; 169 | if (*this == Range(1, MaxType)) return xmsg() << '+'; 170 | if (Min == Max) return xmsg().prt("{%tX}", Min); 171 | return xmsg().prt("{%tX,%tX}", Min, Max); 172 | } 173 | }; 174 | /// 用以标识错误的范围。 175 | static inline const Range ErrRange = {Range::ErrType, Range::ErrType}; 176 | 177 | public: 178 | //////////////////////////////////////////////////////////////// 词法类 179 | class Lexical { 180 | public: 181 | enum Type : uint8_t { 182 | LT_End, //< end -> \0 183 | //< bs -> [ \t] 184 | //< ws -> [ \t\n\r]* // 空白词法不生成类型。 185 | //< note -> #[^\n\0]*(\n|\0) // 注释词法不生成类型。 186 | //< 以下词法不单独成词。 187 | //< hex -> [0-9A-Fa-f] 188 | //< range_value -> {hex}{1,8} | {hex}{1,16} 189 | //< range -> ([\*\+\?])|(\{{bs}{range_value}?{bs},?{bs}{range_value}?{bs}\}) 190 | //< hexhex -> {hex}{2} 191 | LT_Dot, //< dot -> \.{range}? 192 | LT_Record, //< record -> \<\^?[AFQDWB]{bs}[^\n\r\0\>]*\> 193 | //< const -> {hexhex} 194 | LT_Hexs, //< hexs -> {const}+ 195 | LT_Sets, 196 | }; 197 | 198 | public: 199 | //////////////////////////////////////////////////////////////// 词法基类 200 | class Base : public std::enable_shared_from_this { 201 | public: 202 | Base(Type t, const Range& r = {1, 1}) : type(t), range(r) { 203 | reset_match(); 204 | } 205 | virtual ~Base() {} 206 | /// 用以尝试 重新组织 并 输出 还原特征串。 207 | virtual xmsg sig() const = 0; 208 | /// 输出词法 bin 细节。 209 | virtual void bin(vbin&) const = 0; 210 | /// 输出词法 bin 。注意:默认不输出 range ,需在 bin 中自决。 211 | void bins(vbin& bs) const { bs << type; bin(bs); } 212 | /// 词法序列优化。 213 | std::shared_ptr optimizes() { 214 | if (!child) return optimize(); 215 | // 如果不是最后一条词法,则需要先优化下一条词法。 216 | auto newo = child->optimizes(); 217 | if (newo) child = newo; 218 | 219 | return optimize(); 220 | } 221 | /** 222 | 进行词法优化。 223 | 通常是 通过分析下一条词法,整合 同类型词法。 224 | 225 | 返回空时,无需操作。 226 | 返回非空时,当前结点应替换成新的返回结果。 227 | */ 228 | virtual std::shared_ptr optimize() { return {}; } 229 | /// 匹配重置。 230 | void reset_match() { 231 | match_count = Range::InitType; 232 | match_mem = nullptr; 233 | } 234 | /// 给定内存段细节匹配。 235 | virtual bool test(const xblk&) const = 0; 236 | /** 237 | 指定 内存范围 和 索引,进行匹配。 238 | 匹配失败返回 false ,失败且 lp > blk.size() 时,彻底失败。 239 | \param blk 匹配范围。 240 | \param lp 当前指针。注意是引用,内部会修改其值。 241 | \return -2 失败,不可回退。内存范围不足继续匹配。 242 | \return -1 失败,但可回退。 243 | \return 其他返回表示 成功匹配,返回匹配字节数。(注意可能返回 0) 244 | */ 245 | intptr_t match(const xblk& blk, const intptr_t& lp) { 246 | void* pp = (uint8_t*)blk.begin() + lp; 247 | // 如果匹配达到最大,指针回退,允许继续。 248 | if (match_count >= range.Max) { 249 | xsdbg << pp << " | `" << sig() << "` max match, back."; 250 | return -1; 251 | } 252 | // 如果尚未匹配,则先进行最小匹配。 253 | if (match_count < range.Min) { 254 | // 如果内存范围已经不足以进行最低匹配,则彻底失败。 255 | if ((Range::Type)blk.size() < (lp + range.Min)) { 256 | xsdbg << pp << " | `" << sig() << "` min match fail !"; 257 | return -2; 258 | } 259 | xsdbg << pp << " | `" << sig() << "` min matching..."; 260 | // 记录匹配地址。注意应在 test 之前,因 test 可能会使用它。 261 | match_mem = pp; 262 | // 最低匹配失败,允许回退继续。 263 | if (!test(xblk(pp, range.Min))) { 264 | xsdbg << pp << " | `" << sig() << "` min match fail, back."; 265 | return -1; 266 | } 267 | match_count = range.Min; 268 | return range.Min; 269 | } 270 | 271 | // 如果内存范围已经不足以进行递进匹配,则彻底失败。 272 | if ((Range::Type)blk.size() < (lp + 1)) { 273 | xsdbg << pp << ' ' << sig() << " stepping fail !"; 274 | return -2; 275 | } 276 | xsdbg << pp << ' ' << sig() << " stepping : " << match_count + 1; 277 | // 递进匹配失败,允许回退继续。 278 | if (!test(xblk(pp, 1))) { 279 | xsdbg << pp << ' ' << sig() << " stepping fail, back."; 280 | return -1; 281 | } 282 | ++match_count; 283 | return 1; 284 | } 285 | /// 添加子结点。 286 | void push_back(std::shared_ptr& o) { 287 | // 如果没有子结点,则直接添加。否则让子结点添加。 288 | if (child) return child->push_back(o); 289 | child = o; 290 | o->parent = shared_from_this(); 291 | } 292 | 293 | public: 294 | const Type type; //< 指示词法类型。 295 | Range range; //< 指示匹配内存大小。 296 | intptr_t match_count; //< 指示在匹配过程中匹配的大小。 297 | void* match_mem; //< 记录匹配位置。 298 | // 注意使用 weak_ptr 避免循环引用。 299 | std::weak_ptr parent; //< 上一条词法。 300 | std::shared_ptr child; //< 下一条词法。 301 | }; 302 | //////////////////////////////////////////////////////////////// 词法 end 303 | class End : public Base { 304 | public: 305 | End() : Base(LT_End, {0, 0}) {}; 306 | End(vbin&) : End() {}; 307 | virtual xmsg sig() const { return {}; } 308 | virtual void bin(vbin&) const {} 309 | virtual bool test(const xblk&) const { return true; } 310 | }; 311 | //////////////////////////////////////////////////////////////// 词法 dot 312 | class Dot : public Base { 313 | public: 314 | Dot(const Range& r) : Base(LT_Dot, r) {} 315 | Dot(vbin& bs) : Dot(ErrRange) { bs >> range.Min >> range.Max; } 316 | virtual xmsg sig() const { 317 | #ifdef xsig_need_debug 318 | return xmsg() << '.' << range.sig(); 319 | #else 320 | return {}; 321 | #endif 322 | } 323 | virtual void bin(vbin& bs) const { bs << range.Min << range.Max; } 324 | virtual std::shared_ptr optimize() { 325 | if (!child) return {}; 326 | if (child->type != LT_Dot) return {}; 327 | 328 | // 同是 . ,直接融合,无需构造新对象。 329 | range += child->range; 330 | // 注意断链。 331 | child = child->child; 332 | child->parent = shared_from_this(); 333 | 334 | return {}; 335 | } 336 | virtual bool test(const xblk&) const { return true; } 337 | }; 338 | //////////////////////////////////////////////////////////////// 词法 record 339 | class Record : public Base { 340 | public: 341 | Record(const char f, const std::string& n, const bool b) 342 | : Base(LT_Record, {0, 0}), flag(f), name(n), isoff(b) { 343 | switch (f) { 344 | case 'A': case 'a': range = Range(0); break; 345 | case 'F': case 'f': range = Range(sizeof(uint32_t)); break; 346 | case 'Q': case 'q': range = Range(sizeof(uint64_t)); break; 347 | case 'D': case 'd': range = Range(sizeof(uint32_t)); break; 348 | case 'W': case 'w': range = Range(sizeof(uint16_t)); break; 349 | case 'B': case 'b': range = Range(sizeof(uint8_t)); break; 350 | default: range = Range(0); break; 351 | } 352 | } 353 | Record(vbin& bs) : Base(LT_Record, {0, 0}) { 354 | vbin n; 355 | bs >> flag >> isoff >> n; 356 | name.assign((const char*)n.data(), n.size()); 357 | switch (flag) { 358 | case 'A': case 'a': range = Range(0); break; 359 | case 'F': case 'f': range = Range(sizeof(uint32_t)); break; 360 | case 'Q': case 'q': range = Range(sizeof(uint64_t)); break; 361 | case 'D': case 'd': range = Range(sizeof(uint32_t)); break; 362 | case 'W': case 'w': range = Range(sizeof(uint16_t)); break; 363 | case 'B': case 'b': range = Range(sizeof(uint8_t)); break; 364 | default: range = Range(0); break; 365 | } 366 | } 367 | virtual xmsg sig() const { 368 | #ifdef xsig_need_debug 369 | xmsg ss; 370 | ss << '<'; 371 | if (isoff) ss << '^'; 372 | ss << (char)toupper(flag); 373 | if (!name.empty()) ss << ' ' << name; 374 | ss << '>'; 375 | return ss; 376 | #else 377 | return {}; 378 | #endif 379 | } 380 | virtual void bin(vbin& bs) const { 381 | bs << flag << isoff << name.size() << name; 382 | } 383 | virtual bool test(const xblk&) const { 384 | // 没有需要校验的引用,直接返回 true 。 385 | auto lock = ref.lock(); 386 | if (!lock) return true; 387 | // 无视类型,直接比较。 388 | const auto& r = *(const Record*)lock.get(); 389 | const auto v = pick_value(nullptr); 390 | const auto rv = r.pick_value(nullptr); 391 | xsdbg << " check : " << name << " : " << v.q << " == " << rv.q; 392 | return v.q == rv.q; 393 | } 394 | value pick_value(const void* start) const { 395 | value rv; 396 | rv.q = 0; 397 | switch (flag) { 398 | case 'A': case 'a': { 399 | rv.t = 'p'; 400 | rv.p = match_mem; 401 | if (isoff) rv.p = (void*)((size_t)rv.p - (size_t)start); 402 | return rv; 403 | } 404 | case 'F': case 'f': { 405 | rv.t = 'p'; 406 | const auto off = *(int32_t*)match_mem; 407 | rv.p = (void*)((uint8_t*)match_mem + off + sizeof(off)); 408 | if (isoff) rv.p = (void*)((size_t)rv.p - (size_t)start); 409 | return rv; 410 | } 411 | case 'Q': case 'q': { 412 | rv.t = 'q'; 413 | rv.q = *(uint64_t*)match_mem; 414 | if (isoff) rv.q = rv.q - (uint64_t)start; 415 | return rv; 416 | } 417 | case 'D': case 'd': { 418 | rv.t = 'd'; 419 | rv.d = *(uint32_t*)match_mem; 420 | #ifndef xsig_is_x64 421 | if (isoff) rv.d = rv.d - (uint32_t)start; 422 | #endif 423 | return rv; 424 | } 425 | case 'W': case 'w': { 426 | rv.t = 'w'; 427 | rv.w = *(uint16_t*)match_mem; 428 | return rv; 429 | } 430 | case 'B': case 'b': { 431 | rv.t = 'b'; 432 | rv.b = *(uint8_t*)match_mem; 433 | return rv; 434 | } 435 | default: rv.t = 'n'; return rv; 436 | } 437 | } 438 | 439 | public: 440 | char flag; 441 | std::string name; 442 | bool isoff; 443 | std::weak_ptr ref; 444 | }; 445 | //////////////////////////////////////////////////////////////// 词法 hexs 446 | class Hexs : public Base { 447 | public: 448 | Hexs(const std::string& s) : Base(LT_Hexs, Range(s.size())), str(s) {} 449 | Hexs(vbin& bs) : Base(LT_Hexs, {0, 0}) { 450 | vbin n; 451 | bs >> n; 452 | str.assign((const char*)n.data(), n.size()); 453 | range = Range(str.size()); 454 | } 455 | virtual xmsg sig() const { 456 | #ifdef xsig_need_debug 457 | return xmsg() << bin2hex(str, true); 458 | #else 459 | return {}; 460 | #endif 461 | } 462 | virtual void bin(vbin& bs) const { bs << str.size() << str; } 463 | virtual std::shared_ptr optimize() { 464 | if (!child) return {}; 465 | if (child->type != LT_Hexs) return {}; 466 | 467 | const auto& o = *(const Hexs*)child.get(); 468 | str.append(o.str); 469 | range = Range(str.size()); 470 | 471 | child = child->child; 472 | child->parent = shared_from_this(); 473 | 474 | return {}; 475 | } 476 | virtual bool test(const xblk& blk) const { 477 | xsdbg << " matching string : \r\n" 478 | << " | " << bin2hex(str, true) << "\r\n" 479 | << " " << blk.begin() << " | " 480 | << bin2hex(blk.begin(), blk.size(), true); 481 | if (blk.size() != str.size()) return false; 482 | return memcmp(str.data(), blk.begin(), str.size()) == 0; 483 | } 484 | 485 | public: 486 | std::string str; 487 | }; 488 | //////////////////////////////////////////////////////////////// 非词法 sets 489 | class Sets : public Base { 490 | public: 491 | Sets() : Base(LT_Sets, {0, 0}) {} 492 | Sets(const std::vector& mods, 493 | const std::vector& blks, 494 | const std::vector& cfgs) 495 | : Base(LT_Sets, {0, 0}), _mods(mods), _blks(blks), _cfgs(cfgs) {} 496 | Sets(vbin& bs) : Base(LT_Sets, {0, 0}) { 497 | size_t c = 0; 498 | vbin s; 499 | bs >> c; 500 | for (size_t i = 0; i < c; ++i ) { 501 | bs >> s; 502 | _mods.push_back(std::string((const char*)s.data(), s.size())); 503 | } 504 | bs >> c; 505 | for (size_t i = 0; i < c; ++i) { 506 | void* start = nullptr; 507 | void* end = nullptr; 508 | bs >> start >> end; 509 | _blks.push_back(xblk(start, end)); 510 | } 511 | bs >> c; 512 | for (size_t i = 0; i < c; ++i) { 513 | bs >> s; 514 | _cfgs.push_back(std::string((const char*)s.data(), s.size())); 515 | } 516 | } 517 | virtual xmsg sig() const { 518 | xmsg msg; 519 | msg << '@'; 520 | for (const auto& v : _mods) { 521 | msg << v << ','; 522 | } 523 | for (const auto& v : _blks) { 524 | msg << v.begin() << ',' << v.end() << ','; 525 | } 526 | for (const auto& v : _cfgs) { 527 | msg << v << ','; 528 | } 529 | return msg; 530 | } 531 | virtual void bin(vbin& bs) const { 532 | bs << _mods.size(); 533 | for (const auto& v : _mods) { 534 | bs << v.size() << v; 535 | } 536 | bs << _blks.size(); 537 | for (const auto& v : _blks) { 538 | bs << v.begin() << v.end(); 539 | } 540 | bs << _cfgs.size(); 541 | for (const auto& v : _cfgs) { 542 | bs << v.size() << v; 543 | } 544 | } 545 | virtual bool test(const xblk&) const { return true; } 546 | 547 | public: 548 | std::vector _mods; 549 | std::vector _blks; 550 | std::vector _cfgs; 551 | }; 552 | }; 553 | 554 | public: 555 | using Blks = std::vector; 556 | using Reports = std::map; 557 | 558 | private: 559 | static inline const auto gk_separation_line = 560 | "---------------------------------------------------------------- "; 561 | /// 添加一个词法。 562 | void add_lex(std::shared_ptr o) { 563 | if (_lex) return _lex->push_back(o); 564 | _lex = o; 565 | if (o->parent.lock()) xserr << "add_lex has parent !"; 566 | } 567 | //////////////////////////////////////////////////////////////// 词法 hex 568 | ///识别函数 569 | /// 匹配 hex 词法,返回值 < 0 表示非此词法。 570 | static inline char match_hex(Sign& sig) { 571 | const auto ch = sig(); 572 | if (!isxdigit(ch)) return -1; 573 | ++sig; 574 | const char hex = ch & 0x0F; 575 | return (ch > '9') ? hex + 0x09 : hex; 576 | } 577 | //////////////////////////////////////////////////////////////// 词法 range 578 | ///提取函数 579 | /// 匹配词法 range_value ,返回值 < 0 表示非此词法。 580 | static inline Range::Type match_range_value(Sign& sig) { 581 | auto hex = match_hex(sig); 582 | if (hex < 0) return Range::ErrType; 583 | Range::Type r = hex; 584 | for (size_t i = 1; i < (2 * sizeof(Range::Type)); ++i) { 585 | hex = match_hex(sig); 586 | if (hex < 0) return r; 587 | r = (r << 4) | hex; 588 | } 589 | // 其实还应该判断转换后是否超出 intprt_t ,但不好返回,也不好定位。 590 | // 考虑到特征码这么写本来就离谱,这种情况就不处理了。 591 | return r; 592 | } 593 | /// 匹配词法 range ,返回值 == ErrRange 时,匹配错误。 594 | static inline Range match_range(Sign& sig) { 595 | switch (sig()) { 596 | case '*': ++sig; return {0, Range::MaxType}; 597 | case '+': ++sig; return {1, Range::MaxType}; 598 | case '?': ++sig; return {0, 1}; 599 | case '{': ++sig; break; 600 | default: return {1, 1}; 601 | } 602 | // 有尝试使用正则语法匹配,但发现符合要求的正则不好实现。故这里放弃,为记。 603 | while (isblank(sig())) ++sig; 604 | 605 | auto Min = match_range_value(sig); 606 | if (Min < 0) { 607 | xserr << *sig << " range.min lost !"; 608 | return ErrRange; 609 | } 610 | while (isblank(sig())) ++sig; 611 | if ('}' == sig()) { 612 | ++sig; 613 | return Range(Min); 614 | } 615 | if (',' != sig()) { // 不存在 N 值且无分隔符,非法 {} 。 616 | xserr << *sig << " range need ',' !"; 617 | return ErrRange; 618 | } 619 | ++sig; 620 | 621 | while (isblank(sig())) ++sig; 622 | 623 | auto Max = match_range_value(sig); 624 | if (Min < 0) { 625 | xserr << *sig << " range.max lost !"; 626 | return ErrRange; 627 | } 628 | 629 | while (isblank(sig())) ++sig; 630 | 631 | if (sig() != '}') { 632 | xserr << *sig << " range mis '}' end/illegal char/out-max !"; 633 | return ErrRange; 634 | } 635 | ++sig; 636 | 637 | if (Min == 0 && Max == 0) { 638 | xserr << *sig << " illegal range = {0, 0} !"; 639 | return ErrRange; 640 | } 641 | 642 | seqswap(Min, Max); 643 | // xsdbg.prt(" make range = { %tX, %tX }", Min, Max); 644 | return Range(Min, Max); 645 | } 646 | /// 匹配设置 sets 。 647 | static inline std::shared_ptr match_sets(Sign& sig) { 648 | std::string data; 649 | while ('\0' != sig()) { 650 | data.push_back(sig()); 651 | ++sig; 652 | } 653 | data.push_back(','); 654 | 655 | std::vector vec; 656 | auto ds = data.begin(); 657 | for (size_t s = 0, e = 0; e != data.size();) { 658 | e = data.find_first_of(',', s); 659 | if (data.npos == e) break; 660 | 661 | auto its = ds + s; 662 | auto ite = ds + e; 663 | 664 | ++e; 665 | s = e; // 注意设定下个起始位。 666 | 667 | // 前缀空白丢弃。 668 | for (; its != ite; ++its) { 669 | if (!isspace(*its)) break; 670 | } 671 | // 后缀空白丢弃。 672 | for (; ite != its; --ite) { 673 | if (!isspace(*(ite - 1))) break; 674 | } 675 | // 空串丢弃。 676 | if (its == ite) continue; 677 | 678 | vec.emplace_back(std::string(its, ite)); 679 | } 680 | 681 | data.clear(); 682 | auto sets = std::make_shared(); 683 | 684 | std::vector bs; 685 | for (const auto& v : vec) { 686 | if (':' == *v.begin()) { 687 | sets->_cfgs.push_back(v); 688 | continue; 689 | } 690 | bool bhex = true; 691 | for (const auto ch : v) { 692 | if (!isxdigit(ch)) { 693 | bhex = false; 694 | break; 695 | } 696 | } 697 | if (!bhex) { 698 | sets->_mods.push_back(v); 699 | continue; 700 | } 701 | try { 702 | #ifdef xsig_is_x64 703 | const auto u = std::stoull(v, 0, 16); 704 | #else 705 | const auto u = std::stoul(v, 0, 16); 706 | #endif 707 | bs.push_back(u); 708 | if (0 == (bs.size() % 2)) { 709 | sets->_blks.push_back(xblk((void*)*(bs.rbegin() + 1), u)); 710 | } 711 | } catch (...) { 712 | xserr << *sig << " : " << v << " stoull error !"; 713 | return {}; 714 | } 715 | } 716 | if (0 != (bs.size() % 2)) { 717 | xserr << *sig << " : blks no pair !"; 718 | return {}; 719 | } 720 | return sets; 721 | } 722 | //////////////////////////////////////////////////////////////// 一次词法识别 723 | /// 一次词法识别。返回 false 表示 失败 或 结束。 724 | bool make_lex(Sign& sig) { 725 | const auto pos = *sig; 726 | uint8_t hex = sig() & 0xF; 727 | switch (sig()) { 728 | //////////////////////////////////////////////////////////////// 词法 end 识别逻辑 729 | // 一律返回 false 。 730 | case '\0': { 731 | ++sig; 732 | xsdbg << pos << " Lexical end"; 733 | add_lex(std::make_shared()); 734 | return false; 735 | } 736 | //////////////////////////////////////////////////////////////// 词法 ws 识别逻辑。 737 | // 只是跳过 ws ,一律返回 true 。 738 | case ' ': case '\t': case '\n': case '\r': ++sig; return true; 739 | case '@': { 740 | if (_lex) { 741 | xserr << pos << "@ must first character !"; 742 | return false; 743 | } 744 | ++sig; 745 | const auto lex = match_sets(sig); 746 | if (!lex) return false; 747 | add_lex(lex); 748 | return true; 749 | } 750 | //////////////////////////////////////////////////////////////// 词法 note 识别逻辑 751 | // 只是跳过 note ,一律返回 true 。 752 | case '#': { 753 | ++sig; 754 | while (sig() != '\n' && sig() != '\0') ++sig; 755 | xsdbg << pos << " Lexical note"; 756 | return true; 757 | } 758 | //////////////////////////////////////////////////////////////// 词法 dot 识别逻辑 759 | case '.': { 760 | ++sig; 761 | const auto range = match_range(sig); 762 | if (ErrRange == range) return false; 763 | 764 | xsdbg << pos << " Lexical dot ." << range.sig(); 765 | add_lex(std::make_shared(range)); 766 | return true; 767 | } 768 | //////////////////////////////////////////////////////////////// 词法 Record 识别逻辑 769 | case '<': { 770 | ++sig; 771 | 772 | const bool offset = sig() == '^'; 773 | if (offset) ++sig; 774 | 775 | const auto t = sig(); 776 | switch (t) { 777 | case 'A': case 'a': break; 778 | case 'F': case 'f': break; 779 | case 'Q': case 'q': break; 780 | case 'D': case 'd': 781 | #ifndef xsig_is_x64 782 | break; 783 | #endif 784 | case 'W': case 'w': 785 | case 'B': case 'b': 786 | if (offset) { 787 | xserr << pos << " record ^" << t << " not allow !"; 788 | return false; 789 | } 790 | break; 791 | default: 792 | xserr << pos << " record need [AFQDWB] !"; 793 | return false; 794 | } 795 | ++sig; 796 | // 去除前缀空白符。 797 | while (isblank(sig())) ++sig; 798 | 799 | std::string name; 800 | constexpr auto lc = '<'; 801 | constexpr auto rc = '>'; 802 | for (size_t needc = 1; 0 != needc;) { 803 | const auto c = sig(); 804 | switch (c) { 805 | // 不允许分行 或 突然结束。 806 | case '\r': case '\n': case '\0': 807 | xserr << pos << " record need end by '>'"; 808 | return false; 809 | // 允许嵌套。 810 | case lc: ++needc; name.push_back(c); ++sig; break; 811 | case rc: --needc; name.push_back(c); ++sig; break; 812 | default: name.push_back(c); ++sig; break; 813 | } 814 | } 815 | // 前面的简单逻辑令 > 总是被加入,这里删除之。 816 | name.pop_back(); 817 | // 删除后缀空白。 818 | while (isblank(*name.rbegin())) name.pop_back(); 819 | 820 | auto lex = std::make_shared(t, name, offset); 821 | 822 | // 插入前先查询是否存在同名 record ,做引用。空名不做引用。 823 | if (!name.empty()) 824 | for (auto x = _lex; x; x = x->child) { 825 | if (Lexical::LT_Record != x->type) continue; 826 | auto& xx = *(const Lexical::Record*)x.get(); 827 | if (xx.name.empty()) continue; 828 | if (xx.name == name) { 829 | lex->ref = x; 830 | break; 831 | } 832 | } 833 | 834 | xsdbg << pos << " Lexical record " << lex->sig() 835 | << ((lex->ref.lock()) ? " Has ref*" : ""); 836 | add_lex(lex); 837 | return true; 838 | } 839 | case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': 840 | case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': 841 | hex += 9; // 注意这里没有 break 。 842 | case '0': case '1': case '2': case '3': case '4': case '5': 843 | case '6': case '7': case '8': case '9': { 844 | hex <<= 4; 845 | ++sig; 846 | const auto c = match_hex(sig); 847 | if (c < 0) { 848 | xsdbg << pos << "hexhex unpaired !"; 849 | return false; 850 | } 851 | hex |= c; 852 | // TODO: 暂不支持 &|- ,也不支持范围。 853 | xsdbg << pos << " Lexical string " << hex; 854 | add_lex(std::make_shared(std::string(1, hex))); 855 | return true; 856 | } 857 | default: 858 | xserr << *sig << " unknow lexcial !"; 859 | return false; 860 | } 861 | } 862 | 863 | public: 864 | /// 返回对象的词法是否有效。非空,且以 End 结尾。 865 | bool valid() const { 866 | if (!_lex) return false; 867 | // 故意直接取子结点, 而放弃 _lex 的检查。 868 | for (auto x = _lex->child; x; x = x->child) { 869 | if (Lexical::LT_End == x->type && !x->child) return true; 870 | } 871 | return false; 872 | } 873 | /// 特征码串生成 特征码词法组。 874 | bool make_lexs(const char* const s) { 875 | _lex.reset(); 876 | 877 | Sign sig(s); 878 | xsdbg << gk_separation_line << "lexical..."; 879 | while (make_lex(sig)) 880 | ; 881 | xsdbg << gk_separation_line << "lexical done."; 882 | if (!valid()) return false; 883 | 884 | xsdbg << gk_separation_line << "optimization..."; 885 | auto newo = _lex->optimizes(); 886 | if (newo) _lex = newo; 887 | xsdbg << gk_separation_line << "optimization done."; 888 | if (!valid()) return false; 889 | 890 | xsdbg << "```SIG"; 891 | for (auto lex = _lex; lex; lex = lex->child) { 892 | xsdbg << lex->sig(); 893 | } 894 | xsdbg << "```"; 895 | 896 | return true; 897 | } 898 | 899 | public: 900 | //////////////////////////////////////////////////////////////// BM 算法 901 | /* 902 | 网上有不少算法经实验有 BUG 。 903 | 904 | 只有 一个 GS 好后缀表 的那种算法。 905 | 模式串 0000C745FC00000000E8 在匹配 FF50C745E800000000E80000C745FC00000000E8 时,将陷入死循环。 906 | */ 907 | class BM { 908 | public: 909 | /// 不允许默认构造。 910 | BM() = delete; 911 | 912 | public: 913 | /// 用 模式串初始化对象。 914 | BM(const std::string& pat) : _pattern(pat) { 915 | const auto pattern = (const uint8_t*)_pattern.data(); 916 | const intptr_t pattern_len = _pattern.size(); 917 | 918 | // 计算坏字符表,坏字符表最大是 256 。 919 | _bad_char.reset(new intptr_t[0x100]()); 920 | const auto bad_char = _bad_char.get(); 921 | 922 | memset(bad_char, -1, 0x100 * sizeof(intptr_t)); 923 | 924 | for (intptr_t i = 0; i < pattern_len; ++i) bad_char[pattern[i]] = i; 925 | 926 | // 计算好后缀表。好后缀表最大不超过模式串大小。 927 | _suffix.reset(new intptr_t[pattern_len]()); 928 | const auto suffix = _suffix.get(); 929 | _prefix.reset(new bool[pattern_len]()); 930 | const auto prefix = _prefix.get(); 931 | 932 | memset(suffix, -1, pattern_len * sizeof(intptr_t)); 933 | memset(prefix, false, pattern_len * sizeof(bool)); 934 | 935 | for (intptr_t i = 0; i < pattern_len - 1; ++i) { 936 | auto j = i; 937 | intptr_t k = 0; 938 | while (j >= 0 && pattern[j] == pattern[pattern_len - 1 - k]) { 939 | --j; 940 | ++k; 941 | suffix[k] = j + 1; 942 | } 943 | if (j == -1) prefix[k] = true; 944 | } 945 | } 946 | 947 | public: 948 | intptr_t operator()(const uint8_t* mem, const intptr_t size) { 949 | const auto pattern = (const uint8_t*)_pattern.data(); 950 | const intptr_t pattern_len = _pattern.size(); 951 | 952 | const auto bad_char = _bad_char.get(); 953 | const auto suffix = _suffix.get(); 954 | const auto prefix = _prefix.get(); 955 | 956 | const auto match_suffix = [&](const intptr_t j) { 957 | const intptr_t k = pattern_len - 1 - j; 958 | if (suffix[k] != -1) { 959 | return j - suffix[k] + 1; 960 | } 961 | for (intptr_t r = j + 2; r <= pattern_len - 1; ++r) { 962 | if (prefix[pattern_len - r]) return r; 963 | } 964 | return pattern_len; 965 | }; 966 | 967 | intptr_t mem_pos = 0; 968 | while (mem_pos <= size - pattern_len) { 969 | intptr_t pat_pos = pattern_len - 1; 970 | 971 | while (pat_pos >= 0 && mem[mem_pos + pat_pos] == pattern[pat_pos]) 972 | --pat_pos; 973 | 974 | if (pat_pos < 0) return mem_pos; 975 | 976 | intptr_t x = pat_pos - bad_char[mem[mem_pos + pat_pos]]; 977 | intptr_t y = 0; 978 | if (pat_pos < pattern_len - 1) y = match_suffix(pat_pos); 979 | if (x < 0 || y < 0) 980 | xsdbg << "=== " << (uint64_t)pat_pos << " " << (uint64_t)mem_pos 981 | << " " << x << " " << y; 982 | mem_pos += std::minmax(x, y).second; // 避免 max 宏干扰。 983 | } 984 | return (intptr_t)-1; 985 | } 986 | 987 | public: 988 | const std::string _pattern; 989 | 990 | private: 991 | std::unique_ptr _bad_char; 992 | std::unique_ptr _suffix; 993 | std::unique_ptr _prefix; 994 | }; 995 | 996 | public: 997 | //////////////////////////////////////////////////////////////// match with preprocess 998 | /** 999 | 加入 预处理 的匹配。 1000 | 1001 | 1. 找到最长的 hexs 串。 SS 1002 | 1. 确定 SS 前 词法匹配范围最大值的总和。 LA 1003 | 1. 确定 SS 后 词法匹配范围最大值的总和。 LB 1004 | 1. BM 算法扫描 全块,匹配 SS 。 得到匹配位置。 MM 1005 | 1. 重新给出块 {MM - LA, MM + SS.size() + LB} 1006 | */ 1007 | bool match_with_preprocess(const xblk& blk) { 1008 | std::shared_ptr lex; 1009 | std::string ss; 1010 | for (auto x = _lex; x; x = x->child) { 1011 | if (x->type != Lexical::LT_Hexs) continue; 1012 | auto& o = *(Lexical::Hexs*)x.get(); 1013 | if (o.str.size() <= ss.size()) continue; 1014 | lex = x; 1015 | ss = o.str; 1016 | } 1017 | xsdbg << "max string is " << bin2hex(ss, true); 1018 | // 找不到最长 hexs 串的情况,虽然很离谱,但也处理一下。 1019 | if (!lex) return match_core(blk); 1020 | 1021 | intptr_t LA = 0; 1022 | for (auto x = lex->parent.lock(); x; x = x->parent.lock()) { 1023 | const auto k = LA + x->range.Max; 1024 | LA = (k >= LA) ? k : Range::MaxType; 1025 | } 1026 | xsdbg << "LA = " << (uint64_t)LA; 1027 | 1028 | intptr_t LB = 0; 1029 | for (auto x = lex; x; x = x->child) { 1030 | const auto k = LB + x->range.Max; 1031 | LB = (k >= LB) ? k : Range::MaxType; 1032 | } 1033 | xsdbg << "LB = " << (uint64_t)LB; 1034 | 1035 | BM bm(ss); 1036 | intptr_t lp = 0; 1037 | while (lp < (intptr_t)blk.size()) { 1038 | xsdbg << "start lp " << (uint64_t)lp; 1039 | const auto MM = bm((const uint8_t*)blk.begin() + lp, blk.size() - lp); 1040 | xsdbg << " MM " << (uint64_t)MM; 1041 | if (MM < 0) return false; 1042 | auto a = (size_t)blk.begin() + lp + MM - LA; 1043 | auto b = (size_t)blk.begin() + lp + MM + LB; 1044 | if (match_core(xblk((const void*)a, (const void*)b))) return true; 1045 | lp += MM + 1; 1046 | } 1047 | 1048 | return false; 1049 | } 1050 | //////////////////////////////////////////////////////////////// match 内核 1051 | /// 匹配内核。朴素匹配。 1052 | bool match_core(const xblk& blk) { 1053 | try { 1054 | xsdbg << gk_separation_line << "match... " << blk.begin() << " - " 1055 | << blk.end(); 1056 | 1057 | intptr_t lp = 0; 1058 | Range fixRange(0); 1059 | for (auto lex = _lex; lex; lex = lex->child) { 1060 | fixRange += lex->range; 1061 | lex->reset_match(); 1062 | } 1063 | xsdbg << "match need : " << fixRange.Min << " - " << fixRange.Max; 1064 | if (xblk::WholeIn != 1065 | blk.check(xblk((void*)((size_t)blk.begin() + lp), fixRange.Min))) { 1066 | xsdbg << "rest mem not enough"; 1067 | return false; 1068 | } 1069 | 1070 | for (auto lex = _lex; lex;) { 1071 | const auto r = lex->match(blk, lp); 1072 | // 匹配成功,继续。 1073 | if (r >= 0) { 1074 | lp += r; 1075 | lex = lex->child; 1076 | continue; 1077 | } 1078 | // 匹配彻底失败,跳出。 1079 | if (r != -1) { 1080 | xsdbg << gk_separation_line << "match fail"; 1081 | return false; 1082 | } 1083 | // 逐步回退到未能最大匹配的特征。 1084 | for (; lex; lex = lex->parent.lock()) { 1085 | const auto c = lex->match_count; 1086 | if (c >= lex->range.Min) { 1087 | if (c < lex->range.Max) break; 1088 | xsdbg << "back " << c; 1089 | lp -= c; 1090 | } 1091 | lex->reset_match(); 1092 | } 1093 | 1094 | if (lex) continue; 1095 | // 前面所有特征都达到了最大匹配,无法回退。则 回到顶,递增继续。 1096 | xsdbg << "reset and inc..."; 1097 | ++lp; 1098 | lex = _lex; 1099 | } 1100 | 1101 | xsdbg << gk_separation_line << "match done"; 1102 | return true; 1103 | } catch (...) { 1104 | xserr << gk_separation_line << "match error !"; 1105 | return false; 1106 | } 1107 | } 1108 | /// 指定块组,匹配特征。 1109 | bool match(const Blks blks) { 1110 | auto match_func = 1111 | exmatch ? &xsig::match_with_preprocess : &xsig::match_core; 1112 | for (const auto& blk : blks) { 1113 | if ((this->*match_func)(blk)) return true; 1114 | } 1115 | return false; 1116 | } 1117 | /// 提取特征匹配结果。 1118 | Reports report(const void* start) const { 1119 | Reports reps; 1120 | if (!valid()) { 1121 | xserr << "xsig invalid, no report !"; 1122 | return reps; 1123 | } 1124 | int inoname = 0; 1125 | for (auto lex = _lex; lex; lex = lex->child) { 1126 | if (Lexical::LT_Record != lex->type) continue; 1127 | const auto& r = *(const Lexical::Record*)lex.get(); 1128 | auto name = r.name; 1129 | if (name.empty()) name.assign(xmsg().prt("noname%d", inoname++).toas()); 1130 | reps.insert({name, r.pick_value(start)}); 1131 | } 1132 | if (reps.empty()) { 1133 | value v; 1134 | v.t = 'p'; 1135 | v.p = _lex->match_mem; 1136 | reps.insert({"noname", v}); 1137 | } 1138 | return reps; 1139 | } 1140 | /// 转换为二进制。 1141 | vbin to_bin() const { 1142 | vbin bs; 1143 | if (!valid()) { 1144 | xserr << "xsig invalid, no bin !"; 1145 | return bs; 1146 | } 1147 | for (auto lex = _lex; lex; lex = lex->child) { 1148 | lex->bins(bs); 1149 | } 1150 | return bs; 1151 | } 1152 | /// 从二进制读取。 1153 | bool from_bin(vbin& bs) { 1154 | _lex.reset(); 1155 | std::shared_ptr lex; 1156 | try { 1157 | while (!bs.empty()) { 1158 | lex.reset(); 1159 | Lexical::Type t; 1160 | bs >> t; 1161 | 1162 | Range range(0); 1163 | vbin s; 1164 | switch (t) { 1165 | case Lexical::LT_End: 1166 | lex = std::make_shared(); 1167 | break; 1168 | case Lexical::LT_Dot: 1169 | bs >> range.Min >> range.Max; 1170 | lex = std::make_shared(range); 1171 | break; 1172 | case Lexical::LT_Record: { 1173 | char f; 1174 | bool b; 1175 | bs >> f >> b >> s; 1176 | lex = std::make_shared( 1177 | f, std::string((const char*)s.data(), s.size()), b); 1178 | break; 1179 | } 1180 | case Lexical::LT_Hexs: { 1181 | bs >> s; 1182 | lex = std::make_shared( 1183 | std::string((const char*)s.data(), s.size())); 1184 | break; 1185 | } 1186 | case Lexical::LT_Sets: { 1187 | size_t c = 0; 1188 | std::vector mods; 1189 | bs >> c; 1190 | for (size_t i = 0; i < c; ++i) { 1191 | bs >> s; 1192 | mods.push_back(std::string((const char*)s.data(), s.size())); 1193 | } 1194 | std::vector blks; 1195 | bs >> c; 1196 | for (size_t i = 0; i < c; ++i) { 1197 | void* start = nullptr; 1198 | void* end = nullptr; 1199 | bs >> start >> end; 1200 | blks.push_back(xblk(start, end)); 1201 | } 1202 | std::vector cfgs; 1203 | bs >> c; 1204 | for (size_t i = 0; i < c; ++i) { 1205 | bs >> s; 1206 | cfgs.push_back(std::string((const char*)s.data(), s.size())); 1207 | } 1208 | lex = std::make_shared(mods, blks, cfgs); 1209 | break; 1210 | } 1211 | default: { 1212 | xserr << "Unknow type : " << (uint8_t)t; 1213 | return false; 1214 | } 1215 | } 1216 | add_lex(lex); 1217 | xsdbg << lex->sig(); 1218 | if (t == Lexical::LT_End) { 1219 | if (lex->parent.lock()) return true; 1220 | xserr << "bins empty !"; 1221 | return false; 1222 | } 1223 | } 1224 | xserr << "No End !"; 1225 | } catch (...) { 1226 | xserr << XTEXT("xsig::from_bin exception !"); 1227 | } 1228 | return false; 1229 | } 1230 | std::shared_ptr get_sets() const { 1231 | if (!_lex) return std::shared_ptr(); 1232 | if (Lexical::LT_Sets != _lex->type) return std::shared_ptr(); 1233 | 1234 | const auto lex = (const Lexical::Sets*)_lex.get(); 1235 | 1236 | auto ret = std::make_shared(); 1237 | ret->_mods = lex->_mods; 1238 | ret->_blks = lex->_blks; 1239 | ret->_cfgs = lex->_cfgs; 1240 | 1241 | return ret; 1242 | } 1243 | 1244 | public: 1245 | /// 指定块,检查内存可读。注意到:有些模块可读范围可能中断,导致匹配异常。 1246 | static inline Blks check_blk(const xblk& blk) { 1247 | #ifndef _WIN32 1248 | // 非 windows 暂不确定如何判断内存可读。 1249 | return {blk}; 1250 | #else 1251 | // 内存可读直接返回。 1252 | if (FALSE == IsBadReadPtr(blk.begin(), blk.size())) { 1253 | // xsdbg << "kk : " << blk.begin() << " - " << blk.end(); 1254 | return {blk}; 1255 | } 1256 | // 1 byte 都不可读,直接返回空。 1257 | if (blk.size() <= 1) return {}; 1258 | // 否则按 二分法 切片 递归 判断。 1259 | Blks blks; 1260 | 1261 | const size_t asize = blk.size() / 2; 1262 | // xsdbg << "a>>" << blk.begin() << " : " << asize; 1263 | for (const auto& v : check_blk(xblk(blk.begin(), asize))) blks.push_back(v); 1264 | 1265 | auto bsize = blk.size() - asize; 1266 | // xsdbg << "b>>" << (void*)((size_t)blk.begin() + asize) << " : " << bsize; 1267 | for (const auto& v : 1268 | check_blk(xblk((void*)((size_t)blk.begin() + asize), bsize))) 1269 | blks.push_back(v); 1270 | 1271 | if (blks.empty()) return blks; 1272 | 1273 | // 优化,整合连续的块。 1274 | auto opts(std::move(blks)); 1275 | 1276 | auto it = opts.begin(); 1277 | auto as = (*it).begin(); 1278 | auto ae = (*it).end(); 1279 | ++it; 1280 | // xsdbg << "a " << as << " - " << ae; 1281 | while (it != opts.end()) { 1282 | const auto bs = (*it).begin(); 1283 | const auto be = (*it).end(); 1284 | ++it; 1285 | // xsdbg << "b " << bs << " - " << be; 1286 | 1287 | if (ae == bs) { 1288 | ae = be; 1289 | // xsdbg << "++ : " << as << " - " << ae; 1290 | } else { 1291 | // xsdbg << "c>> : " << as << " - " << ae; 1292 | blks.push_back(xblk(as, ae)); 1293 | as = bs; 1294 | ae = be; 1295 | } 1296 | } 1297 | // xsdbg << "d>> : " << as << " - " << ae; 1298 | blks.push_back(xblk(as, ae)); 1299 | return blks; 1300 | #endif 1301 | } 1302 | /// 读取特征码串。要求多段特征码串,以 单行 / 分隔。 1303 | static inline std::vector read_sig(const std::string& _data) { 1304 | std::vector sigs; 1305 | /* 1306 | 注意到,这里不适合用 正则表达式分割文本。 1307 | 1308 | - 加前缀 \n 是应对起始 / 的情况,可以组成 \n/ 。 1309 | - 起始为 / 时,可使起始简单形成一个 空串。空串被忽略。 1310 | - 起始非 / 时,\n 被加入 文本串,无影响。 1311 | - 加后缀 \n/\n 是应对无 / 结尾的情况。 1312 | - 结尾为 \n/ 时,使结尾简单形成一个 空串。空串被忽略。 1313 | - 结尾非 /n/ 时,用于标记结尾,无影响。 1314 | */ 1315 | const std::string data = "\n" + _data + "\n/\n"; 1316 | auto ds = data.begin(); 1317 | for (size_t s = 0, e = 0, p = 0; e != data.size(); p = e + 2) { 1318 | e = data.find("\n/", p); 1319 | if (data.npos == e) break; 1320 | 1321 | auto its = ds + s; 1322 | auto ite = ds + e; 1323 | 1324 | // 如果不是单行 / ,则视为 sig 内容,继续。 1325 | // 注意,因为加了后缀,itn 不可能为 end() 。 1326 | auto itn = ite + 2; 1327 | if ('\r' == *itn) ++itn; 1328 | if ('\n' != *itn) continue; 1329 | 1330 | s = e + 2; // 注意设定下个起始位。 1331 | 1332 | // 前缀空白丢弃。 1333 | for (; its != ite; ++its) { 1334 | if (!isspace(*its)) break; 1335 | } 1336 | // 后缀空白丢弃。 1337 | for (; ite != its; --ite) { 1338 | if (!isspace(*(ite - 1))) break; 1339 | } 1340 | // 空串丢弃。 1341 | if (its == ite) continue; 1342 | 1343 | sigs.emplace_back(std::string(its, ite)); 1344 | } 1345 | 1346 | return sigs; 1347 | } 1348 | /// 读取特征码文件。 1349 | static inline std::vector read_sig_file( 1350 | const std::filesystem::path& path) { 1351 | std::vector sigs; 1352 | std::ifstream file; 1353 | file.open(path, std::ios_base::in | std::ios_base::binary); 1354 | if (!file) { 1355 | xserr << "open sig file fail !"; 1356 | return sigs; 1357 | } 1358 | 1359 | file.seekg(0, std::ios_base::end); 1360 | const size_t filelen = (size_t)file.tellg(); 1361 | if (0 == filelen) { 1362 | xserr << "sig file empty !"; 1363 | return sigs; 1364 | } 1365 | file.seekg(0, std::ios_base::beg); 1366 | std::string data; 1367 | data.resize(filelen); 1368 | file.read((char*)data.data(), filelen); 1369 | file.close(); 1370 | 1371 | if (data.size() >= 3 && "\xEF\xBB\xBF" == data.substr(0, 3)) { 1372 | data.erase(data.begin(), data.begin() + 3); 1373 | } 1374 | 1375 | return read_sig(data); 1376 | } 1377 | 1378 | private: 1379 | std::shared_ptr _lex; //< 特征码起始词法。是一个双向链表。 1380 | public: 1381 | #ifdef xsig_need_debug 1382 | static inline bool dbglog = false; //< 指示是否输出 debug 信息。 1383 | #endif 1384 | static inline bool exmatch = true; //< match 函数使用 预处理。 1385 | }; 1386 | #undef xsig_is_x64 1387 | #undef xserr 1388 | #undef xsdbg 1389 | #undef xslog 1390 | 1391 | } // namespace xlib 1392 | 1393 | #endif // _XLIB_XSIG_H_ -------------------------------------------------------------------------------- /xswap.cc: -------------------------------------------------------------------------------- 1 | #include "xswap.h" 2 | 3 | #include "xlib_test.h" 4 | 5 | enum xswap_test_enum { 6 | xte_0, 7 | xte_1, 8 | }; 9 | 10 | SHOW_TEST_INIT(xswap) 11 | 12 | SHOW_TEST_HEAD(bswap int8_t); 13 | done = xlib::bswap((int8_t)0x12) == 0x12; 14 | SHOW_TEST_RESULT; 15 | 16 | SHOW_TEST_HEAD(bswap uint16_t); 17 | done = xlib::bswap((uint16_t)0x1234) == 0x3412; 18 | SHOW_TEST_RESULT; 19 | 20 | SHOW_TEST_HEAD(bswap int32_t); 21 | done = xlib::bswap((int32_t)0x12345678) == 0x78563412; 22 | SHOW_TEST_RESULT; 23 | 24 | SHOW_TEST_HEAD(bswap uint64_t); 25 | done = xlib::bswap((uint64_t)0x1234567812345678) == 0x7856341278563412; 26 | SHOW_TEST_RESULT; 27 | 28 | SHOW_TEST_HEAD(bswap enum); 29 | done = xlib::bswap(xte_0) == xte_0; 30 | SHOW_TEST_RESULT; 31 | 32 | SHOW_TEST_HEAD(seqswap); 33 | void* a = (void*)0x2; 34 | void* b = (void*)0x1; 35 | done = xlib::seqswap(a, b); 36 | SHOW_TEST_RESULT; 37 | 38 | SHOW_TEST_DONE; -------------------------------------------------------------------------------- /xswap.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file xswap.h 3 | \brief 定义了 swap 的相关模板。 4 | 5 | \version 2.2.0.230224 6 | 7 | \author triones 8 | \date 2014-01-07 9 | 10 | \details bswap 在开启完全优化情况下,会被编译成只有一条 bswap 的汇编指令。 11 | 12 | \section history 版本记录 13 | 14 | - 2014-01-07 从 mkmem 移植入 xlib ,虽然 C++0x 可以实现模版定义与实现分离,但不适用于 LIB ,为记。 1.0 。 15 | - 2016-11-14 适配 Linux g++ 。 1.1 。 16 | - 2019-09-20 重构 bswap 。 2.0 。 17 | - 2019-11-05 升级声明。 2.1 。 18 | - 2021-08-05 升级定义。 2.2 。 19 | */ 20 | #ifndef _XLIB_XSWAP_H_ 21 | #define _XLIB_XSWAP_H_ 22 | 23 | #include 24 | #include 25 | 26 | namespace xlib { 27 | 28 | #ifdef _WIN32 29 | #define xbswap16 _byteswap_ushort 30 | #define xbswap32 _byteswap_ulong 31 | #define xbswap64 _byteswap_uint64 32 | #else 33 | #define xbswap16 __builtin_bswap16 34 | #define xbswap32 __builtin_bswap32 35 | #define xbswap64 __builtin_bswap64 36 | #endif 37 | 38 | /** 39 | 用于翻转数值。 40 | \param values 数值。 41 | \return 翻转后的原类型数值。 42 | 43 | \code 44 | bswap(0x12345678) == 0x78563412; 45 | bswap((short)0x1234) == 0x3412; 46 | \endcode 47 | 48 | \note 49 | 注意到:无法实现 constexpr 。 50 | */ 51 | template inline 52 | std::enable_if_t<(std::is_integral_v || std::is_enum_v) && sizeof(T) == sizeof(uint8_t), T> 53 | bswap(const T& values) { 54 | return values; 55 | } 56 | 57 | template inline 58 | std::enable_if_t<(std::is_integral_v || std::is_enum_v) && sizeof(T) == sizeof(uint16_t), T> 59 | bswap(const T& values) { 60 | return (T)xbswap16(values); 61 | } 62 | 63 | template inline 64 | std::enable_if_t<(std::is_integral_v || std::is_enum_v) && sizeof(T) == sizeof(uint32_t), T> 65 | bswap(const T& values) { 66 | return (T)xbswap32(values); 67 | } 68 | 69 | template inline 70 | std::enable_if_t<(std::is_integral_v || std::is_enum_v) && sizeof(T) == sizeof(uint64_t), T> 71 | bswap(const T& values) { 72 | return (T)xbswap64(values); 73 | } 74 | 75 | #undef xbswap16 76 | #undef xbswap32 77 | #undef xbswap64 78 | 79 | /** 80 | 当 A > B 时,对调两值,并返回真。否则不变,返回假。 81 | \param a 任意类型非常量值。 82 | \param b 任意类型非常量值。 83 | \return 返回是否对调两值。 84 | 85 | \code 86 | void* a = 0x5; 87 | void* b = 0x1; 88 | seqswap(a, b); // 返回 true ,并且 a == 1,b == 5 。 89 | \endcode 90 | */ 91 | template inline 92 | bool seqswap(T& a, T& b) { 93 | // 避免 max 宏干扰。 94 | if (std::minmax(a, b).second == b) return false; 95 | std::swap(a, b); 96 | return true; 97 | } 98 | 99 | } // namespace xlib 100 | 101 | #endif // _XLIB_XSWAP_H_ 102 | -------------------------------------------------------------------------------- /xvarint.cc: -------------------------------------------------------------------------------- 1 | #include "xvarint.h" 2 | 3 | #include "xlib_test.h" 4 | 5 | enum xvarint_enum { 6 | XVE_0, 7 | XVE_1, 8 | XVE_2, 9 | XVE_3, 10 | XVE_4, 11 | }; 12 | 13 | SHOW_TEST_INIT(xvarint) 14 | 15 | SHOW_TEST_HEAD(zig unsigned); 16 | done = 0x88 == xlib::xzig((uint8_t)0x88); 17 | SHOW_TEST_RESULT; 18 | 19 | SHOW_TEST_HEAD(zig signed); 20 | done = 7 == xlib::xzig((int16_t)-4); 21 | SHOW_TEST_RESULT; 22 | 23 | SHOW_TEST_HEAD(zag unsigned); 24 | done = 0x12345678 == xlib::xzag(xlib::xzig((uint32_t)0x12345678)); 25 | SHOW_TEST_RESULT; 26 | 27 | SHOW_TEST_HEAD(zag signed); 28 | done = 0x1234567812345678 == xlib::xzag(xlib::xzig((int64_t)0x1234567812345678)); 29 | SHOW_TEST_RESULT; 30 | 31 | SHOW_TEST_HEAD(xvarint T signed); 32 | constexpr xlib::xvarint v64((int64_t)12345678); 33 | done = "\x9C\x85\xE3\x0B" == std::string((const char*)v64.data(), v64.size()); 34 | SHOW_TEST_RESULT; 35 | 36 | SHOW_TEST_HEAD(xvarint T unsigned); 37 | constexpr xlib::xvarint v32((uint32_t)0x87654321); 38 | done = "\xA1\x86\x95\xBB\x08" == std::string((const char*)v32.data(), v32.size()); 39 | SHOW_TEST_RESULT; 40 | 41 | SHOW_TEST_HEAD(xvarint T* signed); 42 | done = 12345678 == xlib::xvarint("\x9C\x85\xE3\x0B"); 43 | SHOW_TEST_RESULT; 44 | 45 | SHOW_TEST_HEAD(xvarint T* unsigned); 46 | done = 0x87654321 == xlib::xvarint("\xA1\x86\x95\xBB\x08"); 47 | SHOW_TEST_RESULT; 48 | 49 | SHOW_TEST_HEAD(xvarint enum); 50 | auto ee = XVE_4; 51 | auto xx = xlib::xvarint(ee); 52 | xvarint_enum ex = xlib::xvarint(xx.data()); 53 | done = ex == ee; 54 | SHOW_TEST_RESULT; 55 | 56 | SHOW_TEST_DONE; -------------------------------------------------------------------------------- /xvarint.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file xvarint.h 3 | \brief 定义了 zig 、 zag 、 varint 相关操作。 4 | 5 | \version 2.0.1.250116 6 | \note For All 7 | 8 | \author triones 9 | \date 2017-09-01 10 | 11 | \section history 版本记录 12 | 13 | - 2017-09-05 添加 varint 实现。 1.0 。 14 | - 2017-09-11 修正处理有符号时的错误。 1.0.1 。 15 | - 2019-10-21 改进。1.1 。 16 | - 2019-11-06 重构 zig 、 zag 。 1.2 。 17 | - 2020-03-13 重构 varint 。 2.0 。 18 | */ 19 | #ifndef _XLIB_XVARINT_H_ 20 | #define _XLIB_XVARINT_H_ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | namespace xlib { 28 | 29 | /// zig 用于处理有符号数为无符号数,但返回还是原类型。 30 | template inline constexpr 31 | std::enable_if_t || std::is_enum_v, T> 32 | xzig(const T& v) { 33 | if constexpr (std::is_signed_v) { 34 | // 有符号值,转换成无符号值。 35 | return ((v << 1) ^ (v >> (sizeof(T) * CHAR_BIT - 1))); 36 | } else { 37 | // 无符号值或枚举值,不转换。 38 | return v; 39 | } 40 | } 41 | 42 | /// zag 根据输入类型,处理有符号数,返回还是原类型。 43 | template inline constexpr 44 | std::enable_if_t || std::is_enum_v, T> 45 | xzag(const T& v) { 46 | if constexpr (std::is_signed_v) { 47 | // 有符号值,转换成有符号值。 48 | return ((-(v & 0x01)) ^ ((v >> 1) & ~((T)1 << (sizeof(T) * CHAR_BIT - 1)))); 49 | } else { 50 | // 无符号值,不转换。 51 | return v; 52 | } 53 | } 54 | 55 | template || std::is_enum_v, int> = 0> 56 | class xvarint : public std::array { 57 | public: 58 | using base = std::array; 59 | 60 | private: 61 | T _value; 62 | 63 | public: 64 | constexpr xvarint(const T& value) 65 | : base(), _value(value) { 66 | // g++ 这里有 will be initialized after 警告,可忽略。 67 | using U = typename std::make_unsigned_t; 68 | auto v = (U)xzig(value); 69 | for (auto& pv : *this) { 70 | const auto vv = (uint8_t)(v & 0x7F); 71 | v >>= (CHAR_BIT - 1); 72 | if (0 == v) { 73 | pv = vv; 74 | break; 75 | } 76 | pv = vv | 0x80; 77 | } 78 | } 79 | constexpr uint8_t* data() const noexcept { 80 | return (uint8_t*)base::data(); 81 | } 82 | constexpr size_t size() const noexcept { 83 | size_t n = 0; 84 | for (const auto& v : *this) { 85 | ++n; 86 | if (0 == (v & 0x80)) break; 87 | } 88 | return n; 89 | } 90 | constexpr operator T() const noexcept { 91 | return _value; 92 | } 93 | constexpr T operator()() const noexcept { 94 | return _value; 95 | } 96 | constexpr xvarint(const char* p) : base(), _value(T()) { 97 | using U = typename std::make_unsigned_t; 98 | U v = 0; 99 | size_t count = 0; 100 | for (auto& pv : *this) { 101 | pv = *p; 102 | ++p; 103 | v |= ((pv & 0x7F) << (count * (CHAR_BIT - 1))); 104 | ++count; 105 | if (0 == (pv & 0x80)) { 106 | _value = xzag((T)v); 107 | break; 108 | } 109 | } 110 | } 111 | template ::value, int> = 0> 112 | xvarint(const Ty* p) : xvarint((const char*)p) {} 113 | }; 114 | 115 | } // namespace xlib 116 | 117 | #endif // _XLIB_XVARINT_H_ -------------------------------------------------------------------------------- /xxstring.cc: -------------------------------------------------------------------------------- 1 | #include "xxstring.h" 2 | 3 | #include "xlib_test.h" 4 | 5 | SHOW_TEST_INIT(xxstring) 6 | 7 | const std::string asbuf((const char*)u8"AA转换测试BB"); 8 | const std::wstring wsbuf(L"AA转换测试BB"); 9 | const std::u8string u8buf((const char8_t*)u8"AA转换测试BB"); 10 | 11 | SHOW_TEST_HEAD(string); 12 | done = xlib::xxstring(asbuf) == xlib::xxstring(u8buf); 13 | SHOW_TEST_RESULT; 14 | 15 | SHOW_TEST_HEAD(wstring); 16 | done = xlib::xxstring(wsbuf) == xlib::xxstring(u8buf); 17 | SHOW_TEST_RESULT; 18 | 19 | SHOW_TEST_HEAD(string()); 20 | done = std::string(xlib::xxstring(u8buf)) == asbuf; 21 | SHOW_TEST_RESULT; 22 | 23 | const xlib::xxstring cxx(u8buf); 24 | xlib::xxstring xx(u8buf); 25 | 26 | SHOW_TEST_HEAD(string = cxx); 27 | std::string s(cxx); 28 | done = s == asbuf; 29 | SHOW_TEST_RESULT; 30 | 31 | SHOW_TEST_HEAD(string = xx); 32 | const std::string ss = xx; 33 | done = ss == asbuf; 34 | SHOW_TEST_RESULT; 35 | 36 | SHOW_TEST_HEAD(s = cxx); 37 | s = cxx; 38 | done = s == asbuf; 39 | SHOW_TEST_RESULT; 40 | 41 | SHOW_TEST_HEAD(s = xx); 42 | s = xx; 43 | done = s == asbuf; 44 | SHOW_TEST_RESULT; 45 | 46 | SHOW_TEST_HEAD(string&); 47 | const std::string& rs = xx; 48 | done = (void*)&xx == (void*)&rs; 49 | SHOW_TEST_RESULT; 50 | 51 | SHOW_TEST_HEAD(operator wstring); 52 | done = std::wstring(xlib::xxstring(u8buf)) == wsbuf; 53 | SHOW_TEST_RESULT; 54 | 55 | xlib::xxstring x(asbuf); 56 | SHOW_TEST_HEAD(string&&); 57 | done = (std::string(x.move_as()) == asbuf) && x.empty(); 58 | SHOW_TEST_RESULT; 59 | 60 | SHOW_TEST_HEAD(xmsg); 61 | done = xlib::xxstring(xlib::xmsg() << u8buf) == xlib::xxstring(u8buf); 62 | SHOW_TEST_RESULT; 63 | 64 | SHOW_TEST_HEAD(xmsg <<); 65 | done = (xlib::xmsg() << xlib::xxstring(u8buf)) == u8buf; 66 | SHOW_TEST_RESULT; 67 | 68 | SHOW_TEST_DONE; -------------------------------------------------------------------------------- /xxstring.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file xxstring.h 3 | \brief 对于 存放 UTF-8 编码的 std::string ,用 xxstring 替换会方便一些。 4 | 5 | \version 0.0.2.241230 6 | 7 | \author triones 8 | \date 2023-03-17 9 | 10 | \section history 版本记录 11 | 12 | - 2023-03-17 新建 xxstring 类。 13 | */ 14 | #ifndef _XLIB_XXSTRING_H_ 15 | #define _XLIB_XXSTRING_H_ 16 | 17 | #include "xcodecvt.h" 18 | #include "xmsg.h" 19 | 20 | namespace xlib { 21 | 22 | class xxstring : public std::u8string { 23 | public: 24 | // 开放 std::u8string 构造。 其他有默认构造。 25 | using std::u8string::u8string; 26 | public: 27 | xxstring() = default; 28 | xxstring(const xxstring&) = default; 29 | xxstring& operator=(const xxstring&) = default; 30 | xxstring(xxstring&&) = default; 31 | xxstring& operator=(xxstring&&) = default; 32 | 33 | public: 34 | xxstring(const std::u8string& s) : std::u8string(s) {} 35 | xxstring& operator=(const std::u8string& s) { 36 | assign(s); 37 | return *this; 38 | } 39 | xxstring(std::u8string&& s) : std::u8string(std::move(s)) {} 40 | xxstring& operator=(std::u8string&& s) { 41 | std::u8string::operator=(std::move(s)); 42 | return *this; 43 | } 44 | 45 | public: 46 | // std::string 构造,视之为 UTF-8 编码,不进行编码转换。 47 | xxstring(const std::string& s): xxstring(*(const xxstring*)&s) {} 48 | xxstring& operator=(const std::string& s) { 49 | return operator=(*(const xxstring*)&s); 50 | } 51 | xxstring(std::string&& s): xxstring(std::move(*(xxstring*)&s)) {} 52 | xxstring& operator=(std::string&& s) { 53 | return operator=(std::move(*(xxstring*)&s)); 54 | } 55 | 56 | public: 57 | // std::wstring 构造。无法实现 移动语义。 58 | xxstring(const std::wstring& s): std::u8string(xlib::ws2u8(s)) {} 59 | xxstring& operator=(const std::wstring& s) { 60 | std::u8string::operator=(xlib::ws2u8(s)); 61 | return *this; 62 | } 63 | 64 | public: 65 | // 返回引用,强转,而不进行编码转换。 66 | // 因为 xxstring 的初衷就是灵活在 std::string 与 std::u8string 之间转换。所以隐式转换是必要的。 67 | const std::string& beas() const { return *(const std::string*)this; } 68 | std::string& beas() { return *(std::string*)this; } 69 | std::string toas() const { return *(const std::string*)this; } 70 | operator const std::string&() const { return *(const std::string*)this; } 71 | operator std::string&() { return *(std::string*)this; } 72 | std::string&& move_as() { return std::move(*(std::string*)this); } 73 | /* 74 | 下面的一些转换已经尝试过,但都存在一些冲突。 75 | 其中, 不能与上面共存,编译无冲突,但使用有冲突。 76 | explicit std::string&&() 不冲突了,但 move 无效。 77 | 实在需要 move 时,使用 move_as() 。 78 | 注:std::move 与 std::move 都不行。 79 | */ 80 | //operator std::string() const & { return *(const std::string*)this; } 81 | //operator const std::string() & { return *(const std::string*)this; } 82 | //operator std::string&&() { return std::move(*(std::string*)this); } 83 | // 转换成 std::wstring ,有编码转换。 84 | // 因为转 std::wstring 不可避免地会有编码转换,所以隐式转换无谓临时对象。 85 | std::wstring tows() const { return xlib::u82ws(*this); } 86 | operator std::wstring() const { return xlib::u82ws(*this); } 87 | 88 | public: 89 | bool operator==(const xxstring& v) { 90 | return *(const std::u8string*)(this) == *(const std::u8string*)(&v); 91 | } 92 | bool operator!=(const xxstring& v) { return !operator==(v); } 93 | 94 | public: 95 | // 扩展支持 xmsg 构造。 96 | xxstring(const xlib::xmsg& s) : std::u8string(s) {} 97 | xxstring& operator=(const xlib::xmsg& s) { return operator=(s); } 98 | xxstring(xlib::xmsg&& s): std::u8string(std::move(s)) {} 99 | xxstring& operator=(xlib::xmsg&& s) { return operator=(std::move(s)); } 100 | 101 | const xlib::xmsg& bexmsg() const { return *(const xlib::xmsg*)this; } 102 | xlib::xmsg& bexmsg() { return *(xlib::xmsg*)this; } 103 | xlib::xmsg toxmsg() const { return *(const xlib::xmsg*)this; } 104 | operator const xlib::xmsg&() const { return *(const xlib::xmsg*)this; } 105 | operator xlib::xmsg&() { return *(xlib::xmsg*)this; } 106 | xlib::xmsg&& move_xmsg() { return std::move(*(xlib::xmsg*)this); } 107 | 108 | public: 109 | // 扩展支持 xmsg 输出。好像不需要。因为基类是 std::u8string 。 110 | //friend xmsg& operator<<(xmsg& msg, const xxstring& s) { 111 | // return msg << std::u8string(s); 112 | //} 113 | }; 114 | 115 | } // namespace xlib 116 | 117 | #endif // _XLIB_XXSTRING_H_ --------------------------------------------------------------------------------