├── .github
└── workflows
│ ├── macos.yml
│ ├── ubuntu.yml
│ └── windows.yml
├── .gitignore
├── .vscode
├── launch.json
└── tasks.json
├── LICENSE
├── OpenString.natvis
├── OpenString.sln
├── README.md
├── VisualStudioSolution
├── OpenString.vcxproj
├── OpenString.vcxproj.filters
├── dictionary.txt
├── packages.config
├── test.vcxproj
└── test.vcxproj.filters
├── benchmark
├── codeunit_sequence__benchmark.cpp
├── format__benchmark.cpp
├── main.cpp
└── pch.h
├── include
├── codeunit_sequence.h
├── codeunit_sequence_view.h
├── common
│ ├── adapters.h
│ ├── assertion.h
│ ├── basic_types.h
│ ├── constants.h
│ ├── definitions.h
│ ├── functions.h
│ ├── linear_iterator.h
│ ├── platforms.h
│ └── sequence.h
├── format.h
├── text.h
├── text_view.h
├── unicode.h
└── wide_text.h
├── source
├── codeunit_sequence.cpp
├── format.cpp
├── text.cpp
└── wide_text.cpp
├── test
├── gtest_printers_extension.h
├── main.cpp
├── pch.h
├── scoped_memory_leak_detector.h
├── test__codeunit_sequence.cpp
├── test__codeunit_sequence_view.cpp
├── test__format.cpp
├── test__text.cpp
├── test__text_view.cpp
└── test__wide_text.cpp
└── xmake.lua
/.github/workflows/macos.yml:
--------------------------------------------------------------------------------
1 | name: macos
2 |
3 | on:
4 | pull_request:
5 | paths:
6 | - "xmake.lua"
7 | - "include/**"
8 | - "source/**"
9 | - "test/**"
10 | push:
11 | branches:
12 | - "master"
13 |
14 | jobs:
15 | Build:
16 | name: Build with XMake
17 | strategy:
18 | fail-fast: false
19 | matrix:
20 | os: [ macos-latest ]
21 | configuration: [ debug, release ]
22 | runs-on: ${{ matrix.os }}
23 | steps:
24 | - uses: actions/checkout@v2
25 |
26 | - name: Setup XMake
27 | uses: xmake-io/github-action-setup-xmake@v1
28 | with:
29 | xmake-version: latest
30 | actions-cache-folder: ".xmake-cache-macos"
31 |
32 | - name: Build with configuration file
33 | run: |
34 | xmake f -m ${{ matrix.configuration }} -y
35 | xmake -y -v -w
36 |
37 | - name: Upload binary
38 | uses: actions/upload-artifact@v3
39 | with:
40 | name: test-${{ matrix.os }}-${{ matrix.configuration }}
41 | path: build/macosx/x86_64/${{ matrix.configuration }}/test
42 |
43 |
44 | Test:
45 | needs: Build
46 | name: Test
47 | strategy:
48 | fail-fast: false
49 | matrix:
50 | os: [ macos-latest ]
51 | configuration: [ debug, release ]
52 | runs-on: ${{ matrix.os }}
53 | steps:
54 | - name: Download binary
55 | uses: actions/download-artifact@v3
56 | with:
57 | name: test-${{ matrix.os }}-${{ matrix.configuration }}
58 | path: build/
59 |
60 | - name: Run executable
61 | run: |
62 | chmod 755 ./build/test
63 | ./build/test
64 |
65 |
--------------------------------------------------------------------------------
/.github/workflows/ubuntu.yml:
--------------------------------------------------------------------------------
1 | name: ubuntu
2 |
3 | on:
4 | pull_request:
5 | paths:
6 | - "xmake.lua"
7 | - "include/**"
8 | - "source/**"
9 | - "test/**"
10 | push:
11 | branches:
12 | - "master"
13 |
14 | jobs:
15 | Build:
16 | name: Build with XMake
17 | strategy:
18 | fail-fast: false
19 | matrix:
20 | os: [ ubuntu-latest ]
21 | configuration: [ debug, release ]
22 | runs-on: ${{ matrix.os }}
23 | steps:
24 | - uses: actions/checkout@v2
25 |
26 | - name: Setup XMake
27 | uses: xmake-io/github-action-setup-xmake@v1
28 | with:
29 | xmake-version: latest
30 | actions-cache-folder: ".xmake-cache-ubuntu"
31 |
32 | - name: Build with configuration file
33 | run: |
34 | xmake f -m ${{ matrix.configuration }} -y
35 | xmake -y -v -w
36 |
37 | - name: Upload binary
38 | uses: actions/upload-artifact@v3
39 | with:
40 | name: test-${{ matrix.os }}-${{ matrix.configuration }}
41 | path: build/linux/x86_64/${{ matrix.configuration }}/test
42 |
43 | Test:
44 | needs: Build
45 | name: Test
46 | strategy:
47 | fail-fast: false
48 | matrix:
49 | os: [ ubuntu-latest ]
50 | configuration: [ debug, release ]
51 | runs-on: ${{ matrix.os }}
52 | steps:
53 | - name: Download binary
54 | uses: actions/download-artifact@v3
55 | with:
56 | name: test-${{ matrix.os }}-${{ matrix.configuration }}
57 | path: build/
58 |
59 | - name: Run executable
60 | run: |
61 | chmod 755 ./build/test
62 | ./build/test
63 |
64 |
65 |
--------------------------------------------------------------------------------
/.github/workflows/windows.yml:
--------------------------------------------------------------------------------
1 | name: windows
2 |
3 | on:
4 | pull_request:
5 | paths:
6 | - "xmake.lua"
7 | - "include/**"
8 | - "source/**"
9 | - "test/**"
10 | push:
11 | branches:
12 | - "master"
13 |
14 | jobs:
15 | Build:
16 | name: Build with XMake
17 | strategy:
18 | fail-fast: false
19 | matrix:
20 | os: [ windows-latest ]
21 | configuration: [ debug, release ]
22 | runs-on: ${{ matrix.os }}
23 | steps:
24 | - uses: actions/checkout@v2
25 |
26 | - name: Setup XMake
27 | uses: xmake-io/github-action-setup-xmake@v1
28 | with:
29 | xmake-version: latest
30 | actions-cache-folder: ".xmake-cache-windows"
31 |
32 | - name: Build with configuration file
33 | run: |
34 | xmake f -m ${{ matrix.configuration }} -y
35 | xmake -y -v -w
36 |
37 | - name: Upload binary
38 | uses: actions/upload-artifact@v3
39 | with:
40 | name: test-${{ matrix.os }}-${{ matrix.configuration }}
41 | path: build/windows/x64/${{ matrix.configuration }}/test.exe
42 |
43 | Test:
44 | needs: Build
45 | name: Test
46 | strategy:
47 | fail-fast: false
48 | matrix:
49 | os: [ windows-latest ]
50 | configuration: [ debug, release ]
51 | runs-on: ${{ matrix.os }}
52 | steps:
53 | - name: Download binary
54 | uses: actions/download-artifact@v3
55 | with:
56 | name: test-${{ matrix.os }}-${{ matrix.configuration }}
57 | path: build/
58 |
59 | - name: Run executable
60 | run: start "build/test.exe"
61 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xmake cache
2 | .xmake/
3 | build/
4 |
5 | # Clangd cache
6 | .cache/clangd/
7 |
8 | # Build files
9 | x64/
10 |
11 | # nuget packages
12 | packages/
13 |
14 | # Generated files
15 | compile_commands.json
16 |
17 | # Visual Studio cache/options directory
18 | .vs/
19 |
20 | # Visual Studio Code cache/options directory
21 | .vscode/settings.json
22 |
23 | # JetBrains Rider cache/options directory
24 | .idea/
25 |
26 | # JetBrains Rider auto-generated XML file
27 | *.sln.DotSettings
28 |
29 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Debug test - xmake",
9 | "request": "launch",
10 | "type": "xmake",
11 | "target": "",
12 | "windows": {
13 | "name" : "windows",
14 | "type": "xmake",
15 | "request": "launch",
16 | "target": "${workspaceFolder}/build/windows/x64/debug/test.exe",
17 | },
18 | "linux": {
19 | "name" : "linux",
20 | "type": "xmake",
21 | "request": "launch",
22 | "target": "${workspaceFolder}/build/linux/x86_64/debug/test",
23 | },
24 | "args": [],
25 | "cwd": "${workspaceFolder}",
26 | "preLaunchTask": "Build test"
27 | },
28 | {
29 | "name": "Debug test",
30 | "request": "launch",
31 | "type": "cppdbg",
32 | "program": "",
33 | "windows": {
34 | "name" : "windows",
35 | "type" : "cppvsdbg",
36 | "request": "launch",
37 | "program": "${workspaceFolder}/build/windows/x64/debug/test.exe"
38 | },
39 | "linux": {
40 | "name" : "linux",
41 | "type" : "cppdbg",
42 | "request": "launch",
43 | "program": "${workspaceFolder}/build/linux/x86_64/debug/test"
44 | },
45 | "args": [],
46 | "cwd": "${workspaceFolder}",
47 | "visualizerFile": "${workspaceFolder}/OpenString.natvis",
48 | "preLaunchTask": "Build test"
49 | }
50 | ]
51 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=733558
3 | // for the documentation about the tasks.json format
4 | "version": "2.0.0",
5 | "tasks": [
6 | {
7 | "label": "Build test",
8 | "type": "shell",
9 | "command": "xmake build -w test"
10 | }
11 | ]
12 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 OpenString
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/OpenString.natvis:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | (-∞,+∞)
7 |
8 |
9 | (-∞,{upper_.value})
10 |
11 |
12 | (-∞,{upper_.value}]
13 |
14 |
15 | ({lower_.value},+∞)
16 |
17 |
18 | [{lower_.value},+∞)
19 |
20 |
21 | ({lower_.value},{upper_.value})
22 |
23 |
24 | ({lower_.value},{upper_.value}]
25 |
26 |
27 | [{lower_.value},{upper_.value})
28 |
29 |
30 | [{lower_.value},{upper_.value}]
31 |
32 |
33 | invalid
34 |
35 |
36 |
37 |
38 | {data_,[size_]}
39 |
40 | - size_
41 |
42 |
43 |
44 |
45 | {((easy::codeunit_sequence::sso*)this)->alloc ? ((easy::codeunit_sequence::norm*)this)->data : ((easy::codeunit_sequence::sso*)this)->data._Elems}
46 |
47 | - (bool)(((easy::codeunit_sequence::sso*)this)->alloc)
48 | - ((easy::codeunit_sequence::sso*)this)->alloc ? ((easy::codeunit_sequence::norm*)this)->size : (easy::u32)((easy::codeunit_sequence::sso*)this)->size
49 | - ((easy::codeunit_sequence::sso*)this)->alloc ? ((easy::codeunit_sequence::norm*)this)->capacity : easy::codeunit_sequence::SSO_SIZE_MAX
50 | - ((easy::codeunit_sequence::sso*)this)->alloc ? ((easy::codeunit_sequence::norm*)this)->data : (easy::u32)((easy::codeunit_sequence::sso*)this)->data._Elems
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/OpenString.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.4.33205.214
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OpenString", "VisualStudioSolution\OpenString.vcxproj", "{16BBF1B5-AF6E-4B3D-A507-1C24CB0E3285}"
7 | EndProject
8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "VisualStudioSolution\test.vcxproj", "{5AEA09DB-474B-4770-AEF6-22F4AF32EDC3}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Visualizer", "Visualizer", "{69C45B19-D146-4B14-862B-2C41E82374E0}"
11 | ProjectSection(SolutionItems) = preProject
12 | OpenString.natvis = OpenString.natvis
13 | EndProjectSection
14 | EndProject
15 | Global
16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
17 | Debug|x64 = Debug|x64
18 | Release|x64 = Release|x64
19 | EndGlobalSection
20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
21 | {16BBF1B5-AF6E-4B3D-A507-1C24CB0E3285}.Debug|x64.ActiveCfg = Debug|x64
22 | {16BBF1B5-AF6E-4B3D-A507-1C24CB0E3285}.Debug|x64.Build.0 = Debug|x64
23 | {16BBF1B5-AF6E-4B3D-A507-1C24CB0E3285}.Release|x64.ActiveCfg = Release|x64
24 | {16BBF1B5-AF6E-4B3D-A507-1C24CB0E3285}.Release|x64.Build.0 = Release|x64
25 | {5AEA09DB-474B-4770-AEF6-22F4AF32EDC3}.Debug|x64.ActiveCfg = Debug|x64
26 | {5AEA09DB-474B-4770-AEF6-22F4AF32EDC3}.Debug|x64.Build.0 = Debug|x64
27 | {5AEA09DB-474B-4770-AEF6-22F4AF32EDC3}.Release|x64.ActiveCfg = Release|x64
28 | {5AEA09DB-474B-4770-AEF6-22F4AF32EDC3}.Release|x64.Build.0 = Release|x64
29 | EndGlobalSection
30 | GlobalSection(SolutionProperties) = preSolution
31 | HideSolutionNode = FALSE
32 | EndGlobalSection
33 | GlobalSection(ExtensibilityGlobals) = postSolution
34 | SolutionGuid = {CF1AD042-3A9F-47DD-B4F0-51ED3471675B}
35 | EndGlobalSection
36 | EndGlobal
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
OpenString
3 |
14 |
15 |
A modern user-friendly string library for game developing
16 |
17 |
18 | ## License
19 |
20 | *OpenString* is distributed under the MIT license.
21 |
22 | ## Features
23 |
24 | - User-friendly APIs
25 | - Encoding dependencies are encapsulated and hidden
26 | - Using [{fmt}](https://fmt.dev/latest/index.html)-like formatting method
27 | - High performance
28 |
29 | ## Target Users
30 |
31 | - Ask for a easy-using and high-performance string library.
32 | - Do not prepare to worry about encoding things.
33 | - Deal with strings in common sense and safe way.
34 |
35 | ## Benchmarks
36 |
37 | todo
38 |
39 | ## Q&A
40 |
41 | todo
42 |
43 | ## TODO
44 |
45 | - CMake support
46 | - Regex support
47 | - Folder directory path library
48 | - A ``name`` class for easy copying and comparing
49 | - Localization support
50 |
51 | ## Thanks
52 |
53 | - [{fmt}](https://github.com/fmtlib/fmt): Provides a lot of inspiration for the formatting method.
54 | - [Folly](https://github.com/facebook/folly): FBString provides references for some implementations.
55 | - [xmake](https://github.com/xmake-io/xmake): A powerful cross-platform build utility.
--------------------------------------------------------------------------------
/VisualStudioSolution/OpenString.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | x64
7 |
8 |
9 | Release
10 | x64
11 |
12 |
13 |
14 | 16.0
15 | Win32Proj
16 | {16bbf1b5-af6e-4b3d-a507-1c24cb0e3285}
17 | OpenString
18 | 10.0
19 |
20 |
21 |
22 | StaticLibrary
23 | true
24 | v143
25 | Unicode
26 |
27 |
28 | Application
29 | false
30 | v143
31 | true
32 | Unicode
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | $(SolutionDir)include;$(IncludePath)
48 | $(Platform)\$(Configuration)\OpenString\
49 |
50 |
51 |
52 | Level3
53 | true
54 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;NOMINMAX
55 | true
56 | stdcpp17
57 |
58 |
59 |
60 |
61 | Console
62 | true
63 |
64 |
65 |
66 |
67 | Level3
68 | true
69 | true
70 | true
71 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
72 | true
73 |
74 |
75 | Console
76 | true
77 | true
78 | true
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/VisualStudioSolution/OpenString.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
6 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
7 |
8 |
9 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
10 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
11 |
12 |
13 | {4adabc1e-7b75-4860-a3a1-f9098d269d47}
14 |
15 |
16 |
17 |
18 | include
19 |
20 |
21 | include
22 |
23 |
24 | include
25 |
26 |
27 | include
28 |
29 |
30 | include\common
31 |
32 |
33 | include\common
34 |
35 |
36 | include\common
37 |
38 |
39 | include\common
40 |
41 |
42 | include
43 |
44 |
45 | include
46 |
47 |
48 | include\common
49 |
50 |
51 |
52 |
53 | source
54 |
55 |
56 | source
57 |
58 |
59 | source
60 |
61 |
62 |
--------------------------------------------------------------------------------
/VisualStudioSolution/dictionary.txt:
--------------------------------------------------------------------------------
1 | Hoshizora
2 | codeunit
3 | cbegin
4 | cend
5 |
--------------------------------------------------------------------------------
/VisualStudioSolution/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/VisualStudioSolution/test.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | x64
7 |
8 |
9 | Release
10 | x64
11 |
12 |
13 |
14 | 16.0
15 | Win32Proj
16 | {5aea09db-474b-4770-aef6-22f4af32edc3}
17 | test
18 | 10.0
19 |
20 |
21 |
22 | Application
23 | true
24 | v143
25 | Unicode
26 |
27 |
28 | Application
29 | false
30 | v143
31 | true
32 | Unicode
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | $(SolutionDir)include;$(SolutionDir)test\platform;$(IncludePath)
48 | $(Platform)\$(Configuration)\test\
49 |
50 |
51 |
52 | Level3
53 | true
54 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions);WIN32_LEAN_AND_MEAN;NOMINMAX
55 | true
56 | stdcpp17
57 | $(SolutionDir)include;%(AdditionalIncludeDirectories)
58 |
59 |
60 | Console
61 | true
62 |
63 |
64 |
65 |
66 | Level3
67 | true
68 | true
69 | true
70 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
71 | true
72 |
73 |
74 | Console
75 | true
76 | true
77 | true
78 |
79 |
80 |
81 |
82 | {16bbf1b5-af6e-4b3d-a507-1c24cb0e3285}
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 | 这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/VisualStudioSolution/test.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
6 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | common
21 |
22 |
23 | common
24 |
25 |
26 |
27 | common
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/benchmark/codeunit_sequence__benchmark.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "common/basic_types.h"
3 | #include "text.h"
4 |
5 | void std_string_construct(benchmark::State& state)
6 | {
7 | for (auto _ : state)
8 | {
9 | std::string empty_string;
10 | }
11 | }
12 |
13 | void codeunit_sequence_construct(benchmark::State& state)
14 | {
15 | for (auto _ : state)
16 | {
17 | easy::codeunit_sequence empty_sequence;
18 | }
19 | }
20 |
21 | BENCHMARK(std_string_construct);
22 | BENCHMARK(codeunit_sequence_construct);
--------------------------------------------------------------------------------
/benchmark/format__benchmark.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "format.h"
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/benchmark/main.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 |
3 | BENCHMARK_MAIN();
--------------------------------------------------------------------------------
/benchmark/pch.h:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 |
4 | #include
5 |
--------------------------------------------------------------------------------
/include/codeunit_sequence.h:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 | #include "common/definitions.h"
4 |
5 | #include
6 |
7 | #include "codeunit_sequence_view.h"
8 |
9 | namespace ostr
10 | {
11 | class OPEN_STRING_API codeunit_sequence
12 | {
13 | public:
14 |
15 | // code-region-start: constructors
16 |
17 | codeunit_sequence() noexcept;
18 | explicit codeunit_sequence(u64 size) noexcept;
19 | codeunit_sequence(const codeunit_sequence&) noexcept;
20 | codeunit_sequence(codeunit_sequence&&) noexcept;
21 | codeunit_sequence& operator=(const codeunit_sequence&) noexcept;
22 | codeunit_sequence& operator=(codeunit_sequence&&) noexcept;
23 |
24 | codeunit_sequence& operator=(const codeunit_sequence_view& view) noexcept;
25 |
26 | ~codeunit_sequence() noexcept;
27 |
28 | explicit codeunit_sequence(const char* data) noexcept;
29 | codeunit_sequence(const char* from, const char* last) noexcept;
30 | codeunit_sequence(const char* data, u64 count) noexcept;
31 |
32 | explicit codeunit_sequence(const codeunit_sequence_view& sv) noexcept;
33 |
34 | template
35 | static codeunit_sequence build(const Args&... argument);
36 | template
37 | static codeunit_sequence join(const Container& container, const codeunit_sequence_view& separator) noexcept;
38 |
39 | // code-region-end: constructors
40 |
41 | // code-region-start: iterators
42 |
43 | using const_iterator = codeunit_sequence_view::const_iterator;
44 |
45 | struct iterator
46 | {
47 | iterator() noexcept;
48 | explicit iterator(char* v) noexcept;
49 | [[nodiscard]] char* data() const noexcept;
50 | [[nodiscard]] char& operator*() const noexcept;
51 | [[nodiscard]] i64 operator-(const iterator& rhs) const noexcept;
52 | iterator& operator+=(i64 diff) noexcept;
53 | iterator& operator-=(i64 diff) noexcept;
54 | iterator& operator+=(u64 diff) noexcept;
55 | iterator& operator-=(u64 diff) noexcept;
56 | [[nodiscard]] iterator operator+(i64 diff) const noexcept;
57 | [[nodiscard]] iterator operator-(i64 diff) const noexcept;
58 | [[nodiscard]] iterator operator+(u64 diff) const noexcept;
59 | [[nodiscard]] iterator operator-(u64 diff) const noexcept;
60 | iterator& operator++() noexcept;
61 | iterator operator++(int) noexcept;
62 | iterator& operator--() noexcept;
63 | iterator operator--(int) noexcept;
64 | [[nodiscard]] bool operator==(const iterator& rhs) const noexcept;
65 | [[nodiscard]] bool operator!=(const iterator& rhs) const noexcept;
66 | [[nodiscard]] bool operator<(const iterator& rhs) const noexcept;
67 | [[nodiscard]] bool operator>(const iterator& rhs) const noexcept;
68 | [[nodiscard]] bool operator<=(const iterator& rhs) const noexcept;
69 | [[nodiscard]] bool operator>=(const iterator& rhs) const noexcept;
70 |
71 | char* value;
72 | };
73 |
74 | [[nodiscard]] iterator begin() noexcept;
75 | [[nodiscard]] const_iterator begin() const noexcept;
76 | [[nodiscard]] iterator end() noexcept;
77 | [[nodiscard]] const_iterator end() const noexcept;
78 | [[nodiscard]] const_iterator cbegin() const noexcept;
79 | [[nodiscard]] const_iterator cend() const noexcept;
80 |
81 | // code-region-end: iterators
82 |
83 | [[nodiscard]] codeunit_sequence_view view() const& noexcept;
84 | /**
85 | * FATAL: You must NOT get view from rvalue of a codeunit sequence
86 | */
87 | codeunit_sequence_view view() && = delete;
88 |
89 | /**
90 | * @return The length of this codeunit sequence
91 | */
92 | [[nodiscard]] u64 size() const noexcept;
93 |
94 | /**
95 | * @return Whether this codeunit sequence is empty or not.
96 | */
97 | [[nodiscard]] bool is_empty() const noexcept;
98 |
99 | /**
100 | * @param rhs Another codeunit sequence
101 | * @return Whether two codeunit sequences are equal.
102 | */
103 | [[nodiscard]] bool operator==(const codeunit_sequence_view& rhs) const noexcept;
104 | [[nodiscard]] bool operator==(const codeunit_sequence& rhs) const noexcept;
105 | [[nodiscard]] bool operator==(const char* rhs) const noexcept;
106 |
107 | /**
108 | * @param rhs Another codeunit sequence
109 | * @return Whether two codeunit sequences are different.
110 | */
111 | [[nodiscard]] bool operator!=(const codeunit_sequence_view& rhs) const noexcept;
112 | [[nodiscard]] bool operator!=(const codeunit_sequence& rhs) const noexcept;
113 | [[nodiscard]] bool operator!=(const char* rhs) const noexcept;
114 |
115 | /**
116 | * Append a codeunit sequence back.
117 | * @return ref of this codeunit sequence.
118 | */
119 | codeunit_sequence& append(const codeunit_sequence_view& rhs) noexcept;
120 | codeunit_sequence& append(const codeunit_sequence& rhs) noexcept;
121 | codeunit_sequence& append(const codepoint& cp) noexcept;
122 | codeunit_sequence& append(const char* rhs) noexcept;
123 | /**
124 | * \brief Append amount of same character after this sequence.
125 | * \param codeunit Character to fill in.
126 | * Specially, if character is '\0', it will still resize the sequence,
127 | * but will not set memory to 0, so you can use like
128 | * c.append('\0', 1024);
129 | * for like buffering file or network streaming in.
130 | * \param count How many character to fill in.
131 | * \return self
132 | */
133 | codeunit_sequence& append(char codeunit, u64 count = 1) noexcept;
134 |
135 | codeunit_sequence& operator+=(const codeunit_sequence_view& rhs) noexcept;
136 | codeunit_sequence& operator+=(const codeunit_sequence& rhs) noexcept;
137 | codeunit_sequence& operator+=(const codepoint& cp) noexcept;
138 | codeunit_sequence& operator+=(const char* rhs) noexcept;
139 | codeunit_sequence& operator+=(char codeunit) noexcept;
140 |
141 | [[nodiscard]] codeunit_sequence_view subview(u64 from, u64 size = SIZE_MAX) const noexcept;
142 |
143 | /**
144 | * Make this a subsequence from specific range
145 | *
146 | * Example: codeunit_sequence("codeunit_sequence").subsequence(2, 3) == "ar_";
147 | *
148 | * @param from start index
149 | * @param size size of subsequence
150 | * @return ref of this codeunit sequence
151 | */
152 | codeunit_sequence& subsequence(u64 from, u64 size = SIZE_MAX) noexcept;
153 |
154 | /**
155 | * Get the index of specific codeunit_sequence.
156 | *
157 | * Example: codeunit_sequence("codeunit_sequence").index_of("ar_") == 2;
158 | *
159 | * @param pattern pattern to search.
160 | * @param from start index to search
161 | * @param size size of search range
162 | * @return the index of where the codeunit_sequence first found
163 | */
164 | [[nodiscard]] u64 index_of(const codeunit_sequence_view& pattern, u64 from = 0, u64 size = SIZE_MAX) const noexcept;
165 | [[nodiscard]] u64 last_index_of(const codeunit_sequence_view& pattern, u64 from = 0, u64 size = SIZE_MAX) const noexcept;
166 |
167 | [[nodiscard]] u64 count(const codeunit_sequence_view& pattern) const noexcept;
168 |
169 | [[nodiscard]] bool starts_with(const codeunit_sequence_view& pattern) const noexcept;
170 | [[nodiscard]] bool ends_with(const codeunit_sequence_view& pattern) const noexcept;
171 |
172 | /**
173 | * Empty the string without reallocation.
174 | */
175 | void empty();
176 | /**
177 | * Empty the string with size reserved.
178 | * @param size the size reserved
179 | */
180 | void empty(u64 size);
181 |
182 | void reserve(u64 size);
183 |
184 | codeunit_sequence& write_at(u64 index, char codeunit) noexcept;
185 | [[nodiscard]] const char& read_at(u64 index) const noexcept;
186 |
187 | [[nodiscard]] char& operator[](u64 index) noexcept;
188 | [[nodiscard]] const char& operator[](u64 index) const noexcept;
189 |
190 | codeunit_sequence& reverse(u64 from = 0, u64 size = SIZE_MAX) noexcept;
191 |
192 | u32 split(const codeunit_sequence_view& splitter, std::vector& pieces, bool cull_empty = true) const noexcept;
193 |
194 | codeunit_sequence& replace(const codeunit_sequence_view& destination, const codeunit_sequence_view& source, u64 from = 0, u64 size = SIZE_MAX);
195 | codeunit_sequence& replace(const codeunit_sequence_view& destination, u64 from, u64 size = SIZE_MAX);
196 |
197 | codeunit_sequence& self_remove_prefix(const codeunit_sequence_view& prefix) noexcept;
198 | codeunit_sequence& self_remove_suffix(const codeunit_sequence_view& suffix) noexcept;
199 | [[nodiscard]] codeunit_sequence_view view_remove_prefix(const codeunit_sequence_view& prefix) const noexcept;
200 | [[nodiscard]] codeunit_sequence_view view_remove_suffix(const codeunit_sequence_view& suffix) const noexcept;
201 |
202 | codeunit_sequence& self_trim_start(const codeunit_sequence_view& characters = codeunit_sequence_view(" \t")) noexcept;
203 | codeunit_sequence& self_trim_end(const codeunit_sequence_view& characters = codeunit_sequence_view(" \t")) noexcept;
204 | codeunit_sequence& self_trim(const codeunit_sequence_view& characters = codeunit_sequence_view(" \t")) noexcept;
205 | [[nodiscard]] codeunit_sequence_view view_trim_start(const codeunit_sequence_view& characters = codeunit_sequence_view(" \t")) const noexcept;
206 | [[nodiscard]] codeunit_sequence_view view_trim_end(const codeunit_sequence_view& characters = codeunit_sequence_view(" \t")) const noexcept;
207 | [[nodiscard]] codeunit_sequence_view view_trim(const codeunit_sequence_view& characters = codeunit_sequence_view(" \t")) const noexcept;
208 |
209 | [[nodiscard]] char* data() noexcept;
210 | [[nodiscard]] const char* data() const noexcept;
211 | [[nodiscard]] const char* c_str() const noexcept;
212 |
213 | private:
214 |
215 | static constexpr u8 SSO_SIZE_MAX = 14;
216 | static bool is_short_size(u64 size) noexcept;
217 |
218 | struct sso
219 | {
220 | u8 alloc : 1;
221 | u8 size : 7;
222 | std::array data;
223 | };
224 |
225 | struct norm
226 | {
227 | u32 alloc : 1;
228 | u32 size : 15;
229 | u32 capacity; // character capacity, which is 1 less than memory capacity
230 | char* data;
231 | };
232 |
233 | [[nodiscard]] sso& as_sso();
234 | [[nodiscard]] const sso& as_sso() const;
235 |
236 | [[nodiscard]] norm& as_norm();
237 | [[nodiscard]] const norm& as_norm() const;
238 |
239 | /// @return is this a sequence with less than 15 chars
240 | [[nodiscard]] bool is_short() const;
241 |
242 | [[nodiscard]] u64 get_capacity() const;
243 |
244 | [[nodiscard]] char* last();
245 |
246 | [[nodiscard]] const char* last() const;
247 |
248 | void deallocate();
249 |
250 | void set_size(u64 size);
251 |
252 | /**
253 | * \brief Receive and consume data from another string.
254 | * eg. "abc".transfer_data("def"); results in "def" and ""
255 | * \param other Provider.
256 | */
257 | void transfer_data(codeunit_sequence& other);
258 |
259 | std::array store_{ };
260 | };
261 |
262 | namespace details
263 | {
264 | template
265 | [[nodiscard]] constexpr codeunit_sequence_view view_sequence(const T& v)
266 | {
267 | return codeunit_sequence_view{ v };
268 | }
269 |
270 | template<>
271 | [[nodiscard]] inline codeunit_sequence_view view_sequence(const codeunit_sequence& v)
272 | {
273 | return v.view();
274 | }
275 | }
276 |
277 | template
278 | codeunit_sequence codeunit_sequence::build(const Args&... argument)
279 | {
280 | u64 size = 0;
281 | std::array arguments{ details::view_sequence(argument)... };
282 | for(const codeunit_sequence_view& a : arguments)
283 | size += a.size();
284 | codeunit_sequence result(size);
285 | for(const codeunit_sequence_view& a : arguments)
286 | result.append(a);
287 | return { result };
288 | }
289 |
290 | template
291 | codeunit_sequence codeunit_sequence::join(const Container& container, const codeunit_sequence_view& separator) noexcept
292 | {
293 | codeunit_sequence result;
294 | for(const auto& element : container)
295 | {
296 | if(!result.is_empty())
297 | result += separator;
298 | result.append(details::view_sequence(element));
299 | }
300 | return result;
301 | }
302 |
303 | OPEN_STRING_API [[nodiscard]] bool operator==(const codeunit_sequence_view& lhs, const codeunit_sequence& rhs) noexcept;
304 | }
305 |
--------------------------------------------------------------------------------
/include/codeunit_sequence_view.h:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 | #include "common/definitions.h"
4 |
5 | #include
6 | #include
7 |
8 | #include "common/basic_types.h"
9 | #include "common/constants.h"
10 | #include "unicode.h"
11 | #include "common/functions.h"
12 |
13 | namespace ostr
14 | {
15 | class OPEN_STRING_API codeunit_sequence_view
16 | {
17 | public:
18 |
19 | // code-region-start: constructors
20 |
21 | constexpr codeunit_sequence_view() noexcept;
22 | constexpr codeunit_sequence_view(const codeunit_sequence_view&) noexcept;
23 | constexpr codeunit_sequence_view(codeunit_sequence_view&&) noexcept;
24 | constexpr codeunit_sequence_view& operator=(const codeunit_sequence_view& other) noexcept;
25 | constexpr codeunit_sequence_view& operator=(codeunit_sequence_view&& other) noexcept;
26 | ~codeunit_sequence_view() noexcept = default;
27 |
28 | constexpr codeunit_sequence_view(const char* data, const u64 count) noexcept;
29 | constexpr codeunit_sequence_view(const char* data, const char* last) noexcept;
30 | explicit constexpr codeunit_sequence_view(const char* str) noexcept;
31 | explicit constexpr codeunit_sequence_view(const char& c) noexcept;
32 | explicit constexpr codeunit_sequence_view(const codepoint& cp) noexcept;
33 |
34 | // code-region-end: constructors
35 |
36 | // code-region-start: iterators
37 |
38 | struct const_iterator
39 | {
40 | constexpr const_iterator() noexcept;
41 | explicit constexpr const_iterator(const char* v) noexcept;
42 |
43 | [[nodiscard]] constexpr const char* data() const noexcept;
44 | [[nodiscard]] constexpr const char& operator*() const noexcept;
45 |
46 | [[nodiscard]] constexpr i64 operator-(const const_iterator& rhs) const noexcept;
47 |
48 | constexpr const_iterator& operator+=(const i64 diff) noexcept;
49 | constexpr const_iterator& operator-=(const i64 diff) noexcept;
50 | constexpr const_iterator& operator+=(const u64 diff) noexcept;
51 | constexpr const_iterator& operator-=(const u64 diff) noexcept;
52 |
53 | [[nodiscard]] constexpr const_iterator operator+(const i64 diff) const noexcept;
54 | [[nodiscard]] constexpr const_iterator operator-(const i64 diff) const noexcept;
55 | [[nodiscard]] constexpr const_iterator operator+(const u64 diff) const noexcept;
56 | [[nodiscard]] constexpr const_iterator operator-(const u64 diff) const noexcept;
57 |
58 | constexpr const_iterator& operator++() noexcept;
59 | constexpr const_iterator operator++(int) noexcept;
60 | constexpr const_iterator& operator--() noexcept;
61 | constexpr const_iterator operator--(int) noexcept;
62 |
63 | [[nodiscard]] constexpr bool operator==(const const_iterator& rhs) const noexcept;
64 | [[nodiscard]] constexpr bool operator!=(const const_iterator& rhs) const noexcept;
65 | [[nodiscard]] constexpr bool operator<(const const_iterator& rhs) const noexcept;
66 | [[nodiscard]] constexpr bool operator>(const const_iterator& rhs) const noexcept;
67 | [[nodiscard]] constexpr bool operator<=(const const_iterator& rhs) const noexcept;
68 | [[nodiscard]] constexpr bool operator>=(const const_iterator& rhs) const noexcept;
69 |
70 | const char* value = nullptr;
71 | };
72 |
73 | [[nodiscard]] constexpr const_iterator begin() const noexcept;
74 | [[nodiscard]] constexpr const_iterator end() const noexcept;
75 | [[nodiscard]] constexpr const_iterator cbegin() const noexcept;
76 | [[nodiscard]] constexpr const_iterator cend() const noexcept;
77 |
78 | // code-region-end: iterators
79 |
80 | [[nodiscard]] constexpr bool operator==(const codeunit_sequence_view& rhs) const noexcept;
81 | [[nodiscard]] constexpr bool operator==(const char* rhs) const noexcept;
82 | [[nodiscard]] constexpr bool operator!=(const codeunit_sequence_view& rhs) const noexcept;
83 | [[nodiscard]] constexpr bool operator!=(const char* rhs) const noexcept;
84 |
85 | [[nodiscard]] constexpr u64 size() const noexcept;
86 |
87 | // This is not allowed for getting rid of misuse
88 | [[nodiscard]] const char* c_str() const noexcept = delete;
89 |
90 | // Warning: Please make sure that you know what you are doing
91 | // this is not guaranteed to be null-terminated
92 | [[nodiscard]] const char* data() const noexcept;
93 |
94 | /// @return Is this an empty string
95 | [[nodiscard]] constexpr bool is_empty() const noexcept;
96 |
97 | [[nodiscard]] constexpr codeunit_sequence_view subview(const u64 from, const u64 size = SIZE_MAX) const noexcept;
98 |
99 | [[nodiscard]] constexpr const char& read_at(const u64 index) const noexcept;
100 | [[nodiscard]] constexpr const char& read_from_last(const u64 index) const noexcept;
101 |
102 | // [[nodiscard]] constexpr codeunit_sequence_view operator[](const index_interval& range) const noexcept
103 | // {
104 | // return this->subview(range);
105 | // }
106 |
107 | /**
108 | * \brief this is learned from FBString in folly
109 | * see https://github.com/facebook/folly/blob/main/folly/FBString.h
110 | * which is a Boyer-Moore-like trick
111 | * \param pattern string which to search in this string
112 | * \param from search range start index
113 | * \param size search range size
114 | * \return index where searched, return global_constant::INDEX_INVALID_INDEX if not found
115 | */
116 | [[nodiscard]] constexpr u64 index_of(const codeunit_sequence_view& pattern, const u64 from = 0, const u64 size = SIZE_MAX) const noexcept;
117 | [[nodiscard]] constexpr u64 index_of(const char codeunit, const u64 from = 0, const u64 size = SIZE_MAX) const noexcept;
118 | [[nodiscard]] constexpr u64 last_index_of(const codeunit_sequence_view& pattern, const u64 from = 0, const u64 size = SIZE_MAX) const noexcept;
119 | [[nodiscard]] constexpr u64 index_of_any(const codeunit_sequence_view& units, const u64 from = 0, const u64 size = SIZE_MAX) const noexcept;
120 | [[nodiscard]] constexpr u64 last_index_of_any(const codeunit_sequence_view& units, const u64 from = 0, const u64 size = SIZE_MAX) const noexcept;
121 |
122 | [[nodiscard]] constexpr bool contains(const codeunit_sequence_view& pattern) const noexcept;
123 | [[nodiscard]] constexpr bool contains(const char codeunit) const noexcept;
124 |
125 | [[nodiscard]] constexpr std::array split(const codeunit_sequence_view& splitter) const noexcept;
126 | u32 split(const codeunit_sequence_view& splitter, std::vector& pieces, bool cull_empty = true) const noexcept;
127 |
128 | [[nodiscard]] constexpr u64 count(const codeunit_sequence_view& pattern, u64 from = 0, u64 size = global_constant::SIZE_INVALID) const noexcept;
129 |
130 | [[nodiscard]] constexpr bool starts_with(const codeunit_sequence_view& prefix) const noexcept;
131 | [[nodiscard]] constexpr bool ends_with(const codeunit_sequence_view& suffix) const noexcept;
132 |
133 | [[nodiscard]] constexpr codeunit_sequence_view remove_prefix(const codeunit_sequence_view& prefix) const noexcept;
134 | [[nodiscard]] constexpr codeunit_sequence_view remove_suffix(const codeunit_sequence_view& suffix) const noexcept;
135 |
136 | [[nodiscard]] constexpr codeunit_sequence_view trim_start(const codeunit_sequence_view& units = codeunit_sequence_view(" \t")) const noexcept;
137 | [[nodiscard]] constexpr codeunit_sequence_view trim_end(const codeunit_sequence_view& units = codeunit_sequence_view(" \t")) const noexcept;
138 | [[nodiscard]] constexpr codeunit_sequence_view trim(const codeunit_sequence_view& units = codeunit_sequence_view(" \t")) const noexcept;
139 |
140 | private:
141 |
142 | u64 size_{ 0 };
143 | const char* data_{ nullptr };
144 |
145 | };
146 |
147 | namespace details
148 | {
149 | [[nodiscard]] constexpr u64 get_sequence_length(const char* str) noexcept
150 | {
151 | if(!str)
152 | return 0;
153 | u64 count = 0;
154 | while(str[count] != 0)
155 | ++count;
156 | return count;
157 | }
158 | }
159 |
160 | constexpr codeunit_sequence_view::codeunit_sequence_view() noexcept = default;
161 | constexpr codeunit_sequence_view::codeunit_sequence_view(const codeunit_sequence_view&) noexcept = default;
162 | constexpr codeunit_sequence_view::codeunit_sequence_view(codeunit_sequence_view&&) noexcept = default;
163 | constexpr codeunit_sequence_view& codeunit_sequence_view::operator=(const codeunit_sequence_view& other) noexcept = default;
164 | constexpr codeunit_sequence_view& codeunit_sequence_view::operator=(codeunit_sequence_view&& other) noexcept = default;
165 |
166 | constexpr codeunit_sequence_view::codeunit_sequence_view(const char* data, const u64 count) noexcept
167 | : size_{ count }
168 | , data_{ data }
169 | { }
170 |
171 | constexpr codeunit_sequence_view::codeunit_sequence_view(const char* data, const char* last) noexcept
172 | : size_{ static_cast(last - data) }
173 | , data_{ data }
174 | { }
175 |
176 | constexpr codeunit_sequence_view::codeunit_sequence_view(const char* str) noexcept
177 | : codeunit_sequence_view{ str, details::get_sequence_length(str) }
178 | { }
179 |
180 | constexpr codeunit_sequence_view::codeunit_sequence_view(const char& c) noexcept
181 | : codeunit_sequence_view{ &c, 1 }
182 | { }
183 |
184 | constexpr codeunit_sequence_view::codeunit_sequence_view(const codepoint& cp) noexcept
185 | : codeunit_sequence_view{ cp.raw(), cp.size() }
186 | { }
187 |
188 | constexpr codeunit_sequence_view::const_iterator::const_iterator() noexcept = default;
189 |
190 | constexpr codeunit_sequence_view::const_iterator::const_iterator(const char* v) noexcept
191 | : value{ v }
192 | { }
193 |
194 | constexpr const char* codeunit_sequence_view::const_iterator::data() const noexcept
195 | {
196 | return this->value;
197 | }
198 |
199 | constexpr const char& codeunit_sequence_view::const_iterator::operator*() const noexcept
200 | {
201 | return *this->value;
202 | }
203 |
204 | constexpr i64 codeunit_sequence_view::const_iterator::operator-(const const_iterator& rhs) const noexcept
205 | {
206 | return this->value - rhs.value;
207 | }
208 |
209 | constexpr codeunit_sequence_view::const_iterator& codeunit_sequence_view::const_iterator::operator+=(const i64 diff) noexcept
210 | {
211 | this->value += diff;
212 | return *this;
213 | }
214 |
215 | constexpr codeunit_sequence_view::const_iterator& codeunit_sequence_view::const_iterator::operator-=(const i64 diff) noexcept
216 | {
217 | this->value -= diff;
218 | return *this;
219 | }
220 |
221 | constexpr codeunit_sequence_view::const_iterator& codeunit_sequence_view::const_iterator::operator+=(const u64 diff) noexcept
222 | {
223 | this->value += diff;
224 | return *this;
225 | }
226 |
227 | constexpr codeunit_sequence_view::const_iterator& codeunit_sequence_view::const_iterator::operator-=(const u64 diff) noexcept
228 | {
229 | this->value -= diff;
230 | return *this;
231 | }
232 |
233 | constexpr codeunit_sequence_view::const_iterator codeunit_sequence_view::const_iterator::operator+(const i64 diff) const noexcept
234 | {
235 | const_iterator tmp = *this;
236 | tmp += diff;
237 | return tmp;
238 | }
239 |
240 | constexpr codeunit_sequence_view::const_iterator codeunit_sequence_view::const_iterator::operator-(const i64 diff) const noexcept
241 | {
242 | const_iterator tmp = *this;
243 | tmp -= diff;
244 | return tmp;
245 | }
246 |
247 | constexpr codeunit_sequence_view::const_iterator codeunit_sequence_view::const_iterator::operator+(const u64 diff) const noexcept
248 | {
249 | const_iterator tmp = *this;
250 | tmp += diff;
251 | return tmp;
252 | }
253 |
254 | constexpr codeunit_sequence_view::const_iterator codeunit_sequence_view::const_iterator::operator-(const u64 diff) const noexcept
255 | {
256 | const_iterator tmp = *this;
257 | tmp -= diff;
258 | return tmp;
259 | }
260 |
261 | constexpr codeunit_sequence_view::const_iterator& codeunit_sequence_view::const_iterator::operator++() noexcept
262 | {
263 | ++this->value;
264 | return *this;
265 | }
266 |
267 | constexpr codeunit_sequence_view::const_iterator codeunit_sequence_view::const_iterator::operator++(int) noexcept
268 | {
269 | const const_iterator tmp = *this;
270 | ++*this;
271 | return tmp;
272 | }
273 |
274 | constexpr codeunit_sequence_view::const_iterator& codeunit_sequence_view::const_iterator::operator--() noexcept
275 | {
276 | --this->value;
277 | return *this;
278 | }
279 |
280 | constexpr codeunit_sequence_view::const_iterator codeunit_sequence_view::const_iterator::operator--(int) noexcept
281 | {
282 | const const_iterator tmp = *this;
283 | --*this;
284 | return tmp;
285 | }
286 |
287 | constexpr bool codeunit_sequence_view::const_iterator::operator==(const const_iterator& rhs) const noexcept
288 | {
289 | return this->value == rhs.value;
290 | }
291 |
292 | constexpr bool codeunit_sequence_view::const_iterator::operator!=(const const_iterator& rhs) const noexcept
293 | {
294 | return !(*this == rhs);
295 | }
296 |
297 | constexpr bool codeunit_sequence_view::const_iterator::operator<(const const_iterator& rhs) const noexcept
298 | {
299 | return this->value < rhs.value;
300 | }
301 |
302 | constexpr bool codeunit_sequence_view::const_iterator::operator>(const const_iterator& rhs) const noexcept
303 | {
304 | return rhs < *this;
305 | }
306 |
307 | constexpr bool codeunit_sequence_view::const_iterator::operator<=(const const_iterator& rhs) const noexcept
308 | {
309 | return rhs >= *this;
310 | }
311 |
312 | constexpr bool codeunit_sequence_view::const_iterator::operator>=(const const_iterator& rhs) const noexcept
313 | {
314 | return !(*this < rhs);
315 | }
316 |
317 | constexpr codeunit_sequence_view::const_iterator codeunit_sequence_view::begin() const noexcept
318 | {
319 | return const_iterator{ this->data_ };
320 | }
321 |
322 | constexpr codeunit_sequence_view::const_iterator codeunit_sequence_view::end() const noexcept
323 | {
324 | return const_iterator{ this->data_ + this->size_ };
325 | }
326 |
327 | constexpr codeunit_sequence_view::const_iterator codeunit_sequence_view::cbegin() const noexcept
328 | {
329 | return this->begin();
330 | }
331 |
332 | constexpr codeunit_sequence_view::const_iterator codeunit_sequence_view::cend() const noexcept
333 | {
334 | return this->end();
335 | }
336 |
337 | constexpr bool codeunit_sequence_view::operator==(const codeunit_sequence_view& rhs) const noexcept
338 | {
339 | if (this->size() != rhs.size())
340 | return false;
341 | for(u64 i = 0; i < this->size(); ++i)
342 | if(this->read_at(i) != rhs.read_at(i))
343 | return false;
344 | return true;
345 | }
346 |
347 | constexpr bool codeunit_sequence_view::operator==(const char* rhs) const noexcept
348 | {
349 | return this->operator==(codeunit_sequence_view(rhs));
350 | }
351 |
352 | constexpr bool codeunit_sequence_view::operator!=(const codeunit_sequence_view& rhs) const noexcept
353 | {
354 | return !this->operator==(rhs);
355 | }
356 |
357 | constexpr bool codeunit_sequence_view::operator!=(const char* rhs) const noexcept
358 | {
359 | return !this->operator==(rhs);
360 | }
361 |
362 | constexpr u64 codeunit_sequence_view::size() const noexcept
363 | {
364 | return this->size_;
365 | }
366 |
367 | inline const char* codeunit_sequence_view::data() const noexcept
368 | {
369 | return this->data_;
370 | }
371 |
372 | constexpr bool codeunit_sequence_view::is_empty() const noexcept
373 | {
374 | return this->size_ == 0;
375 | }
376 |
377 | constexpr codeunit_sequence_view codeunit_sequence_view::subview(const u64 from, const u64 size) const noexcept
378 | {
379 | const u64 self_size = this->size();
380 |
381 | if(from >= self_size || size == 0)
382 | return { };
383 |
384 | const char* first_data = this->data_ + from;
385 | const u64 actual_size = minimum(size, self_size - from);
386 | return { first_data, actual_size };
387 | }
388 |
389 | constexpr const char& codeunit_sequence_view::read_at(const u64 index) const noexcept
390 | {
391 | return this->data_[index];
392 | }
393 |
394 | constexpr const char& codeunit_sequence_view::read_from_last(const u64 index) const noexcept
395 | {
396 | return this->data_[this->size_ - index - 1];
397 | }
398 |
399 | constexpr u64 codeunit_sequence_view::index_of(const codeunit_sequence_view& pattern, const u64 from, const u64 size) const noexcept
400 | {
401 | const codeunit_sequence_view view = this->subview(from, size);
402 | if(view.size() < pattern.size())
403 | return global_constant::INDEX_INVALID;
404 | const char pattern_last = pattern.read_from_last(0);
405 | u64 skip = 1;
406 | while(pattern.size() > skip && pattern.read_from_last(skip) != pattern_last)
407 | ++skip;
408 |
409 | u64 i = 0;
410 | const u64 endpoint = view.size() - pattern.size() + 1;
411 | while(i < endpoint)
412 | {
413 | while(true)
414 | {
415 | if(view.read_at(i + pattern.size() - 1) == pattern_last)
416 | break;
417 | ++i;
418 | if(i == endpoint)
419 | return global_constant::INDEX_INVALID;
420 | }
421 | if(view.subview(i, pattern.size()) == pattern)
422 | return i + from;
423 | i += skip;
424 | }
425 | return global_constant::INDEX_INVALID;
426 | }
427 |
428 | constexpr u64 codeunit_sequence_view::index_of(const char codeunit, const u64 from, const u64 size) const noexcept
429 | {
430 | if(codeunit == 0)
431 | return global_constant::INDEX_INVALID;
432 | const codeunit_sequence_view view = this->subview(from, size);
433 | for(u64 i = 0; i < view.size(); ++i)
434 | if(view.read_at(i) == codeunit)
435 | return i + from;
436 | return global_constant::INDEX_INVALID;
437 | }
438 |
439 | constexpr u64 codeunit_sequence_view::last_index_of(const codeunit_sequence_view& pattern, const u64 from, const u64 size) const noexcept
440 | {
441 | const codeunit_sequence_view view = this->subview(from, size);
442 | if(pattern.is_empty())
443 | return global_constant::INDEX_INVALID;
444 | if(view.size() < pattern.size())
445 | return global_constant::INDEX_INVALID;
446 |
447 | const char pattern_first = pattern.read_at(0);
448 | u64 skip = 1;
449 | while(pattern.size() > skip && pattern.read_at(skip) != pattern_first)
450 | ++skip;
451 |
452 | u64 i = view.size() - pattern.size();
453 | while(true)
454 | {
455 | while(true)
456 | {
457 | if(view.read_at(i) == pattern_first)
458 | break;
459 | if(i == 0)
460 | return global_constant::INDEX_INVALID;
461 | --i;
462 | }
463 | if(view.subview(i, pattern.size()) == pattern)
464 | return i + from;
465 | if(i < skip)
466 | break;
467 | i -= skip;
468 | }
469 | return global_constant::INDEX_INVALID;
470 | }
471 |
472 | constexpr u64 codeunit_sequence_view::index_of_any(const codeunit_sequence_view& units, const u64 from, const u64 size) const noexcept
473 | {
474 | const u64 actual_size = minimum(this->size() - from, size);
475 | const u64 final_index = from + actual_size;
476 | for(u64 i = from; i < final_index; ++i)
477 | if(const char& unit = this->read_at(i); units.contains(unit))
478 | return i;
479 | return global_constant::INDEX_INVALID;
480 | }
481 |
482 | constexpr u64 codeunit_sequence_view::last_index_of_any(const codeunit_sequence_view& units, const u64 from, const u64 size) const noexcept
483 | {
484 | const u64 actual_size = minimum(this->size() - from, size);
485 | const u64 final_index = from + actual_size;
486 | for(u64 i = final_index - 1; i >= from; --i)
487 | if(const char& unit = this->read_at(i); units.contains(unit))
488 | return i;
489 | return global_constant::INDEX_INVALID;
490 | }
491 |
492 | constexpr bool codeunit_sequence_view::contains(const codeunit_sequence_view& pattern) const noexcept
493 | {
494 | return this->index_of(pattern) != global_constant::INDEX_INVALID;
495 | }
496 |
497 | constexpr bool codeunit_sequence_view::contains(const char codeunit) const noexcept
498 | {
499 | return this->index_of(codeunit) != global_constant::INDEX_INVALID;
500 | }
501 |
502 | constexpr std::array codeunit_sequence_view::split(const codeunit_sequence_view& splitter) const noexcept
503 | {
504 | const u64 index = this->index_of(splitter);
505 | if(index == global_constant::INDEX_INVALID)
506 | return { *this, { } };
507 | const codeunit_sequence_view first = this->subview(0, index);
508 | const codeunit_sequence_view second = this->subview(index + splitter.size());
509 | return { first, second };
510 | }
511 |
512 | inline u32 codeunit_sequence_view::split(const codeunit_sequence_view& splitter, std::vector& pieces, const bool cull_empty) const noexcept
513 | {
514 | codeunit_sequence_view view{ *this };
515 | u32 count = 0;
516 | while(true)
517 | {
518 | const auto [ left, right ] = view.split(splitter);
519 | if(!cull_empty || !left.is_empty())
520 | pieces.push_back(left);
521 | ++count;
522 | if(right.is_empty())
523 | break;
524 | view = right;
525 | }
526 | return count;
527 | }
528 |
529 | constexpr u64 codeunit_sequence_view::count(const codeunit_sequence_view& pattern, const u64 from, const u64 size) const noexcept
530 | {
531 | codeunit_sequence_view view = this->subview(from, size);
532 | u64 count = 0;
533 | while(true)
534 | {
535 | const u64 index = view.index_of(pattern);
536 | if(index == global_constant::INDEX_INVALID)
537 | break;
538 | ++count;
539 | view = view.subview(index + pattern.size());
540 | }
541 | return count;
542 | }
543 |
544 | constexpr bool codeunit_sequence_view::starts_with(const codeunit_sequence_view& prefix) const noexcept
545 | {
546 | if(prefix.is_empty())
547 | return true;
548 | return this->subview(0, prefix.size()) == prefix;
549 | }
550 |
551 | constexpr bool codeunit_sequence_view::ends_with(const codeunit_sequence_view& suffix) const noexcept
552 | {
553 | if(suffix.is_empty())
554 | return true;
555 | const u64 end_size = suffix.size();
556 | const u64 this_size = this->size();
557 | return this->subview(this_size - end_size, end_size) == suffix;
558 | }
559 |
560 | constexpr codeunit_sequence_view codeunit_sequence_view::remove_prefix(const codeunit_sequence_view& prefix) const noexcept
561 | {
562 | return this->starts_with(prefix) ? this->subview(prefix.size()) : *this;
563 | }
564 |
565 | constexpr codeunit_sequence_view codeunit_sequence_view::remove_suffix(const codeunit_sequence_view& suffix) const noexcept
566 | {
567 | return this->ends_with(suffix) ? this->subview(0, this->size() - suffix.size()) : *this;
568 | }
569 |
570 | constexpr codeunit_sequence_view codeunit_sequence_view::trim_start(const codeunit_sequence_view& units) const noexcept
571 | {
572 | if(this->is_empty())
573 | return { };
574 | for(u64 i = 0; i < this->size(); ++i)
575 | if(const char codeunit = this->read_at(i); !units.contains(codeunit))
576 | return this->subview(i);
577 | return { };
578 | }
579 |
580 | constexpr codeunit_sequence_view codeunit_sequence_view::trim_end(const codeunit_sequence_view& units) const noexcept
581 | {
582 | if(this->is_empty())
583 | return { };
584 |
585 | for(u64 i = this->size(); i > 0; --i)
586 | if(const char codeunit = this->read_at(i - 1); !units.contains(codeunit))
587 | return this->subview(0, i);
588 | return { };
589 | }
590 |
591 | constexpr codeunit_sequence_view codeunit_sequence_view::trim(const codeunit_sequence_view& units) const noexcept
592 | {
593 | return this->trim_start(units).trim_end(units);
594 | }
595 | }
596 |
597 | inline namespace literal
598 | {
599 | [[nodiscard]] constexpr ostr::codeunit_sequence_view operator""_cuqv(const char* str, const size_t len) noexcept
600 | {
601 | return { str, len };
602 | }
603 | }
604 |
--------------------------------------------------------------------------------
/include/common/adapters.h:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 |
4 | #include
5 | #include
6 |
7 | namespace ostr
8 | {
9 | template
10 | struct allocator
11 | {
12 | static T* allocate_single() noexcept
13 | {
14 | return new T;
15 | }
16 |
17 | static T* allocate_array(const size_t count) noexcept
18 | {
19 | return new T[count];
20 | }
21 |
22 | static void deallocate_single(const T* ptr) noexcept
23 | {
24 | delete ptr;
25 | }
26 |
27 | static void deallocate_array(const T* ptr) noexcept
28 | {
29 | delete[] ptr;
30 | }
31 |
32 | template
33 | static void placement_construct(void* ptr, Args&&...args) noexcept
34 | {
35 | new(ptr) T{ std::forward(args)... };
36 | }
37 | };
38 | }
39 |
--------------------------------------------------------------------------------
/include/common/assertion.h:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 |
4 | #include "platforms.h"
5 | #include "common/definitions.h"
6 | #include "format.h"
--------------------------------------------------------------------------------
/include/common/basic_types.h:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 | #include
4 |
5 | namespace ostr
6 | {
7 | using i8 = int8_t;
8 | using u8 = uint8_t;
9 | using i16 = int16_t;
10 | using u16 = uint16_t;
11 | using i32 = int32_t;
12 | using u32 = uint32_t;
13 | using i64 = int64_t;
14 | using u64 = uint64_t;
15 |
16 | using f32 = float;
17 | using f64 = double;
18 |
19 | using byte = u8;
20 | }
21 |
22 | inline namespace literal
23 | {
24 | [[nodiscard]] constexpr char operator""_as_char(const unsigned long long value) noexcept
25 | {
26 | return static_cast(value);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/include/common/constants.h:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 |
4 | #include "basic_types.h"
5 | #include
6 |
7 | namespace ostr::global_constant
8 | {
9 | static constexpr u64 INDEX_INVALID = std::numeric_limits::max();
10 | static constexpr u64 SIZE_INVALID = std::numeric_limits::max();
11 |
12 | static constexpr u64 TOLERANCE_EXPONENT = 3;
13 | static constexpr f64 TOLERANCE = 1e-3;
14 |
15 | static constexpr f64 GOLDEN_RATIO = 1.61803398874989484820458683436563811772030917980576286213544862270526046281890;
16 | }
17 |
--------------------------------------------------------------------------------
/include/common/definitions.h:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 |
4 | #ifndef OPEN_STRING_API
5 | #define OPEN_STRING_API
6 | #endif
7 |
8 | #ifndef OPEN_STRING_CODE_BLOCK
9 | #define OPEN_STRING_CODE_BLOCK(code) do{code}while(false)
10 | #endif
11 |
12 | #ifndef OPEN_STRING_STRINGIFY
13 | #define OPEN_STRING_STRINGIFY(x) #x
14 | #endif
15 | #ifndef OPEN_STRING_STRINGIFY_EXPANDED
16 | #define OPEN_STRING_STRINGIFY_EXPANDED(x) OPEN_STRING_STRINGIFY(x)
17 | #endif
18 |
19 | #ifndef OPEN_STRING_UNLIKELY
20 | #define OPEN_STRING_UNLIKELY(expression) (!!(expression))
21 | #endif
22 |
23 | #ifndef OPEN_STRING_CHECK_OR
24 | #define OPEN_STRING_CHECK_OR(result, expression, ...) \
25 | OPEN_STRING_CODE_BLOCK({ \
26 | if(OPEN_STRING_UNLIKELY(!(expression))) \
27 | { \
28 | OPEN_STRING_PRINT_FORMATTED_DEBUG_MESSAGE("\nCheck failed: {}\n\t[" __FILE__ ":" OPEN_STRING_STRINGIFY_EXPANDED(__LINE__) "] " OPEN_STRING_STRINGIFY(expression) "\n", __VA_ARGS__ ); \
29 | OPEN_STRING_DEBUG_BREAK(); \
30 | result; \
31 | } \
32 | })
33 | #endif
34 |
35 | #ifndef OPEN_STRING_CHECK
36 | #define OPEN_STRING_CHECK(expression, ...) OPEN_STRING_CHECK_OR(/* Do nothing */, expression, __VA_ARGS__)
37 | #endif
38 |
39 | #ifndef OPEN_STRING_RESULT_CHECK
40 | #define OPEN_STRING_RESULT_CHECK(result) OPEN_STRING_CODE_BLOCK( \
41 | HRESULT result__{ result }; \
42 | OPEN_STRING_CHECK(SUCCEEDED(result__), "Result check failed! {}", _com_error{ result__ }.ErrorMessage()); \
43 | )
44 | #endif
--------------------------------------------------------------------------------
/include/common/functions.h:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 | #include
4 | #include
5 |
6 | namespace ostr
7 | {
8 | [[nodiscard]] constexpr u64 minimum(const u64 a, const u64 b) noexcept
9 | {
10 | return std::min(a, b);
11 | }
12 |
13 | [[nodiscard]] constexpr u64 minimum(const std::initializer_list v) noexcept
14 | {
15 | return std::min(v);
16 | }
17 |
18 | [[nodiscard]] constexpr u64 maximum(const u64 a, const u64 b) noexcept
19 | {
20 | return std::max(a, b);
21 | }
22 |
23 | [[nodiscard]] constexpr u64 maximum(const std::initializer_list v) noexcept
24 | {
25 | return std::max(v);
26 | }
27 |
28 | [[nodiscard]] inline u64 round_ceil(const f64 v) noexcept
29 | {
30 | return static_cast(ceil(v));
31 | }
32 |
33 | [[nodiscard]] constexpr u64 power(const u64 base, const u64 exponent) noexcept
34 | {
35 | u64 result = 1;
36 | for(u64 i = 0; i < exponent; ++i)
37 | result *= base;
38 | return result;
39 | }
40 |
41 | template
42 | constexpr std::enable_if_t> bitwise_swap(T& a, T& b) noexcept
43 | {
44 | T temp = a;
45 | a = b;
46 | b = temp;
47 | }
48 |
49 | template
50 | std::enable_if_t> bitwise_swap(T& a, T& b) noexcept
51 | {
52 | std::array temp;
53 | std::copy_n(reinterpret_cast(&a), sizeof(T), temp.data());
54 | std::copy_n(reinterpret_cast(&b), sizeof(T), reinterpret_cast(&a));
55 | std::copy_n(temp.data(), sizeof(T), reinterpret_cast(&b));
56 | }
57 |
58 | namespace details
59 | {
60 | [[nodiscard]] constexpr u64 hash_byte_crc64_implementation(const byte b) noexcept
61 | {
62 | u64 result = 0;
63 | u64 c = b;
64 | c <<= 56;
65 | for (u64 j = 0; j < 8; j++)
66 | {
67 | if ((result ^ c) & 0x8000000000000000ull)
68 | {
69 | constexpr u64 crc64_ecma182_poly = 0x42F0E1EBA9EA3693ull;
70 | result = (result << 1) ^ crc64_ecma182_poly;
71 | }
72 | else
73 | {
74 | result <<= 1;
75 | }
76 | c <<= 1;
77 | }
78 | return result;
79 | }
80 | }
81 |
82 | [[nodiscard]] constexpr u64 hash_byte_crc64(const byte b) noexcept
83 | {
84 | constexpr auto h = details::hash_byte_crc64_implementation;
85 | constexpr u64 crc64_table[256] =
86 | {
87 | h(0), h(1), h(2), h(3), h(4), h(5), h(6), h(7),
88 | h(8), h(9), h(10), h(11), h(12), h(13), h(14), h(15),
89 | h(16), h(17), h(18), h(19), h(20), h(21), h(22), h(23),
90 | h(24), h(25), h(26), h(27), h(28), h(29), h(30), h(31),
91 | h(32), h(33), h(34), h(35), h(36), h(37), h(38), h(39),
92 | h(40), h(41), h(42), h(43), h(44), h(45), h(46), h(47),
93 | h(48), h(49), h(50), h(51), h(52), h(53), h(54), h(55),
94 | h(56), h(57), h(58), h(59), h(60), h(61), h(62), h(63),
95 | h(64), h(65), h(66), h(67), h(68), h(69), h(70), h(71),
96 | h(72), h(73), h(74), h(75), h(76), h(77), h(78), h(79),
97 | h(80), h(81), h(82), h(83), h(84), h(85), h(86), h(87),
98 | h(88), h(89), h(90), h(91), h(92), h(93), h(94), h(95),
99 | h(96), h(97), h(98), h(99), h(100), h(101), h(102), h(103),
100 | h(104), h(105), h(106), h(107), h(108), h(109), h(110), h(111),
101 | h(112), h(113), h(114), h(115), h(116), h(117), h(118), h(119),
102 | h(120), h(121), h(122), h(123), h(124), h(125), h(126), h(127),
103 | h(128), h(129), h(130), h(131), h(132), h(133), h(134), h(135),
104 | h(136), h(137), h(138), h(139), h(140), h(141), h(142), h(143),
105 | h(144), h(145), h(146), h(147), h(148), h(149), h(150), h(151),
106 | h(152), h(153), h(154), h(155), h(156), h(157), h(158), h(159),
107 | h(160), h(161), h(162), h(163), h(164), h(165), h(166), h(167),
108 | h(168), h(169), h(170), h(171), h(172), h(173), h(174), h(175),
109 | h(176), h(177), h(178), h(179), h(180), h(181), h(182), h(183),
110 | h(184), h(185), h(186), h(187), h(188), h(189), h(190), h(191),
111 | h(192), h(193), h(194), h(195), h(196), h(197), h(198), h(199),
112 | h(200), h(201), h(202), h(203), h(204), h(205), h(206), h(207),
113 | h(208), h(209), h(210), h(211), h(212), h(213), h(214), h(215),
114 | h(216), h(217), h(218), h(219), h(220), h(221), h(222), h(223),
115 | h(224), h(225), h(226), h(227), h(228), h(229), h(230), h(231),
116 | h(232), h(233), h(234), h(235), h(236), h(237), h(238), h(239),
117 | h(240), h(241), h(242), h(243), h(244), h(245), h(246), h(247),
118 | h(248), h(249), h(250), h(251), h(252), h(253), h(254), h(255)
119 | };
120 | return crc64_table[b];
121 | }
122 |
123 | template
124 | [[nodiscard]] constexpr u64 hash_sequence_crc64(const T* data, const u64 length, const u64 seed = 0) noexcept
125 | {
126 | static_assert(sizeof(T) == 1, "CRC64 hash only supports byte-sized types.");
127 |
128 | u64 ans = seed;
129 | for (u64 i = 0; i < length; ++i)
130 | {
131 | const T& value = data[i];
132 | const byte hash_byte = ((ans >> 56) ^ value) & 0xFF;
133 | const u64 hash_next = hash_byte_crc64(hash_byte);
134 | ans = hash_next ^ (ans << 8);
135 | }
136 | return ans;
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/include/common/linear_iterator.h:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 |
4 | #include "common/basic_types.h"
5 |
6 | namespace ostr
7 | {
8 | template
9 | struct linear_iterator
10 | {
11 | constexpr linear_iterator() noexcept = default;
12 |
13 | constexpr explicit linear_iterator(T* v) noexcept
14 | : data_{ v }
15 | { }
16 |
17 | [[nodiscard]] constexpr T* data() const noexcept
18 | {
19 | return this->data_;
20 | }
21 |
22 | [[nodiscard]] constexpr T& operator*() const noexcept
23 | {
24 | return *this->data_;
25 | }
26 |
27 | constexpr linear_iterator& operator+=(const i64 diff) noexcept
28 | {
29 | this->data_ += diff;
30 | return *this;
31 | }
32 |
33 | constexpr linear_iterator& operator-=(const i64 diff) noexcept
34 | {
35 | this->data_ -= diff;
36 | return *this;
37 | }
38 |
39 | constexpr linear_iterator& operator+=(const u64 diff) noexcept
40 | {
41 | this->data_ += diff;
42 | return *this;
43 | }
44 |
45 | constexpr linear_iterator& operator-=(const u64 diff) noexcept
46 | {
47 | this->data_ -= diff;
48 | return *this;
49 | }
50 |
51 | [[nodiscard]] constexpr linear_iterator operator+(const i64 diff) const noexcept
52 | {
53 | linear_iterator tmp = *this;
54 | tmp += diff;
55 | return tmp;
56 | }
57 |
58 | [[nodiscard]] constexpr linear_iterator operator-(const i64 diff) const noexcept
59 | {
60 | linear_iterator tmp = *this;
61 | tmp -= diff;
62 | return tmp;
63 | }
64 |
65 | [[nodiscard]] constexpr linear_iterator operator+(const u64 diff) const noexcept
66 | {
67 | linear_iterator tmp = *this;
68 | tmp += diff;
69 | return tmp;
70 | }
71 |
72 | [[nodiscard]] constexpr linear_iterator operator-(const u64 diff) const noexcept
73 | {
74 | linear_iterator tmp = *this;
75 | tmp -= diff;
76 | return tmp;
77 | }
78 |
79 | constexpr linear_iterator& operator++() noexcept
80 | {
81 | ++this->data_;
82 | return *this;
83 | }
84 |
85 | constexpr linear_iterator operator++(int) noexcept
86 | {
87 | const linear_iterator tmp = *this;
88 | ++*this;
89 | return tmp;
90 | }
91 |
92 | constexpr linear_iterator& operator--() noexcept
93 | {
94 | --this->data_;
95 | return *this;
96 | }
97 |
98 | constexpr linear_iterator operator--(int) noexcept
99 | {
100 | const linear_iterator tmp = *this;
101 | --*this;
102 | return tmp;
103 | }
104 |
105 | private:
106 | T* data_;
107 | };
108 |
109 | template
110 | [[nodiscard]] constexpr i64 operator-(const linear_iterator& lhs, const linear_iterator& rhs) noexcept
111 | {
112 | return lhs.data() - rhs.data();
113 | }
114 |
115 | template
116 | [[nodiscard]] constexpr bool operator==(const linear_iterator& lhs, const linear_iterator& rhs) noexcept
117 | {
118 | return lhs.data() == rhs.data();
119 | }
120 |
121 | template
122 | [[nodiscard]] constexpr bool operator!=(const linear_iterator& lhs, const linear_iterator& rhs) noexcept
123 | {
124 | return !(lhs == rhs);
125 | }
126 |
127 | template
128 | [[nodiscard]] constexpr bool operator<(const linear_iterator& lhs, const linear_iterator& rhs) noexcept
129 | {
130 | return lhs.data() < rhs.data();
131 | }
132 |
133 | template
134 | [[nodiscard]] constexpr bool operator>(const linear_iterator& lhs, const linear_iterator& rhs) noexcept
135 | {
136 | return rhs < lhs;
137 | }
138 |
139 | template
140 | [[nodiscard]] constexpr bool operator<=(const linear_iterator& lhs, const linear_iterator& rhs) noexcept
141 | {
142 | return rhs >= lhs;
143 | }
144 |
145 | template
146 | [[nodiscard]] constexpr bool operator>=(const linear_iterator& lhs, const linear_iterator& rhs) noexcept
147 | {
148 | return !(lhs < rhs);
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/include/common/platforms.h:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 |
4 | #if _WIN64
5 |
6 | #ifndef WIN32_LEAN_AND_MEAN
7 | #define WIN32_LEAN_AND_MEAN
8 | #endif
9 | #ifndef NOMINMAX
10 | #define NOMINMAX
11 | #endif
12 |
13 | #include
14 | #include
15 | #include
16 |
17 | #define OPEN_STRING_PRINT_SIMPLE_DEBUG_MESSAGE(...) \
18 | { \
19 | [](const char* message_format, auto&&...args) \
20 | { \
21 | const ostr::codeunit_sequence message = ostr::format(message_format, args...); \
22 | OutputDebugStringA(message.c_str()); \
23 | }(__VA_ARGS__); \
24 | } \
25 |
26 | #define OPEN_STRING_PRINT_FORMATTED_DEBUG_MESSAGE(...) \
27 | { \
28 | [](const char* debug_format, const char* message_format, auto&&...args) \
29 | { \
30 | const ostr::codeunit_sequence message = ostr::format(message_format, args...); \
31 | OutputDebugStringA(ostr::format(debug_format, message).c_str()); \
32 | }(__VA_ARGS__); \
33 | } \
34 |
35 | // Visual Studio will not trigger the breakpoint during single-step debugging without __nop()
36 | #define OPEN_STRING_DEBUG_BREAK() (__nop(), __debugbreak())
37 |
38 | #elif defined(__linux__) || defined(__MACH__)
39 |
40 | #define OPEN_STRING_PRINT_SIMPLE_DEBUG_MESSAGE(...)
41 | #define OPEN_STRING_PRINT_FORMATTED_DEBUG_MESSAGE(...)
42 | #define OPEN_STRING_DEBUG_BREAK()
43 |
44 | #endif
45 |
--------------------------------------------------------------------------------
/include/common/sequence.h:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 | #include
4 | #include "common/adapters.h"
5 | #include "common/basic_types.h"
6 | #include "common/assertion.h"
7 | #include "common/functions.h"
8 | #include "common/linear_iterator.h"
9 |
10 | namespace ostr
11 | {
12 | template
13 | struct OPEN_STRING_API sequence
14 | {
15 | public:
16 | sequence() noexcept;
17 |
18 | template
19 | explicit sequence(const std::array& array) noexcept;
20 |
21 | template
22 | explicit sequence(std::array&& array) noexcept;
23 |
24 | sequence(std::initializer_list list) noexcept;
25 |
26 | // Copy constructor is not allowed because element inside maybe not copyable.
27 | // TODO: Maybe we can use std::is_copy_constructible_v to check.
28 | sequence(const sequence& other) noexcept = delete;
29 | sequence(sequence&& other) noexcept;
30 |
31 | sequence& operator=(const sequence& other) noexcept = delete;
32 |
33 | // The purpose of this function is not clear.
34 | // Do you want to move the memory ownership from other to this?
35 | // Or do you want to just copy the data from other to this?
36 | // TODO: Two functions for two purposed.
37 | sequence& operator=(sequence&& other) noexcept = delete;
38 |
39 | ~sequence() noexcept;
40 |
41 | using const_iterator = linear_iterator;
42 | using iterator = linear_iterator;
43 |
44 | [[nodiscard]] const_iterator cbegin() const noexcept;
45 | [[nodiscard]] const_iterator cend() const noexcept;
46 | [[nodiscard]] iterator begin() noexcept;
47 | [[nodiscard]] iterator end() noexcept;
48 | [[nodiscard]] const_iterator begin() const noexcept;
49 | [[nodiscard]] const_iterator end() const noexcept;
50 |
51 | [[nodiscard]] bool is_empty() const noexcept;
52 |
53 | [[nodiscard]] bool operator==(const sequence& other) const noexcept;
54 |
55 | [[nodiscard]] u64 size() const noexcept;
56 | [[nodiscard]] u64 capacity() const noexcept;
57 |
58 | [[nodiscard]] T* data() noexcept;
59 | [[nodiscard]] const T* data() const noexcept;
60 |
61 | void empty() noexcept;
62 | void empty(u64 size) noexcept;
63 |
64 | [[nodiscard]] const T& read_at(const u64 index) const noexcept;
65 | [[nodiscard]] const T& read_from_last(const u64 index = 0) const noexcept;
66 | [[nodiscard]] T& access_at(const u64 index) noexcept;
67 | [[nodiscard]] T& access_from_last(const u64 index = 0) noexcept;
68 |
69 | void reserve(const u64 size) noexcept;
70 |
71 | void push_back_uninitialized(const u64 size = 1) noexcept;
72 |
73 | void resize_uninitialized(const u64 size) noexcept;
74 |
75 | template
76 | void resize(const u64 size, Args&&...args) noexcept;
77 |
78 | void push_back(T element) noexcept;
79 |
80 | template
81 | void emplace_back(Args&&...args) noexcept;
82 |
83 | template
84 | T1 pop_back() noexcept;
85 |
86 | void append(const T* const source, const u64 size) noexcept;
87 |
88 | private:
89 |
90 | [[nodiscard]] bool is_short() const noexcept;
91 |
92 | [[nodiscard]] T* data_at(u64 index) noexcept;
93 | [[nodiscard]] const T* data_at(u64 index) const noexcept;
94 |
95 | static constexpr u64 POINTER_SIZE = sizeof(void*);
96 | static constexpr u64 ELEMENT_SIZE = sizeof(T);
97 | static constexpr u64 ELEMENT_SHORT_CAPACITY = maximum({ POINTER_SIZE / ELEMENT_SIZE, I });
98 | static constexpr u64 STORAGE_SHORT_CAPACITY = maximum({ ELEMENT_SHORT_CAPACITY, 1 }) * ELEMENT_SIZE;
99 |
100 | struct short_sequence_storage
101 | {
102 | std::array stack_storage;
103 | };
104 |
105 | struct large_sequence_storage
106 | {
107 | T* heap_storage;
108 | };
109 |
110 | [[nodiscard]] short_sequence_storage& as_small() noexcept
111 | {
112 | return reinterpret_cast(this->storage_);
113 | }
114 |
115 | [[nodiscard]] const short_sequence_storage& as_small() const noexcept
116 | {
117 | return reinterpret_cast(this->storage_);
118 | }
119 |
120 | [[nodiscard]] const large_sequence_storage& as_large() const noexcept
121 | {
122 | return reinterpret_cast(this->storage_);
123 | }
124 |
125 | [[nodiscard]] large_sequence_storage& as_large() noexcept
126 | {
127 | return reinterpret_cast(this->storage_);
128 | }
129 |
130 | struct storage
131 | {
132 | std::array data;
133 |
134 | [[nodiscard]] byte* raw_data() noexcept
135 | {
136 | return *reinterpret_cast(this->data.data());
137 | }
138 | };
139 |
140 | private:
141 | u64 capacity_{ ELEMENT_SHORT_CAPACITY };
142 | u64 size_{ 0 };
143 | storage storage_{ };
144 | };
145 |
146 | template
147 | sequence::sequence() noexcept = default;
148 |
149 | template
150 | template
151 | sequence::sequence(const std::array& array) noexcept
152 | {
153 | this->reserve(array.size());
154 | std::copy_n(array.data(), array.size(), this->data());
155 | this->size_ = array.size();
156 | }
157 |
158 | template
159 | template
160 | sequence::sequence(std::array&& array) noexcept
161 | {
162 | this->reserve(array.size());
163 | std::move(array.data(), array.end(), this->data());
164 | this->size_ = array.size();
165 | }
166 |
167 | template
168 | sequence::sequence(std::initializer_list list) noexcept
169 | {
170 | this->reserve(list.size());
171 | std::copy_n(list.begin(), list.size(), this->data());
172 | this->size_ = list.size();
173 | }
174 |
175 | template
176 | sequence::sequence(sequence&& other) noexcept
177 | : capacity_{ other.capacity_ }
178 | , size_{ other.size_ }
179 | , storage_{ std::move(other.storage_) }
180 | {
181 | other.capacity_ = ELEMENT_SHORT_CAPACITY;
182 | other.size_ = 0;
183 | }
184 |
185 | template
186 | sequence::~sequence() noexcept
187 | {
188 | for(u64 i = 0; i < this->size(); ++i)
189 | std::destroy_at(this->data_at(i));
190 | this->size_ = 0;
191 | if(!this->is_short())
192 | {
193 | const byte* data = reinterpret_cast(this->as_large().heap_storage);
194 | allocator::deallocate_array(data);
195 | }
196 | }
197 |
198 | template
199 | typename sequence::const_iterator sequence::cbegin() const noexcept
200 | {
201 | return const_iterator{ this->data() };
202 | }
203 |
204 | template
205 | typename sequence::const_iterator sequence::cend() const noexcept
206 | {
207 | return const_iterator{ this->data_at(this->size_) };
208 | }
209 |
210 | template
211 | typename sequence::iterator sequence::begin() noexcept
212 | {
213 | return iterator{ this->data() };
214 | }
215 |
216 | template
217 | typename sequence::iterator sequence::end() noexcept
218 | {
219 | return iterator{ this->data_at(this->size_) };
220 | }
221 |
222 | template
223 | typename sequence::const_iterator sequence::begin() const noexcept
224 | {
225 | return this->cbegin();
226 | }
227 |
228 | template
229 | typename sequence::const_iterator sequence::end() const noexcept
230 | {
231 | return this->cend();
232 | }
233 |
234 | template
235 | bool sequence::is_empty() const noexcept
236 | {
237 | return this->size_ == 0;
238 | }
239 |
240 | template
241 | bool sequence::operator==(const sequence& other) const noexcept
242 | {
243 | if(this->size() != other.size())
244 | return false;
245 | for(u64 i = 0; i < this->size(); ++i)
246 | if(this->data_at(i) != other.data_at(i))
247 | return false;
248 | return true;
249 | }
250 |
251 | template
252 | u64 sequence::size() const noexcept
253 | {
254 | return this->size_;
255 | }
256 |
257 | template
258 | u64 sequence::capacity() const noexcept
259 | {
260 | return this->capacity_;
261 | }
262 |
263 | template
264 | T* sequence::data() noexcept
265 | {
266 | return this->is_short() ? this->as_small().stack_storage.data() : this->as_large().heap_storage;
267 | }
268 |
269 | template
270 | const T* sequence::data() const noexcept
271 | {
272 | return this->is_short() ? this->as_small().stack_storage.data() : this->as_large().heap_storage;
273 | }
274 |
275 | template
276 | void sequence::empty() noexcept
277 | {
278 | for(T& e : *this)
279 | std::destroy_at(&e);
280 | this->size_ = 0;
281 | }
282 |
283 | template
284 | void sequence::empty(u64 size) noexcept
285 | {
286 | this->empty();
287 | if(size >= this->capacity_)
288 | {
289 | T* allocated = allocator::allocate_array(size);
290 | if(!this->is_short())
291 | allocator::deallocate_array(this->data());
292 | this->as_large().heap_storage = allocated;
293 | this->capacity_ = size;
294 | }
295 | }
296 |
297 | template
298 | const T& sequence::read_at(const u64 index) const noexcept
299 | {
300 | OPEN_STRING_CHECK(index < this->size_, "index out of range");
301 | return *(this->data_at(index));
302 | }
303 |
304 | template
305 | const T& sequence::read_from_last(const u64 index) const noexcept
306 | {
307 | OPEN_STRING_CHECK(index < this->size_, "index out of range");
308 | return this->read_at(this->size_ - index - 1);
309 | }
310 |
311 | template
312 | T& sequence::access_at(const u64 index) noexcept
313 | {
314 | OPEN_STRING_CHECK(index < this->size_, "index out of range");
315 | return *(this->data_at(index));
316 | }
317 |
318 | template
319 | T& sequence::access_from_last(const u64 index) noexcept
320 | {
321 | OPEN_STRING_CHECK(index < this->size_, "index out of range");
322 | return this->access_at(this->size_ - index - 1);
323 | }
324 |
325 | template
326 | void sequence::reserve(const u64 size) noexcept
327 | {
328 | if(size <= this->capacity_)
329 | return;
330 | byte* allocated_byte = allocator::allocate_array(size * ELEMENT_SIZE);
331 | T* old_data = this->data();
332 | const auto source = reinterpret_cast(old_data);
333 | if(!this->is_empty())
334 | {
335 | const auto source_end = reinterpret_cast(old_data + this->size_);
336 | std::move(source, source_end, allocated_byte);
337 | }
338 | if(!this->is_short())
339 | allocator::deallocate_array(source);
340 | this->as_large().heap_storage = reinterpret_cast(allocated_byte);
341 | this->capacity_ = size;
342 | }
343 |
344 | template
345 | void sequence::push_back_uninitialized(const u64 size) noexcept
346 | {
347 | const u64 size_required = this->size_ + size;
348 | if(size_required > this->capacity_)
349 | {
350 | // Golden ratio is a good growth factor.
351 | // reference: https://stackoverflow.com/a/5232342
352 | // " I have an intuition (that is currently not backed up by any hard data)
353 | // that a growth rate in line with the golden ratio
354 | // (because of its relationship to the fibonacci sequence)
355 | // will prove to be the most efficient growth rate for real-world loads
356 | // in terms of minimizing both extra space used and time. "
357 | u64 grown_capacity = round_ceil(static_cast(maximum({ this->capacity_, 1 })) * global_constant::GOLDEN_RATIO);
358 | while(grown_capacity < size_required)
359 | grown_capacity = round_ceil(static_cast(grown_capacity) * global_constant::GOLDEN_RATIO);
360 | this->reserve(grown_capacity);
361 | }
362 | this->size_ = size_required;
363 | }
364 |
365 | template
366 | void sequence::resize_uninitialized(const u64 size) noexcept
367 | {
368 | if(const u64 current_size = this->size(); size > current_size)
369 | {
370 | this->push_back_uninitialized(size - current_size);
371 | }
372 | else
373 | {
374 | for(u64 i = size; i < current_size; ++i)
375 | std::destroy_at(this->data_at(i));
376 | this->size_ = size;
377 | }
378 | }
379 |
380 | template
381 | template
382 | void sequence::resize(const u64 size, Args&&... args) noexcept
383 | {
384 | const u64 origin_size = this->size();
385 | this->resize_uninitialized(size);
386 | for(u64 i = origin_size; i < size; ++i)
387 | allocator::placement_construct(this->data_at(i), std::forward(args)...);
388 | }
389 |
390 | template
391 | void sequence::push_back(T element) noexcept
392 | {
393 | this->emplace_back(std::move(element));
394 | }
395 |
396 | template
397 | template
398 | void sequence::emplace_back(Args&&... args) noexcept
399 | {
400 | this->push_back_uninitialized();
401 | T* const emplace_target = this->data_at(this->size_ - 1);
402 | allocator::placement_construct(emplace_target, std::forward(args)...);
403 | }
404 |
405 | template
406 | template
407 | T1 sequence::pop_back() noexcept
408 | {
409 | OPEN_STRING_CHECK(this->size_ != 0, "Pop failed! Sequence is empty!");
410 | T result = std::move(this->access_at(this->size_ - 1));
411 | std::destroy_at(this->data_at(this->size_ - 1));
412 | --this->size_;
413 | return result;
414 | }
415 |
416 | template
417 | void sequence::append(const T* const source, const u64 size) noexcept
418 | {
419 | this->push_back_uninitialized(size);
420 | std::copy_n(source, size, this->data_at(this->size_ - size));
421 | }
422 |
423 | template
424 | bool sequence::is_short() const noexcept
425 | {
426 | return this->capacity_ == ELEMENT_SHORT_CAPACITY;
427 | }
428 |
429 | template
430 | T* sequence::data_at(u64 index) noexcept
431 | {
432 | return this->data() + index;
433 | }
434 |
435 | template
436 | const T* sequence::data_at(u64 index) const noexcept
437 | {
438 | return this->data() + index;
439 | }
440 | }
441 |
--------------------------------------------------------------------------------
/include/format.h:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 |
4 | #include
5 | #include
6 | #include
7 | #include "common/platforms.h"
8 | #include "common/definitions.h"
9 | #include "codeunit_sequence.h"
10 |
11 | namespace ostr
12 | {
13 | namespace details
14 | {
15 | codeunit_sequence OPEN_STRING_API format_integer(const u64& value, const codeunit_sequence_view& specification);
16 | codeunit_sequence OPEN_STRING_API format_integer(const i64& value, const codeunit_sequence_view& specification);
17 | codeunit_sequence OPEN_STRING_API format_float(const f64& value, const codeunit_sequence_view& specification);
18 | }
19 |
20 | template
21 | [[nodiscard]] codeunit_sequence format(const Format& format_mold_literal, const Args&...args);
22 |
23 | template
24 | struct argument_formatter
25 | {
26 | static codeunit_sequence produce(const T& value, const codeunit_sequence_view& specification)
27 | {
28 | constexpr u64 size = sizeof(T);
29 | const auto reader = reinterpret_cast(&value);
30 | codeunit_sequence raw;
31 | for(u64 i = 0; i < size; ++i)
32 | {
33 | if(!raw.is_empty())
34 | raw += " "_cuqv;
35 | const byte memory = reader[i];
36 | raw += details::format_integer(static_cast(memory), "02x"_cuqv);
37 | }
38 |
39 | if(specification == "r"_cuqv) // output raw memory bytes
40 | return raw;
41 |
42 | OPEN_STRING_CHECK(false, "Undefined format with raw memory bytes:{}!", raw);
43 | return { };
44 | }
45 | };
46 |
47 | namespace details
48 | {
49 | class format_mold_view
50 | {
51 | public:
52 | // code-region-start: constructors
53 |
54 | explicit constexpr format_mold_view(codeunit_sequence_view format_mold) noexcept
55 | : format_mold_(std::move(format_mold))
56 | { }
57 |
58 | // code-region-end: constructors
59 |
60 | // code-region-start: iterators
61 |
62 | enum class run_type : u8
63 | {
64 | plain_text,
65 | escaped_brace,
66 | formatter,
67 | ending
68 | };
69 |
70 | struct const_iterator
71 | {
72 | constexpr const_iterator() noexcept = default;
73 |
74 | explicit constexpr const_iterator(codeunit_sequence_view v) noexcept
75 | : format_mold(std::move(v))
76 | { }
77 |
78 | constexpr const_iterator& initialize() noexcept
79 | {
80 | const auto [ first_type, first_from, first_size ] = this->parse_run(0);
81 | this->type = first_type;
82 | this->from = first_from;
83 | this->size = first_size;
84 | return *this;
85 | }
86 |
87 | [[nodiscard]] constexpr std::tuple operator*() const noexcept
88 | {
89 | return { this->type, this->format_mold.subview(this->from, this->size) };
90 | }
91 |
92 | constexpr const_iterator& operator++()
93 | {
94 | if(this->from == global_constant::INDEX_INVALID)
95 | {
96 | this->type = run_type::ending;
97 | this->from = global_constant::INDEX_INVALID;
98 | this->size = SIZE_MAX;
99 | return *this;
100 | }
101 | const auto [ next_type, next_from, next_size ] = this->parse_run(this->from + this->size);
102 | this->type = next_type;
103 | this->from = next_from;
104 | this->size = next_size;
105 | return *this;
106 | }
107 |
108 | constexpr const_iterator operator++(int) noexcept
109 | {
110 | const_iterator tmp = *this;
111 | ++*this;
112 | return tmp;
113 | }
114 |
115 | [[nodiscard]] constexpr bool operator==(const const_iterator& rhs) const noexcept
116 | {
117 | return this->type == rhs.type && this->from == rhs.from && this->size == rhs.size;
118 | }
119 |
120 | [[nodiscard]] constexpr bool operator!=(const const_iterator& rhs) const noexcept
121 | {
122 | return !(*this == rhs);
123 | }
124 |
125 | struct result_parse_run
126 | {
127 | run_type type = run_type::plain_text;
128 | u64 from = global_constant::INDEX_INVALID;
129 | u64 size = SIZE_MAX;
130 | };
131 | [[nodiscard]] constexpr result_parse_run parse_run(const u64 from_index) const
132 | {
133 | const u64 index = this->format_mold.index_of_any("{}"_cuqv, from_index);
134 | if(index == global_constant::INDEX_INVALID)
135 | {
136 | if(from_index == this->format_mold.size())
137 | return { run_type::ending };
138 | return { run_type::plain_text, from_index, this->format_mold.size() - from_index };
139 | }
140 | if(from_index != index)
141 | return { run_type::plain_text, from_index, index - from_index };
142 | if(format_mold.read_at(index) == format_mold.read_at(index + 1))
143 | return { run_type::escaped_brace, index, 2 };
144 | OPEN_STRING_CHECK(format_mold.read_at(index) != '}', "Unclosed right brace is not allowed!");
145 | const u64 index_next = this->format_mold.index_of_any("{}"_cuqv, index + 1);
146 | OPEN_STRING_CHECK(index_next != global_constant::INDEX_INVALID && format_mold.read_at(index_next) != '{', "Unclosed left brace is not allowed!");
147 | return { run_type::formatter, index, index_next - index + 1 };
148 | }
149 |
150 | codeunit_sequence_view format_mold;
151 | run_type type = run_type::ending;
152 | u64 from = global_constant::INDEX_INVALID;
153 | u64 size = SIZE_MAX;
154 | };
155 |
156 | [[nodiscard]] constexpr const_iterator begin() const noexcept
157 | {
158 | return const_iterator{ this->format_mold_ }.initialize();
159 | }
160 |
161 | [[nodiscard]] constexpr const_iterator end() const noexcept
162 | {
163 | return const_iterator{ this->format_mold_ };
164 | }
165 |
166 | [[nodiscard]] constexpr const_iterator cbegin() const noexcept
167 | {
168 | return this->begin();
169 | }
170 |
171 | [[nodiscard]] constexpr const_iterator cend() const noexcept
172 | {
173 | return this->end();
174 | }
175 |
176 | // code-region-end: iterators
177 |
178 | private:
179 | codeunit_sequence_view format_mold_{ };
180 | };
181 |
182 | template
183 | codeunit_sequence format_argument_value(const void* value, const codeunit_sequence_view specification)
184 | {
185 | return argument_formatter::produce(*static_cast(value), specification);
186 | }
187 |
188 | struct argument_value_package
189 | {
190 | using argument_value_formatter_type = codeunit_sequence(*)(const void* value, codeunit_sequence_view specification);
191 |
192 | const void* value;
193 | argument_value_formatter_type argument_value_formatter;
194 |
195 | template
196 | explicit constexpr argument_value_package(const T& v) noexcept
197 | : value(static_cast(&v))
198 | , argument_value_formatter(format_argument_value)
199 | { }
200 |
201 | [[nodiscard]] codeunit_sequence produce(const codeunit_sequence_view& specification) const
202 | {
203 | return this->argument_value_formatter(this->value, specification);
204 | }
205 | };
206 |
207 | template
208 | codeunit_sequence produce_format(const format_mold_view& format_mold, const Args&...args)
209 | {
210 | constexpr u64 argument_count = sizeof...(Args);
211 | const std::array arguments {{ argument_value_package{ args } ... }};
212 |
213 | enum class indexing_type : u8
214 | {
215 | unknown,
216 | manual,
217 | automatic
218 | };
219 | indexing_type index_type = indexing_type::unknown;
220 | u64 next_index = 0;
221 | codeunit_sequence result;
222 | for(const auto [ type, run ] : format_mold)
223 | {
224 | switch (type)
225 | {
226 | case format_mold_view::run_type::plain_text:
227 | result += run;
228 | break;
229 | case format_mold_view::run_type::escaped_brace:
230 | result += run.read_at(0);
231 | break;
232 | case format_mold_view::run_type::formatter:
233 | {
234 | const auto [ index_run, specification ] = run.subview(1, run.size() - 2).split(":"_cuqv);
235 | u64 current_index = next_index;
236 | if (index_run.is_empty())
237 | {
238 | OPEN_STRING_CHECK(index_type != indexing_type::manual, "Manual index is not allowed mixing with automatic index!");
239 | index_type = indexing_type::automatic;
240 | ++next_index;
241 | }
242 | else
243 | {
244 | OPEN_STRING_CHECK(index_type != indexing_type::automatic, "Automatic index is not allowed mixing with manual index!");
245 | index_type = indexing_type::manual;
246 | [[maybe_unused]] const auto [ last, error ] =
247 | std::from_chars(index_run.data(), index_run.cend().data(), current_index);
248 | OPEN_STRING_CHECK(last == index_run.cend().data(), "Invalid format index [{}]!", index_run);
249 | }
250 | OPEN_STRING_CHECK(current_index < argument_count, "Invalid format index [{}]: Index should be less than count of argument [{}]!", current_index, argument_count);
251 | result += arguments[current_index].produce(specification);
252 | }
253 | break;
254 | case format_mold_view::run_type::ending:
255 | // Unreachable
256 | break;
257 | }
258 | }
259 | return result;
260 | }
261 | }
262 |
263 | template
264 | [[nodiscard]] codeunit_sequence format(const Format& format_mold_literal, const Args&...args)
265 | {
266 | return details::produce_format(details::format_mold_view{ details::view_sequence(format_mold_literal) }, args...);
267 | }
268 |
269 | // code-region-start: formatter specializations for built-in types
270 |
271 | template
272 | struct argument_formatter && std::is_signed_v>>
273 | {
274 | static codeunit_sequence produce(const T& value, const codeunit_sequence_view& specification)
275 | {
276 | return details::format_integer(static_cast(value), specification);
277 | }
278 | };
279 |
280 | template
281 | struct argument_formatter && std::is_unsigned_v>>
282 | {
283 | static codeunit_sequence produce(const T& value, const codeunit_sequence_view& specification)
284 | {
285 | return details::format_integer(static_cast(value), specification);
286 | }
287 | };
288 |
289 | template
290 | struct argument_formatter>>
291 | {
292 | static codeunit_sequence produce(const T& value, const codeunit_sequence_view& specification)
293 | {
294 | return details::format_float(static_cast(value), specification);
295 | }
296 | };
297 |
298 | template<>
299 | struct argument_formatter
300 | {
301 | static codeunit_sequence produce(const char* value, const codeunit_sequence_view& specification)
302 | {
303 | return codeunit_sequence{value};
304 | }
305 | };
306 |
307 | template
308 | struct argument_formatter
309 | {
310 | static codeunit_sequence produce(const char (&value)[N], const codeunit_sequence_view& specification)
311 | {
312 | return codeunit_sequence{value};
313 | }
314 | };
315 |
316 | template<>
317 | struct argument_formatter
318 | {
319 | static codeunit_sequence produce(const codeunit_sequence_view& value, const codeunit_sequence_view& specification)
320 | {
321 | return codeunit_sequence{value};
322 | }
323 | };
324 |
325 | template<>
326 | struct argument_formatter
327 | {
328 | static const codeunit_sequence& produce(const codeunit_sequence& value, const codeunit_sequence_view& specification)
329 | {
330 | return value;
331 | }
332 | };
333 |
334 | template<>
335 | struct argument_formatter
336 | {
337 | static codeunit_sequence produce(std::nullptr_t, const codeunit_sequence_view& specification)
338 | {
339 | return codeunit_sequence{"nullptr"_cuqv};
340 | }
341 | };
342 |
343 | template
344 | struct argument_formatter
345 | {
346 | static codeunit_sequence produce(const T* value, const codeunit_sequence_view& specification)
347 | {
348 | return details::format_integer(reinterpret_cast(value), "#016x"_cuqv);
349 | }
350 | };
351 |
352 | // code-region-end: formatter specializations for built-in types
353 | }
354 |
--------------------------------------------------------------------------------
/include/text.h:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 |
4 | #include "text_view.h"
5 | #include "codeunit_sequence.h"
6 | #include "common/sequence.h"
7 |
8 | namespace ostr
9 | {
10 | class OPEN_STRING_API text
11 | {
12 | public:
13 |
14 | text() noexcept;
15 | text(const text&) noexcept;
16 | text(text&&) noexcept;
17 | text& operator=(const text&) noexcept;
18 | text& operator=(text&&) noexcept;
19 | ~text();
20 |
21 | text(const char* str) noexcept;
22 | text(const text_view& view) noexcept;
23 | text(codeunit_sequence sequence) noexcept;
24 | text(const codeunit_sequence_view& sequence) noexcept;
25 |
26 | static text from_utf8(const char* string_utf8) noexcept;
27 | static text from_utf16(const char16_t* string_utf16) noexcept;
28 | static text from_utf32(const char32_t* string_utf32) noexcept;
29 | static text from_wide(const wchar_t* wide_string) noexcept;
30 |
31 | template
32 | static text build(const Args&... argument);
33 | template
34 | static text join(const Container& container, const text_view& separator) noexcept;
35 |
36 | // code-region-start: iterator
37 |
38 | using const_iterator = text_view::const_iterator;
39 |
40 | struct OPEN_STRING_API iterator
41 | {
42 | iterator() noexcept = default;
43 | iterator(text& t, u64 from, u64 size) noexcept;
44 |
45 | struct OPEN_STRING_API codepoint_accessor
46 | {
47 | explicit codepoint_accessor(iterator& iter) noexcept;
48 |
49 | codepoint_accessor& operator=(char c) noexcept;
50 | codepoint_accessor& operator=(char32_t cp) noexcept;
51 | codepoint_accessor& operator=(const codepoint& cp) noexcept;
52 | codepoint_accessor& operator=(const text_view& tv) noexcept;
53 | codepoint_accessor& operator=(const text& t) noexcept;
54 |
55 | [[nodiscard]] codepoint get_codepoint() const noexcept;
56 | [[nodiscard]] operator codepoint() const noexcept;
57 |
58 | private:
59 |
60 | void assign(const codeunit_sequence_view& sequence_view) const noexcept;
61 |
62 | iterator& it_;
63 | };
64 |
65 | [[nodiscard]] bool is_valid() const noexcept;
66 | [[nodiscard]] u64 raw_size() const noexcept;
67 | [[nodiscard]] codepoint get_codepoint() const noexcept;
68 | [[nodiscard]] codepoint operator*() const noexcept;
69 | [[nodiscard]] codepoint_accessor operator*() noexcept;
70 | iterator& operator++() noexcept;
71 | iterator operator++(int) noexcept;
72 | iterator& operator--() noexcept;
73 | iterator operator--(int) noexcept;
74 | [[nodiscard]] bool operator==(const iterator& rhs) const noexcept;
75 | [[nodiscard]] bool operator!=(const iterator& rhs) const noexcept;
76 |
77 | u64 from = global_constant::INDEX_INVALID;
78 | u64 size = SIZE_MAX;
79 | text* owner = nullptr;
80 | };
81 |
82 | [[nodiscard]] iterator begin() noexcept;
83 | [[nodiscard]] const_iterator begin() const noexcept;
84 | [[nodiscard]] iterator end() noexcept;
85 | [[nodiscard]] const_iterator end() const noexcept;
86 | [[nodiscard]] const_iterator cbegin() const noexcept;
87 | [[nodiscard]] const_iterator cend() const noexcept;
88 |
89 | // code-region-end: iterator
90 |
91 | [[nodiscard]] codeunit_sequence& raw() & noexcept;
92 | [[nodiscard]] const codeunit_sequence& raw() const& noexcept;
93 | [[nodiscard]] codeunit_sequence raw() && noexcept;
94 |
95 | [[nodiscard]] text_view view() const noexcept;
96 |
97 | [[nodiscard]] u64 size() const noexcept;
98 | [[nodiscard]] bool is_empty() const noexcept;
99 |
100 | [[nodiscard]] bool operator==(const text_view& rhs) const noexcept;
101 | [[nodiscard]] bool operator==(const text& rhs) const noexcept;
102 | [[nodiscard]] bool operator==(const char* rhs) const noexcept;
103 | [[nodiscard]] bool operator!=(const text_view& rhs) const noexcept;
104 | [[nodiscard]] bool operator!=(const text& rhs) const noexcept;
105 | [[nodiscard]] bool operator!=(const char* rhs) const noexcept;
106 |
107 | text& append(const text_view& rhs) noexcept;
108 | text& append(const text& rhs) noexcept;
109 | text& append(const codepoint& cp) noexcept;
110 | text& append(const char* rhs) noexcept;
111 | text& append(char codeunit, u64 count = 1) noexcept;
112 |
113 | text& operator+=(const text_view& rhs) noexcept;
114 | text& operator+=(const text& rhs) noexcept;
115 | text& operator+=(const codepoint& cp) noexcept;
116 | text& operator+=(const char* rhs) noexcept;
117 | text& operator+=(char codeunit) noexcept;
118 |
119 | [[nodiscard]] text_view subview(u64 from, u64 size = global_constant::SIZE_INVALID) const noexcept;
120 | text& subtext(u64 from, u64 size = global_constant::SIZE_INVALID) noexcept;
121 |
122 | [[nodiscard]] u64 index_of(const text_view& pattern, u64 from = 0, u64 size = global_constant::SIZE_INVALID) const noexcept;
123 | [[nodiscard]] u64 last_index_of(const text_view& pattern, u64 from = 0, u64 size = global_constant::SIZE_INVALID) const noexcept;
124 |
125 | [[nodiscard]] u64 count(const text_view& pattern, u64 from = 0, u64 size = global_constant::SIZE_INVALID) const noexcept;
126 |
127 | [[nodiscard]] bool starts_with(const text_view& prefix) const noexcept;
128 | [[nodiscard]] bool ends_with(const text_view& suffix) const noexcept;
129 |
130 | /**
131 | * Empty the text without reallocation.
132 | */
133 | void empty() noexcept;
134 |
135 | // How to reserve for unknown size?
136 | // If you got a sequence of code units,
137 | // please use t.raw().reserve(size);
138 | text& reserve(u64) noexcept = delete;
139 |
140 | text& write_at(u64 index, codepoint cp) noexcept;
141 | [[nodiscard]] codepoint read_at(u64 index) const noexcept;
142 |
143 | /// @note: Please use operator[] for read-only access, or use write_at method for write access.
144 | [[nodiscard]] codepoint operator[](u64 index) const noexcept;
145 |
146 | text& reverse(u64 from = 0, u64 size = SIZE_MAX) noexcept;
147 |
148 | u32 split(const text_view& splitter, sequence& pieces, bool cull_empty = true) const noexcept;
149 |
150 | text& replace(const text_view& destination, const text_view& source, u64 from = 0, u64 size = SIZE_MAX);
151 | text& replace(const text_view& destination, u64 from = 0, u64 size = SIZE_MAX);
152 |
153 | text& self_remove_prefix(const text_view& prefix) noexcept;
154 | text& self_remove_suffix(const text_view& suffix) noexcept;
155 | [[nodiscard]] text_view view_remove_prefix(const text_view& prefix) const noexcept;
156 | [[nodiscard]] text_view view_remove_suffix(const text_view& suffix) const noexcept;
157 |
158 | text& self_trim_start(const text_view& characters = text_view(" \t")) noexcept;
159 | text& self_trim_end(const text_view& characters = text_view(" \t")) noexcept;
160 | text& self_trim(const text_view& characters = text_view(" \t")) noexcept;
161 |
162 | [[nodiscard]] text_view view_trim_start(const text_view& characters = text_view(" \t")) const noexcept;
163 | [[nodiscard]] text_view view_trim_end(const text_view& characters = text_view(" \t")) const noexcept;
164 | [[nodiscard]] text_view view_trim(const text_view& characters = text_view(" \t")) const noexcept;
165 |
166 | [[nodiscard]] const char* c_str() const noexcept;
167 |
168 | private:
169 |
170 | codeunit_sequence sequence_{ };
171 |
172 | };
173 |
174 | namespace details
175 | {
176 | template<>
177 | [[nodiscard]] constexpr codeunit_sequence_view view_sequence(const text_view& v)
178 | {
179 | return v.raw();
180 | }
181 | template<>
182 | [[nodiscard]] inline codeunit_sequence_view view_sequence(const text& v)
183 | {
184 | return v.view().raw();
185 | }
186 | }
187 |
188 | template
189 | text text::build(const Args&... argument)
190 | {
191 | return { codeunit_sequence::build(argument...) };
192 | }
193 |
194 | template
195 | text text::join(const Container& container, const text_view& separator) noexcept
196 | {
197 | return { codeunit_sequence::join(container, separator.raw()) };
198 | }
199 |
200 | OPEN_STRING_API [[nodiscard]] bool operator==(const text_view& lhs, const text& rhs) noexcept;
201 |
202 | template<>
203 | struct argument_formatter
204 | {
205 | static codeunit_sequence produce(const text_view& value, const codeunit_sequence_view& specification)
206 | {
207 | return codeunit_sequence{ value.raw() };
208 | }
209 | };
210 |
211 | template<>
212 | struct argument_formatter
213 | {
214 | static codeunit_sequence produce(const text& value, const codeunit_sequence_view& specification)
215 | {
216 | return value.raw();
217 | }
218 | };
219 | }
220 |
--------------------------------------------------------------------------------
/include/text_view.h:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 | #include "common/definitions.h"
4 |
5 | #include
6 | #include "codeunit_sequence_view.h"
7 |
8 | namespace ostr
9 | {
10 | // UTF-8 encoded string
11 | class OPEN_STRING_API text_view
12 | {
13 | public:
14 |
15 | // code-region-start: constructors
16 |
17 | constexpr text_view() noexcept = default;
18 | constexpr text_view(const text_view&) noexcept = default;
19 | constexpr text_view(text_view&&) noexcept = default;
20 | constexpr text_view& operator=(const text_view&) noexcept = default;
21 | constexpr text_view& operator=(text_view&&) noexcept = default;
22 | ~text_view() noexcept = default;
23 |
24 | constexpr text_view(const char* data, const u64 count) noexcept
25 | : view_{ data, count }
26 | { }
27 | constexpr text_view(const char* str) noexcept
28 | : view_{ str }
29 | { }
30 | constexpr text_view(codeunit_sequence_view view) noexcept
31 | : view_{ std::move(view) }
32 | { }
33 | constexpr text_view(const codepoint& cp) noexcept
34 | : view_{ cp.raw(), cp.size() }
35 | { }
36 |
37 | constexpr text_view& operator=(const char* str) noexcept
38 | {
39 | *this = text_view(str);
40 | return *this;
41 | }
42 |
43 | // code-region-end: constructors
44 |
45 | // code-region-start: iterators
46 |
47 | struct const_iterator
48 | {
49 | constexpr const_iterator() noexcept = default;
50 |
51 | explicit constexpr const_iterator(codeunit_sequence_view::const_iterator v) noexcept
52 | : value(v)
53 | { }
54 |
55 | [[nodiscard]] constexpr u64 raw_size() const noexcept
56 | {
57 | return unicode::parse_utf8_length(*this->value);
58 | }
59 |
60 | [[nodiscard]] constexpr codepoint get_codepoint() const noexcept
61 | {
62 | return codepoint{ this->value.data() };
63 | }
64 |
65 | [[nodiscard]] constexpr codepoint operator*() const noexcept
66 | {
67 | return this->get_codepoint();
68 | }
69 |
70 | [[nodiscard]] constexpr i64 operator-(const const_iterator& rhs) const noexcept
71 | {
72 | if(rhs > *this)
73 | return - (rhs - *this);
74 |
75 | const_iterator cur = rhs;
76 | i64 diff = 0;
77 | while(cur != *this)
78 | {
79 | ++cur;
80 | ++diff;
81 | }
82 | return diff;
83 | }
84 |
85 | constexpr const_iterator& operator+=(const i64 diff) noexcept
86 | {
87 | if(diff != 0)
88 | {
89 | using self_operator = const_iterator& (const_iterator::*)();
90 | const auto& op = diff > 0 ? static_cast(&const_iterator::operator++) : static_cast(&const_iterator::operator--);
91 | const u64 diff_abs = abs(diff);
92 | for(u64 i = 0; i < diff_abs; ++i)
93 | (this->*op)();
94 | }
95 | return *this;
96 | }
97 |
98 | constexpr const_iterator& operator-=(const i64 diff) noexcept
99 | {
100 | return this->operator+=(-diff);
101 | }
102 |
103 | [[nodiscard]] constexpr const_iterator operator+(const i64 diff) const noexcept
104 | {
105 | const_iterator tmp = *this;
106 | tmp += diff;
107 | return tmp;
108 | }
109 |
110 | [[nodiscard]] constexpr const_iterator operator-(const i64 diff) const noexcept
111 | {
112 | const_iterator tmp = *this;
113 | tmp -= diff;
114 | return tmp;
115 | }
116 |
117 | constexpr const_iterator& operator++() noexcept
118 | {
119 | this->value += static_cast(this->raw_size());
120 | return *this;
121 | }
122 |
123 | constexpr const_iterator operator++(int) noexcept
124 | {
125 | const const_iterator tmp = *this;
126 | ++*this;
127 | return tmp;
128 | }
129 |
130 | constexpr const_iterator& operator--() noexcept
131 | {
132 | --this->value;
133 | while(this->raw_size() == 0)
134 | --this->value;
135 | return *this;
136 | }
137 |
138 | constexpr const_iterator operator--(int) noexcept
139 | {
140 | const const_iterator tmp = *this;
141 | --*this;
142 | return tmp;
143 | }
144 |
145 | [[nodiscard]] constexpr bool operator==(const const_iterator& rhs) const noexcept
146 | {
147 | return this->value == rhs.value;
148 | }
149 |
150 | [[nodiscard]] constexpr bool operator!=(const const_iterator& rhs) const noexcept
151 | {
152 | return !(*this == rhs);
153 | }
154 |
155 | [[nodiscard]] constexpr bool operator<(const const_iterator& rhs) const noexcept
156 | {
157 | return this->value < rhs.value;
158 | }
159 |
160 | [[nodiscard]] constexpr bool operator>(const const_iterator& rhs) const noexcept
161 | {
162 | return rhs < *this;
163 | }
164 |
165 | [[nodiscard]] constexpr bool operator<=(const const_iterator& rhs) const noexcept
166 | {
167 | return rhs >= *this;
168 | }
169 |
170 | [[nodiscard]] constexpr bool operator>=(const const_iterator& rhs) const noexcept
171 | {
172 | return !(*this < rhs);
173 | }
174 |
175 | codeunit_sequence_view::const_iterator value;
176 | };
177 |
178 | [[nodiscard]] constexpr const_iterator begin() const noexcept
179 | {
180 | return const_iterator{ this->view_.cbegin() };
181 | }
182 |
183 | [[nodiscard]] constexpr const_iterator end() const noexcept
184 | {
185 | return const_iterator{ this->view_.cend() };
186 | }
187 |
188 | [[nodiscard]] constexpr const_iterator cbegin() const noexcept
189 | {
190 | return this->begin();
191 | }
192 |
193 | [[nodiscard]] constexpr const_iterator cend() const noexcept
194 | {
195 | return this->end();
196 | }
197 |
198 | // code-region-end: iterators
199 |
200 | [[nodiscard]] constexpr bool operator==(const text_view& rhs) const noexcept
201 | {
202 | return this->view_ == rhs.view_;
203 | }
204 |
205 | [[nodiscard]] constexpr bool operator!=(const text_view& rhs) const noexcept
206 | {
207 | return !this->operator==(rhs);
208 | }
209 |
210 | [[nodiscard]] constexpr bool operator==(const char* rhs) const noexcept
211 | {
212 | return this->view_ == rhs;
213 | }
214 |
215 | [[nodiscard]] constexpr bool operator!=(const char* rhs) const noexcept
216 | {
217 | return this->view_ != rhs;
218 | }
219 |
220 | [[nodiscard]] constexpr u64 size() const noexcept
221 | {
222 | return this->get_codepoint_index( this->view_.size() );
223 | }
224 |
225 | [[nodiscard]] constexpr codeunit_sequence_view raw() const noexcept
226 | {
227 | return this->view_;
228 | }
229 |
230 | // This is not allowed for getting rid of misuse.
231 | [[nodiscard]] const char* c_str() const noexcept = delete;
232 |
233 | /// @return Is this an empty string
234 | [[nodiscard]] constexpr bool is_empty() const noexcept
235 | {
236 | return this->view_.is_empty();
237 | }
238 |
239 | [[nodiscard]] constexpr text_view subview(const u64 from, const u64 size = SIZE_MAX) const noexcept
240 | {
241 | u64 raw_from = from; u64 raw_size = size;
242 | this->get_codeunit_range(raw_from, raw_size);
243 | return { this->view_.subview(raw_from, raw_size) };
244 | }
245 |
246 | [[nodiscard]] constexpr codepoint read_at(const u64 index) const noexcept
247 | {
248 | const char* data = &this->view_.read_at(this->get_codeunit_index(index));
249 | return codepoint{ data };
250 | }
251 |
252 | [[nodiscard]] constexpr codepoint operator[](const u64 index) const noexcept
253 | {
254 | return this->read_at(index);
255 | }
256 |
257 | [[nodiscard]] constexpr u64 index_of(const text_view& pattern, const u64 from = 0, const u64 size = SIZE_MAX) const noexcept
258 | {
259 | if(pattern.is_empty())
260 | return global_constant::INDEX_INVALID;
261 | u64 raw_from = from; u64 raw_size = size;
262 | this->get_codeunit_range(raw_from, raw_size);
263 | const u64 found_raw_index = this->view_.index_of(pattern.view_, raw_from, raw_size);
264 | if(found_raw_index == global_constant::INDEX_INVALID)
265 | return global_constant::INDEX_INVALID;
266 | return this->get_codepoint_index(found_raw_index);
267 | }
268 |
269 | [[nodiscard]] constexpr u64 index_of(const codepoint& pattern, const u64 from = 0, const u64 size = SIZE_MAX) const noexcept
270 | {
271 | return this->index_of(text_view{ pattern }, from, size);
272 | }
273 |
274 | [[nodiscard]] constexpr u64 last_index_of(const text_view& pattern, const u64 from = 0, const u64 size = SIZE_MAX) const noexcept
275 | {
276 | if(pattern.is_empty())
277 | return global_constant::INDEX_INVALID;
278 | u64 raw_from = from; u64 raw_size = size;
279 | this->get_codeunit_range(raw_from, raw_size);
280 | const u64 found_raw_index = this->view_.last_index_of(pattern.view_, raw_from, raw_size);
281 | if(found_raw_index == global_constant::INDEX_INVALID)
282 | return global_constant::INDEX_INVALID;
283 | return this->get_codepoint_index(found_raw_index);
284 | }
285 |
286 | [[nodiscard]] u64 count(const text_view& pattern, const u64 from, const u64 size) const noexcept
287 | {
288 | return this->view_.count(pattern.view_, from, size);
289 | }
290 |
291 | [[nodiscard]] constexpr bool contains(const text_view& pattern) const noexcept
292 | {
293 | return this->view_.contains(pattern.view_);
294 | }
295 |
296 | [[nodiscard]] constexpr bool contains(const codepoint& pattern) const noexcept
297 | {
298 | return this->contains(text_view{ pattern });
299 | }
300 |
301 | [[nodiscard]] constexpr std::array split(const text_view& splitter) const noexcept
302 | {
303 | const auto [ first, second ] = this->view_.split(splitter.view_);
304 | return { text_view{ first }, text_view{ second } };
305 | }
306 |
307 | [[nodiscard]] constexpr bool starts_with(const text_view& prefix) const noexcept
308 | {
309 | return this->view_.starts_with(prefix.view_);
310 | }
311 |
312 | [[nodiscard]] constexpr bool ends_with(const text_view& suffix) const noexcept
313 | {
314 | return this->view_.ends_with(suffix.view_);
315 | }
316 |
317 | [[nodiscard]] constexpr text_view remove_prefix(const text_view& prefix) const noexcept
318 | {
319 | return text_view{ this->view_.remove_prefix(prefix.view_) };
320 | }
321 |
322 | [[nodiscard]] constexpr text_view remove_suffix(const text_view& suffix) const noexcept
323 | {
324 | return text_view{ this->view_.remove_suffix(suffix.view_) };
325 | }
326 |
327 | [[nodiscard]] constexpr text_view trim_start(const text_view& text_set = text_view(" \t")) const noexcept
328 | {
329 | if(this->is_empty())
330 | return { };
331 | if(text_set.is_empty())
332 | return *this;
333 | auto iterator = this->begin();
334 | const u64 size = this->size();
335 | for(u64 i = 0; i < size; ++i)
336 | {
337 | if(const auto cp = *iterator; !text_set.contains(cp))
338 | return this->subview(i);
339 | ++iterator;
340 | }
341 | return { };
342 | }
343 |
344 | [[nodiscard]] constexpr text_view trim_end(const text_view& text_set = text_view(" \t")) const noexcept
345 | {
346 | if(this->is_empty())
347 | return { };
348 | if(text_set.is_empty())
349 | return *this;
350 | auto iterator = this->end();
351 | const u64 size = this->size();
352 | for(u64 i = size; i > 0; --i)
353 | {
354 | --iterator;
355 | if(const auto cp = *iterator; !text_set.contains(cp))
356 | return this->subview(0, i);
357 | }
358 | return { };
359 | }
360 |
361 | [[nodiscard]] constexpr text_view trim(const text_view& text_set = text_view(" \t")) const noexcept
362 | {
363 | return this->trim_start(text_set).trim_end(text_set);
364 | }
365 |
366 | [[nodiscard]] constexpr u64 get_codepoint_index(const u64 codeunit_index) const noexcept
367 | {
368 | const u64 view_size = this->view_.size();
369 | u64 index = 0;
370 | u64 offset = 0;
371 | while(offset < codeunit_index && offset < view_size)
372 | {
373 | const u8 sequence_length = unicode::parse_utf8_length(this->view_.read_at(offset));
374 | const u8 offset_delta = sequence_length == 0 ? 1 : sequence_length;
375 | offset += offset_delta;
376 | ++index;
377 | }
378 | return index;
379 | }
380 |
381 | [[nodiscard]] constexpr u64 get_codeunit_index(const u64 codepoint_index) const noexcept
382 | {
383 | const u64 view_size = this->view_.size();
384 | u64 index = 0;
385 | u64 offset = 0;
386 | while(index < codepoint_index && offset < view_size)
387 | {
388 | const u8 sequence_length = unicode::parse_utf8_length(this->view_.read_at(offset));
389 | offset += sequence_length;
390 | ++index;
391 | }
392 | return offset;
393 | }
394 |
395 | constexpr void get_codeunit_range(u64& from, u64& size) const noexcept
396 | {
397 | const u64 origin_from = from;
398 | const u64 actual_size = minimum(size, this->size() - from);
399 | const u64 last = this->get_codeunit_index(origin_from + actual_size);
400 | from = this->get_codeunit_index(origin_from);
401 | size = last - from;
402 | }
403 |
404 | // todo: a better conversion approach
405 | [[nodiscard]] constexpr std::optional try_as_integer() const
406 | {
407 | if(this->is_empty())
408 | return { };
409 | i32 result = 0;
410 | i32 signal = 1;
411 | codeunit_sequence_view raw_view = this->raw();
412 | if(raw_view.starts_with("+"_cuqv))
413 | {
414 | raw_view = raw_view.subview(1);
415 | }
416 | if(raw_view.starts_with("-"_cuqv))
417 | {
418 | raw_view = raw_view.subview(1);
419 | signal = -1;
420 | }
421 | for(const char c : raw_view)
422 | {
423 | if(c < '0' || c > '9')
424 | return { };
425 | result *= 10;
426 | result += (c - '0');
427 | }
428 | return result * signal;
429 | }
430 |
431 | private:
432 |
433 | codeunit_sequence_view view_{ };
434 |
435 | };
436 | }
437 |
438 | inline namespace literal
439 | {
440 | [[nodiscard]] constexpr ostr::text_view operator""_txtv(const char* str, const size_t len) noexcept
441 | {
442 | return { str, len };
443 | }
444 | }
445 |
--------------------------------------------------------------------------------
/include/unicode.h:
--------------------------------------------------------------------------------
1 |
2 | #pragma once
3 |
4 | #include "common/basic_types.h"
5 | #include
6 |
7 | namespace ostr
8 | {
9 | namespace unicode
10 | {
11 | static constexpr u64 UTF8_SEQUENCE_MAXIMUM_LENGTH = 4;
12 |
13 | [[nodiscard]] constexpr char32_t get_utf8_maximum_codepoint(const u64 codeunit_count)
14 | {
15 | constexpr std::array maximum_codepoints
16 | {
17 | 0x7F,
18 | 0x7FF,
19 | 0xFFFF,
20 | 0x1FFFFF,
21 | };
22 | return maximum_codepoints.at(codeunit_count - 1);
23 | }
24 |
25 | namespace utf16
26 | {
27 | static constexpr u64 SEQUENCE_MAXIMUM_LENGTH = 2;
28 |
29 | static constexpr char32_t LEADING_SURROGATE_HEADER = 0xD800;
30 | static constexpr char32_t TRAILING_SURROGATE_HEADER = 0xDC00;
31 | static constexpr char32_t LEADING_SURROGATE_MINIMUM = 0xD800;
32 | static constexpr char32_t TRAILING_SURROGATE_MINIMUM = 0xDC00;
33 | static constexpr char32_t LEADING_SURROGATE_MAXIMUM = 0xDBFF;
34 | static constexpr char32_t TRAILING_SURROGATE_MAXIMUM = 0xDFFF;
35 | static constexpr char32_t SURROGATE_MASK = 0x03FF;
36 | static constexpr char32_t SINGLE_UNIT_MAXIMUM_VALUE = 0xFFFF;
37 |
38 | [[nodiscard]] constexpr bool is_leading_surrogate(const char16_t c) noexcept
39 | {
40 | return c >= LEADING_SURROGATE_MINIMUM && c <= LEADING_SURROGATE_MAXIMUM;
41 | }
42 |
43 | [[nodiscard]] constexpr bool is_trailing_surrogate(const char16_t c) noexcept
44 | {
45 | return c >= TRAILING_SURROGATE_MINIMUM && c <= TRAILING_SURROGATE_MAXIMUM;
46 | }
47 |
48 | [[nodiscard]] constexpr bool is_surrogate(const char16_t c) noexcept
49 | {
50 | return c >= LEADING_SURROGATE_MINIMUM && c <= TRAILING_SURROGATE_MAXIMUM;
51 | }
52 |
53 | [[nodiscard]] constexpr u64 parse_utf16_length(const char16_t c) noexcept
54 | {
55 | return is_leading_surrogate(c) ? SEQUENCE_MAXIMUM_LENGTH : 1;
56 | }
57 | }
58 |
59 | /**
60 | * @param length length of a utf8 sequence
61 | * @return mask of utf8 code unit
62 | */
63 | [[nodiscard]] constexpr char get_utf8_mask(const u64 length) noexcept
64 | {
65 | constexpr char masks[] =
66 | {
67 | 0b00111111_as_char,
68 | 0b01111111_as_char,
69 | 0b00011111_as_char,
70 | 0b00001111_as_char,
71 | 0b00000111_as_char,
72 | };
73 | return masks[length];
74 | }
75 |
76 | /**
77 | * @param c start of a utf8 sequence
78 | * @return length of utf8 sequence, return 0 if this is not start of a utf8 sequence
79 | */
80 | [[nodiscard]] constexpr u8 parse_utf8_length(const char c) noexcept
81 | {
82 | if (c == 0)
83 | return 1;
84 | constexpr char mask = 0b10000000_as_char;
85 | u8 size = 0;
86 | u8 v = c;
87 | while (v & mask)
88 | {
89 | ++size;
90 | v <<= 1;
91 | }
92 | // 1 byte when start with 0
93 | // 0 byte when start with 10
94 | // 2 bytes when start with 110
95 | // n bytes when start with 1..(n)..0
96 | return size > 1 ? size : 1 - size;
97 | }
98 |
99 | [[nodiscard]] constexpr u64 parse_utf8_length(const char16_t utf16) noexcept
100 | {
101 | if (utf16 <= get_utf8_maximum_codepoint(1))
102 | return 1;
103 | if (utf16 <= get_utf8_maximum_codepoint(2))
104 | return 2;
105 | if (utf16::is_surrogate(utf16))
106 | return 4;
107 | return 3;
108 | }
109 |
110 | [[nodiscard]] constexpr u64 parse_utf8_length(const char32_t utf32) noexcept
111 | {
112 | if (utf32 <= get_utf8_maximum_codepoint(1))
113 | return 1;
114 | if (utf32 <= get_utf8_maximum_codepoint(2))
115 | return 2;
116 | if (utf32 <= get_utf8_maximum_codepoint(3))
117 | return 3;
118 | return 4;
119 | }
120 |
121 | /**
122 | * @param utf8 input utf-8 code unit sequence
123 | * @param length length of utf-8 code unit sequence
124 | * @return codepoint of input utf8 code unit sequence
125 | */
126 | [[nodiscard]] constexpr char32_t utf8_to_utf32(char const* const utf8, const u64 length) noexcept
127 | {
128 | if (!utf8)
129 | return 0;
130 | const char c0 = utf8[0];
131 | const char lead_mask = get_utf8_mask(length);
132 | char32_t utf32 = c0 & lead_mask;
133 | constexpr char following_mask = get_utf8_mask(0);
134 | for (u64 i = 1; i < length; ++i)
135 | {
136 | constexpr u64 bits_following = 6;
137 | const char ci = utf8[i];
138 | utf32 <<= bits_following;
139 | utf32 |= ci & following_mask;
140 | }
141 | return utf32;
142 | }
143 |
144 | /**
145 | * @param utf32 input utf-32 code unit
146 | * @return decoded utf-8 code unit sequence from utf-32 code unit
147 | */
148 | [[nodiscard]] constexpr std::array utf32_to_utf8(const char32_t utf32) noexcept
149 | {
150 | if (utf32 <= get_utf8_maximum_codepoint(1)) return
151 | { static_cast(utf32),
152 | 0, 0, 0 };
153 | if (utf32 <= get_utf8_maximum_codepoint(2)) return
154 | { static_cast((utf32 >> 6) | 0xc0),
155 | static_cast((utf32 & 0x3f) | 0x80),
156 | 0, 0 };
157 | if (utf32 <= get_utf8_maximum_codepoint(3)) return
158 | { static_cast((utf32 >> 12) | 0xe0),
159 | static_cast(((utf32 >> 6) & 0x3f) | 0x80),
160 | static_cast((utf32 & 0x3f) | 0x80),
161 | 0 };
162 | return
163 | { static_cast((utf32 >> 18) | 0xf0),
164 | static_cast(((utf32 >> 12) & 0x3f) | 0x80),
165 | static_cast(((utf32 >> 6) & 0x3f) | 0x80),
166 | static_cast((utf32 & 0x3f) | 0x80) };
167 | }
168 |
169 | [[nodiscard]] constexpr char32_t utf16_to_utf32(char16_t const* const utf16, const u64 length) noexcept
170 | {
171 | // 1 0
172 | // 9876543210 9876543210
173 | // |||||||||| ||||||||||
174 | // [110110]9876543210 |||||||||| high surrogate
175 | // [110111]9876543210 low surrogate
176 | return length == 1 ? utf16[0] : ((utf16[0] & utf16::SURROGATE_MASK) << 10) + (utf16[1] & utf16::SURROGATE_MASK);
177 | }
178 |
179 | [[nodiscard]] constexpr std::array