mirror of
				https://github.com/davidgiven/fluxengine.git
				synced 2025-10-24 11:11:02 -07:00 
			
		
		
		
	Compare commits
	
		
			105 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 603baee777 | ||
|  | e105b7f498 | ||
|  | bb3fbccb50 | ||
|  | c8edcd963d | ||
|  | 3b60cdc707 | ||
|  | ea061d65c9 | ||
|  | da64c0237f | ||
|  | d2b1602881 | ||
|  | 1afd45068c | ||
|  | f01b30e112 | ||
|  | b5f7fbe14e | ||
|  | 8b6073ccbb | ||
|  | f902c759df | ||
|  | 996fdbc0f5 | ||
|  | 9ff3e3b42a | ||
|  | 0a5604521e | ||
|  | 786636ef5d | ||
|  | 18bdb27225 | ||
|  | faca35dec0 | ||
|  | f8813daae3 | ||
|  | da5a20390f | ||
|  | 3ab3db92f5 | ||
|  | a3cd3dd9dc | ||
|  | 918868e9e8 | ||
|  | cf05a25445 | ||
|  | 5d5399a267 | ||
|  | 2de7af0ba5 | ||
|  | 0382c304ad | ||
|  | 182d9946fe | ||
|  | f24e4029b4 | ||
|  | 4ebda29171 | ||
|  | 53026f3d02 | ||
|  | 99c0e95a2f | ||
|  | dfa56c6b08 | ||
|  | 0419df4b2d | ||
|  | 70bdcd0978 | ||
|  | 022df995aa | ||
|  | dcbe7ec41d | ||
|  | df4d27eefe | ||
|  | 8f233f55e9 | ||
|  | 7db49aec21 | ||
|  | b5eaec0778 | ||
|  | 06b126a2e7 | ||
|  | ed96ebac79 | ||
|  | c6e34d2d88 | ||
|  | 53ac8bad79 | ||
|  | d2e163bc3b | ||
|  | 1404123281 | ||
|  | 01a7afd28a | ||
|  | 3a42911e6f | ||
|  | 8e5d52f2c7 | ||
|  | dfff5d7230 | ||
|  | 19b63786c8 | ||
|  | 5293e1c18b | ||
|  | f200bb8b00 | ||
|  | ed11a5c412 | ||
|  | cdcc63f519 | ||
|  | 7096e9fd9c | ||
|  | c8fe56ea95 | ||
|  | 8a2a58b1a5 | ||
|  | 42aec98368 | ||
|  | 6d73371a79 | ||
|  | 4d60ff8e67 | ||
|  | 311ff4a89f | ||
|  | 5d57957a6e | ||
|  | f89adce02d | ||
|  | 3e505f47bc | ||
|  | 06e29142e6 | ||
|  | 15a69f6dcb | ||
|  | 0f763fe06b | ||
|  | f5adb89338 | ||
|  | 36b120bdbe | ||
|  | cc169d414f | ||
|  | 0fcb2075e0 | ||
|  | 2bda78fb40 | ||
|  | e878c6eef6 | ||
|  | 9ce405cec5 | ||
|  | f064d413b3 | ||
|  | e5a3331f24 | ||
|  | 6f99f88b29 | ||
|  | 8ff0153708 | ||
|  | c7273c06da | ||
|  | cd36caccc7 | ||
|  | a022aab28a | ||
|  | 949e9c216d | ||
|  | 3fcf7d4e69 | ||
|  | e335621558 | ||
|  | 9a0357c67b | ||
|  | 0953039369 | ||
|  | d4a8eb5847 | ||
|  | d48ab7c84e | ||
|  | c43b88ac0b | ||
|  | 76ffbb96ba | ||
|  | b6b28c8a02 | ||
|  | a736e1da05 | ||
|  | a8cc280574 | ||
|  | da9d9385b9 | ||
|  | 149e5c6fba | ||
|  | e14da81b48 | ||
|  | 49a0a6fdb3 | ||
|  | da678dc52d | ||
|  | 6ff68f3f06 | ||
|  | 33feda7208 | ||
|  | 38af98ec9b | ||
|  | d6a11d7164 | 
							
								
								
									
										39
									
								
								.github/workflows/ccpp.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										39
									
								
								.github/workflows/ccpp.yml
									
									
									
									
										vendored
									
									
								
							| @@ -20,10 +20,33 @@ jobs: | ||||
|         path: 'fluxengine-testdata' | ||||
|     - name: apt | ||||
|       run: | | ||||
|         sudo apt install libudev-dev libsqlite3-dev protobuf-compiler libwxgtk3.0-gtk3-dev libfmt-dev libprotobuf-dev wx-common | ||||
|         sudo apt install libudev-dev libsqlite3-dev protobuf-compiler libwxgtk3.0-gtk3-dev libfmt-dev libprotobuf-dev | ||||
|     - name: make | ||||
|       run: CXXFLAGS="-Wp,-D_GLIBCXX_ASSERTIONS" make -j`nproc` -C fluxengine | ||||
|  | ||||
|   #build-linux-debian-11: | ||||
|   #  runs-on: ubuntu-22.04 | ||||
|   #  container: debian:11 | ||||
|   #  steps: | ||||
|   #  - uses: actions/checkout@v4 | ||||
|   #    with: | ||||
|   #      repository: 'davidgiven/fluxengine' | ||||
|   #      path: 'fluxengine' | ||||
|   #  - uses: actions/checkout@v4 | ||||
|   #    with: | ||||
|   #      repository: 'davidgiven/fluxengine-testdata' | ||||
|   #      path: 'fluxengine-testdata' | ||||
|   #  - name: apt update | ||||
|   #    run: apt update | ||||
|   #  - name: apt | ||||
|   #    run: > | ||||
|   #      apt install -y python3 make xz-utils python3 python3-hamcrest | ||||
|   #      protobuf-compiler libprotobuf-dev libsqlite3-dev | ||||
|   #      libfmt-dev libprotobuf-dev wx-common pkg-config | ||||
|   #      libudev-dev g++ libwxgtk3.0-gtk3-dev | ||||
|   #  - name: make | ||||
|   #    run: make -C fluxengine | ||||
|        | ||||
|   build-macos-current: | ||||
|     strategy: | ||||
|       matrix: | ||||
| @@ -42,7 +65,7 @@ jobs: | ||||
|       run: | | ||||
|         brew install sqlite pkg-config libusb protobuf wxwidgets fmt make coreutils dylibbundler libjpeg | ||||
|     - name: make | ||||
|       run: gmake -C fluxengine -j2 | ||||
|       run: gmake -C fluxengine | ||||
|     - name: Upload build artifacts | ||||
|       uses: actions/upload-artifact@v4 | ||||
|       with: | ||||
| @@ -55,15 +78,15 @@ jobs: | ||||
|     steps: | ||||
|     - name: setup WSL | ||||
|       run: | | ||||
|         curl -L https://github.com/WhitewaterFoundry/Fedora-Remix-for-WSL/releases/download/40.1.0/Fedora-Remix-for-WSL-SL_40.1.0.0_x64_arm64.msixbundle -o fedora.msixbundle | ||||
|         unzip fedora.msixbundle Fedora-Remix-for-WSL-SL_40.1.0.0_x64.msix | ||||
|         unzip Fedora-Remix-for-WSL-SL_40.1.0.0_x64.msix install.tar.gz | ||||
|         curl -L https://github.com/WhitewaterFoundry/Fedora-Remix-for-WSL/releases/download/41.0.0/Fedora-Remix-for-WSL-SL_41.0.0.0_x64_arm64.msixbundle -o fedora.msixbundle | ||||
|         unzip fedora.msixbundle Fedora-Remix-for-WSL-SL_41.0.0.0_x64.msix | ||||
|         unzip Fedora-Remix-for-WSL-SL_41.0.0.0_x64.msix install.tar.gz | ||||
|         wsl --update | ||||
|         wsl --set-default-version 2 | ||||
|         wsl --set-default-version 1 | ||||
|         wsl --import fedora fedora install.tar.gz | ||||
|         wsl --set-default fedora | ||||
|         wsl sh -c 'dnf -y install https://github.com/rpmsphere/noarch/raw/master/r/rpmsphere-release-38-1.noarch.rpm' | ||||
|         wsl sh -c 'dnf -y install --setop=install_weak_deps=False gcc gcc-c++ protobuf-c-compiler protobuf-devel fmt-devel systemd-devel sqlite-devel wxGTK-devel mingw32-gcc mingw32-gcc-c++ mingw32-zlib-static mingw32-protobuf-static mingw32-sqlite-static mingw32-wxWidgets3-static mingw32-libpng-static mingw32-libjpeg-static mingw32-libtiff-static mingw32-nsis png2ico' | ||||
|         wsl sh -c 'dnf -y install https://github.com/rpmsphere/noarch/raw/master/r/rpmsphere-release-40-1.noarch.rpm' | ||||
|         wsl sh -c 'dnf -y install gcc gcc-c++ protobuf-c-compiler protobuf-devel fmt-devel systemd-devel sqlite-devel wxGTK-devel mingw32-gcc mingw32-gcc-c++ mingw32-zlib-static mingw32-protobuf-static mingw32-sqlite-static mingw32-wxWidgets3-static mingw32-libpng-static mingw32-libjpeg-static mingw32-libtiff-static mingw32-nsis png2ico' | ||||
|  | ||||
|     - name: fix line endings | ||||
|       run: | | ||||
|   | ||||
							
								
								
									
										14
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							| @@ -16,15 +16,15 @@ jobs: | ||||
|     steps: | ||||
|     - name: setup WSL | ||||
|       run: | | ||||
|         curl -L https://github.com/WhitewaterFoundry/Fedora-Remix-for-WSL/releases/download/39.0.1/Fedora-Remix-for-WSL-SL_39.0.1.0_x64_arm64.msixbundle -o fedora.msixbundle | ||||
|         unzip fedora.msixbundle Fedora-Remix-for-WSL-SL_39.0.1.0_x64.msix | ||||
|         unzip Fedora-Remix-for-WSL-SL_39.0.1.0_x64.msix install.tar.gz | ||||
|         curl -L https://github.com/WhitewaterFoundry/Fedora-Remix-for-WSL/releases/download/41.0.0/Fedora-Remix-for-WSL-SL_41.0.0.0_x64_arm64.msixbundle -o fedora.msixbundle | ||||
|         unzip fedora.msixbundle Fedora-Remix-for-WSL-SL_41.0.0.0_x64.msix | ||||
|         unzip Fedora-Remix-for-WSL-SL_41.0.0.0_x64.msix install.tar.gz | ||||
|         wsl --update | ||||
|         wsl --set-default-version 2 | ||||
|         wsl --set-default-version 1 | ||||
|         wsl --import fedora fedora install.tar.gz | ||||
|         wsl --set-default fedora | ||||
|         wsl sh -c 'dnf -y install https://github.com/rpmsphere/noarch/raw/master/r/rpmsphere-release-38-1.noarch.rpm' | ||||
|         wsl sh -c 'dnf -y install --setop=install_weak_deps=False gcc gcc-c++ protobuf-c-compiler protobuf-devel fmt-devel systemd-devel sqlite-devel wxGTK-devel mingw32-gcc mingw32-gcc-c++ mingw32-zlib-static mingw32-protobuf-static mingw32-sqlite-static mingw32-wxWidgets3-static mingw32-libpng-static mingw32-libjpeg-static mingw32-libtiff-static mingw32-nsis png2ico' | ||||
|         wsl sh -c 'dnf -y install https://github.com/rpmsphere/noarch/raw/master/r/rpmsphere-release-40-1.noarch.rpm' | ||||
|         wsl sh -c 'dnf -y install gcc gcc-c++ protobuf-c-compiler protobuf-devel fmt-devel systemd-devel sqlite-devel wxGTK-devel mingw32-gcc mingw32-gcc-c++ mingw32-zlib-static mingw32-protobuf-static mingw32-sqlite-static mingw32-wxWidgets3-static mingw32-libpng-static mingw32-libjpeg-static mingw32-libtiff-static mingw32-nsis png2ico' | ||||
|  | ||||
|     - name: fix line endings | ||||
|       run: | | ||||
| @@ -97,7 +97,7 @@ jobs: | ||||
|  | ||||
|     - name: make | ||||
|       run: | | ||||
|         gmake -j2 | ||||
|         gmake | ||||
|         mv FluxEngine.pkg FluxEngine-${{ runner.arch }}.pkg | ||||
|  | ||||
|     - name: tag | ||||
|   | ||||
							
								
								
									
										49
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										49
									
								
								Makefile
									
									
									
									
									
								
							| @@ -1,10 +1,17 @@ | ||||
| BUILDTYPE ?= host | ||||
| ifeq ($(BUILDTYPE),) | ||||
|     buildtype_Darwin = osx | ||||
|     buildtype_Haiku = haiku | ||||
|     BUILDTYPE := $(buildtype_$(shell uname -s )) | ||||
|         ifeq ($(BUILDTYPE),) | ||||
|                 BUILDTYPE := unix | ||||
|         endif | ||||
| endif | ||||
| export BUILDTYPE | ||||
|  | ||||
| ifeq ($(BUILDTYPE),windows) | ||||
| 	MINGW = i686-w64-mingw32- | ||||
| 	CC = $(MINGW)gcc | ||||
| 	CXX = $(MINGW)g++ -std=c++17 | ||||
| 	CXX = $(MINGW)g++ -std=c++20 | ||||
| 	CFLAGS += -g -O3 | ||||
| 	CXXFLAGS += \ | ||||
| 		-fext-numeric-literals \ | ||||
| @@ -18,15 +25,21 @@ ifeq ($(BUILDTYPE),windows) | ||||
| 	EXT = .exe | ||||
| else | ||||
| 	CC = gcc | ||||
| 	CXX = g++ -std=c++17 | ||||
| 	CFLAGS = -g -O3 | ||||
| 	CXX = g++ -std=c++20 | ||||
| 	CFLAGS = -g -O3 \ | ||||
| 		-Wno-deprecated-enum-float-conversion \ | ||||
| 		-Wno-deprecated-enum-enum-conversion | ||||
| 	LDFLAGS = | ||||
| 	AR = ar | ||||
| 	PKG_CONFIG = pkg-config | ||||
| 	ifeq ($(BUILDTYPE),osx) | ||||
| 	else | ||||
| 		LDFLAGS += -pthread -Wl,--no-as-needed | ||||
| 	endif | ||||
| endif | ||||
|  | ||||
| HOSTCC = gcc | ||||
| HOSTCXX = g++ -std=c++17 | ||||
| HOSTCXX = g++ -std=c++20 | ||||
| HOSTCFLAGS = -g -O3 | ||||
| HOSTLDFLAGS = | ||||
|  | ||||
| @@ -73,24 +86,26 @@ binaries: all | ||||
| tests: all | ||||
| 	 | ||||
| README.md: $(OBJ)/scripts/+mkdocindex/mkdocindex$(EXT) | ||||
| 	@echo $(PROGRESSINFO) MKDOC $@ | ||||
| 	@echo $(PROGRESSINFO)MKDOC $@ | ||||
| 	@csplit -s -f$(OBJ)/README. README.md '/<!-- FORMATSSTART -->/' '%<!-- FORMATSEND -->%' | ||||
| 	@(cat $(OBJ)/README.00 && $< && cat $(OBJ)/README.01) > README.md | ||||
|  | ||||
| .PHONY: tests | ||||
|  | ||||
| .PHONY: install install-bin | ||||
| install:: all install-bin | ||||
|  | ||||
| clean:: | ||||
| 	$(hide) rm -rf $(REALOBJ) | ||||
|  | ||||
| install-bin: | ||||
| 	@echo "INSTALL" | ||||
| 	$(hide) install -D -v "$(OBJ)/src+fluxengine/src+fluxengine" "$(DESTDIR)$(BINDIR)/fluxengine" | ||||
| 	$(hide) install -D -v "$(OBJ)/src/gui+gui/gui+gui" "$(DESTDIR)$(BINDIR)/fluxengine-gui" | ||||
| 	$(hide) install -D -v "$(OBJ)/tools+brother120tool/tools+brother120tool" "$(DESTDIR)$(BINDIR)/brother120tool" | ||||
| 	$(hide) install -D -v "$(OBJ)/tools+brother240tool/tools+brother240tool" "$(DESTDIR)$(BINDIR)/brother240tool" | ||||
| 	$(hide) install -D -v "$(OBJ)/tools+upgrade-flux-file/tools+upgrade-flux-file" "$(DESTDIR)$(BINDIR)/upgrade-flux-file" | ||||
|  | ||||
| include build/ab.mk | ||||
|  | ||||
| DOCKERFILES = \ | ||||
| 	debian11 \ | ||||
|     debian12 \ | ||||
|     fedora40 \ | ||||
|     fedora41 \ | ||||
| 	manjaro | ||||
|  | ||||
| docker-%: tests/docker/Dockerfile.% | ||||
| 	docker build -t $* -f $< . | ||||
|  | ||||
| .PHONY: dockertests | ||||
| dockertests: $(foreach f,$(DOCKERFILES), docker-$(strip $f) .WAIT) | ||||
|   | ||||
							
								
								
									
										11
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								README.md
									
									
									
									
									
								
							| @@ -125,6 +125,7 @@ choices because they can store multiple types of file system. | ||||
| | [`hplif`](doc/disk-hplif.md) | Hewlett-Packard LIF: a variety of disk formats used by HP | 🦄 | 🦄 | LIF  | | ||||
| | [`ibm`](doc/disk-ibm.md) | IBM PC: Generic PC 3.5"/5.25" disks | 🦄 | 🦄 | FATFS  | | ||||
| | [`icl30`](doc/disk-icl30.md) | ICL Model 30: CP/M; 263kB 35-track DSSD | 🦖 |  | CPMFS  | | ||||
| | [`juku`](doc/disk-juku.md) | Juku E5104: CP/M |  |  | CPMFS  | | ||||
| | [`mac`](doc/disk-mac.md) | Macintosh: 400kB/800kB 3.5" GCR | 🦄 | 🦄 | MACHFS  | | ||||
| | [`micropolis`](doc/disk-micropolis.md) | Micropolis: 100tpi MetaFloppy disks | 🦄 | 🦄 |  | | ||||
| | [`ms2000`](doc/disk-ms2000.md) | : MS2000 Microdisk Development System |  |  | MICRODOS  | | ||||
| @@ -136,6 +137,7 @@ choices because they can store multiple types of file system. | ||||
| | [`rx50`](doc/disk-rx50.md) | Digital RX50: 400kB 5.25" 80-track 10-sector SSDD | 🦖 | 🦖 |  | | ||||
| | [`smaky6`](doc/disk-smaky6.md) | Smaky 6: 308kB 5.25" 77-track 16-sector SSDD, hard sectored | 🦖 |  | SMAKY6  | | ||||
| | [`tartu`](doc/disk-tartu.md) | Tartu: The Palivere and variations | 🦄 | 🦖 | CPMFS  | | ||||
| | [`ti99`](doc/disk-ti99.md) | TI-99: 90kB 35-track SSSD | 🦖 |  |  | | ||||
| | [`tids990`](doc/disk-tids990.md) | Texas Instruments DS990: 1126kB 8" DSSD | 🦖 | 🦖 |  | | ||||
| | [`tiki`](doc/disk-tiki.md) | Tiki 100: CP/M |  |  | CPMFS  | | ||||
| | [`victor9k`](doc/disk-victor9k.md) | Victor 9000 / Sirius One: 1224kB 5.25" DSDD GCR | 🦖 | 🦖 |  | | ||||
| @@ -257,6 +259,15 @@ package, written by Robert Leslie et al, taken from | ||||
| https://www.mars.org/home/rob/proj/hfs. It is GPL 2.0 licensed. Please see the | ||||
| contents of the directory for the full text. | ||||
|  | ||||
| As an exception, `dep/lexy` contains a partial copy of the lexy package, written | ||||
| by foonathen@github, taken from https://github.com/foonathan/lexy. It is BSL 1.0 | ||||
| licensed. Please see the contents of the directory for the full text. | ||||
|  | ||||
| As an exception, `dep/alphanum` contains a copy of the alphanum package, | ||||
| written by Dave Koelle, taken from | ||||
| https://web.archive.org/web/20210207124255/davekoelle.com/alphanum.html. It is | ||||
| MIT licensed. Please see the source for the full text. | ||||
|  | ||||
| __Important:__ Because of all these exceptions, if you distribute the | ||||
| FluxEngine package as a whole, you must comply with the terms of _all_ of the | ||||
| licensing terms. This means that __effectively the FluxEngine package is | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| #include "lib/core/globals.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "aeslanier.h" | ||||
| #include "arch/aeslanier/aeslanier.h" | ||||
| #include "lib/core/crc.h" | ||||
| #include "lib/data/fluxmap.h" | ||||
| #include "lib/data/fluxmapreader.h" | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| #include "lib/core/globals.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "agat.h" | ||||
| #include "arch/agat/agat.h" | ||||
| #include "lib/core/bytes.h" | ||||
| #include "fmt/format.h" | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| #include "lib/core/globals.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "agat.h" | ||||
| #include "arch/agat/agat.h" | ||||
| #include "lib/core/crc.h" | ||||
| #include "lib/data/fluxmap.h" | ||||
| #include "lib/data/fluxmapreader.h" | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| #include "lib/core/utils.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "lib/encoders/encoders.h" | ||||
| #include "agat.h" | ||||
| #include "arch/agat/agat.h" | ||||
| #include "lib/core/crc.h" | ||||
| #include "lib/data/image.h" | ||||
| #include "lib/data/layout.h" | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| #include "lib/core/globals.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "amiga.h" | ||||
| #include "arch/amiga/amiga.h" | ||||
| #include "lib/core/bytes.h" | ||||
| #include "fmt/format.h" | ||||
|  | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
| #include "protocol.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "lib/data/sector.h" | ||||
| #include "amiga.h" | ||||
| #include "arch/amiga/amiga.h" | ||||
| #include "lib/core/bytes.h" | ||||
| #include "fmt/format.h" | ||||
| #include "lib/decoders/decoders.pb.h" | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| #include "lib/core/utils.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "lib/encoders/encoders.h" | ||||
| #include "amiga.h" | ||||
| #include "arch/amiga/amiga.h" | ||||
| #include "lib/core/crc.h" | ||||
| #include "lib/data/image.h" | ||||
| #include "arch/amiga/amiga.pb.h" | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
| #include "protocol.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "lib/data/sector.h" | ||||
| #include "apple2.h" | ||||
| #include "arch/apple2/apple2.h" | ||||
| #include "arch/apple2/apple2.pb.h" | ||||
| #include "lib/decoders/decoders.pb.h" | ||||
| #include "lib/core/bytes.h" | ||||
|   | ||||
| @@ -4,7 +4,7 @@ | ||||
| #include "lib/data/fluxpattern.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "lib/encoders/encoders.h" | ||||
| #include "brother.h" | ||||
| #include "arch/brother/brother.h" | ||||
| #include "lib/data/sector.h" | ||||
| #include "lib/core/bytes.h" | ||||
| #include "lib/core/crc.h" | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| #include "lib/core/utils.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "lib/encoders/encoders.h" | ||||
| #include "brother.h" | ||||
| #include "arch/brother/brother.h" | ||||
| #include "lib/core/crc.h" | ||||
| #include "lib/data/image.h" | ||||
| #include "arch/brother/brother.pb.h" | ||||
|   | ||||
							
								
								
									
										133
									
								
								arch/build.py
									
									
									
									
									
								
							
							
						
						
									
										133
									
								
								arch/build.py
									
									
									
									
									
								
							| @@ -1,102 +1,61 @@ | ||||
| from build.c import cxxlibrary | ||||
| from build.protobuf import proto, protocc | ||||
| from build.protobuf import proto, protocc, protolib | ||||
| from os.path import * | ||||
| from glob import glob | ||||
| import sys | ||||
|  | ||||
| proto( | ||||
| archs = [f for f in glob("*", root_dir="arch") if isfile(f"arch/{f}/{f}.proto")] | ||||
|  | ||||
| ps = [] | ||||
| pls = [] | ||||
| cls = [] | ||||
| for a in archs: | ||||
|     ps += [ | ||||
|         proto( | ||||
|             name=f"proto_{a}", | ||||
|             srcs=[f"arch/{a}/{a}.proto"], | ||||
|             deps=["lib/config+common_proto"], | ||||
|         ) | ||||
|     ] | ||||
|  | ||||
|     pls += [ | ||||
|         protocc( | ||||
|             name=f"proto_lib_{a}", | ||||
|             srcs=[f".+proto_{a}"], | ||||
|             deps=["lib/config+common_proto_lib"], | ||||
|         ) | ||||
|     ] | ||||
|  | ||||
|     cls += [ | ||||
|         cxxlibrary( | ||||
|             name=f"arch_{a}", | ||||
|             srcs=glob(f"arch/{a}/*.cc") + glob(f"arch/{a}/*.h"), | ||||
|             hdrs={f"arch/{a}/{a}.h": f"arch/{a}/{a}.h"}, | ||||
|             deps=[ | ||||
|                 "lib/core", | ||||
|                 "lib/data", | ||||
|                 "lib/config", | ||||
|                 "lib/encoders", | ||||
|                 "lib/decoders", | ||||
|             ], | ||||
|         ) | ||||
|     ] | ||||
|  | ||||
| protolib( | ||||
|     name="proto", | ||||
|     srcs=[ | ||||
|         "./aeslanier/aeslanier.proto", | ||||
|         "./agat/agat.proto", | ||||
|         "./amiga/amiga.proto", | ||||
|         "./apple2/apple2.proto", | ||||
|         "./brother/brother.proto", | ||||
|         "./c64/c64.proto", | ||||
|         "./f85/f85.proto", | ||||
|         "./fb100/fb100.proto", | ||||
|         "./ibm/ibm.proto", | ||||
|         "./macintosh/macintosh.proto", | ||||
|         "./micropolis/micropolis.proto", | ||||
|         "./mx/mx.proto", | ||||
|         "./northstar/northstar.proto", | ||||
|         "./rolandd20/rolandd20.proto", | ||||
|         "./smaky6/smaky6.proto", | ||||
|         "./tartu/tartu.proto", | ||||
|         "./tids990/tids990.proto", | ||||
|         "./victor9k/victor9k.proto", | ||||
|         "./zilogmcz/zilogmcz.proto", | ||||
|     ], | ||||
|     deps=["lib/config+common_proto"], | ||||
|     srcs=ps + ["lib/config+common_proto"], | ||||
| ) | ||||
|  | ||||
| protocc(name="proto_lib", srcs=[".+proto"], deps=["lib/config+common_proto_lib"]) | ||||
| cxxlibrary(name="proto_lib", deps=pls) | ||||
|  | ||||
| cxxlibrary( | ||||
|     name="arch", | ||||
|     srcs=[ | ||||
|         "./arch.cc", | ||||
|         "./aeslanier/decoder.cc", | ||||
|         "./agat/agat.cc", | ||||
|         "./agat/decoder.cc", | ||||
|         "./agat/encoder.cc", | ||||
|         "./amiga/amiga.cc", | ||||
|         "./amiga/decoder.cc", | ||||
|         "./amiga/encoder.cc", | ||||
|         "./apple2/decoder.cc", | ||||
|         "./apple2/encoder.cc", | ||||
|         "./brother/decoder.cc", | ||||
|         "./brother/encoder.cc", | ||||
|         "./c64/c64.cc", | ||||
|         "./c64/decoder.cc", | ||||
|         "./c64/encoder.cc", | ||||
|         "./f85/decoder.cc", | ||||
|         "./fb100/decoder.cc", | ||||
|         "./ibm/decoder.cc", | ||||
|         "./ibm/encoder.cc", | ||||
|         "./macintosh/decoder.cc", | ||||
|         "./macintosh/encoder.cc", | ||||
|         "./micropolis/decoder.cc", | ||||
|         "./micropolis/encoder.cc", | ||||
|         "./mx/decoder.cc", | ||||
|         "./northstar/decoder.cc", | ||||
|         "./northstar/encoder.cc", | ||||
|         "./rolandd20/decoder.cc", | ||||
|         "./smaky6/decoder.cc", | ||||
|         "./tartu/decoder.cc", | ||||
|         "./tartu/encoder.cc", | ||||
|         "./tids990/decoder.cc", | ||||
|         "./tids990/encoder.cc", | ||||
|         "./victor9k/decoder.cc", | ||||
|         "./victor9k/encoder.cc", | ||||
|         "./zilogmcz/decoder.cc", | ||||
|     ], | ||||
|     hdrs={ | ||||
|         "arch/ibm/ibm.h": "./ibm/ibm.h", | ||||
|         "arch/apple2/data_gcr.h": "./apple2/data_gcr.h", | ||||
|         "arch/apple2/apple2.h": "./apple2/apple2.h", | ||||
|         "arch/amiga/amiga.h": "./amiga/amiga.h", | ||||
|         "arch/smaky6/smaky6.h": "./smaky6/smaky6.h", | ||||
|         "arch/tids990/tids990.h": "./tids990/tids990.h", | ||||
|         "arch/zilogmcz/zilogmcz.h": "./zilogmcz/zilogmcz.h", | ||||
|         "arch/amiga/amiga.h": "./amiga/amiga.h", | ||||
|         "arch/f85/data_gcr.h": "./f85/data_gcr.h", | ||||
|         "arch/f85/f85.h": "./f85/f85.h", | ||||
|         "arch/mx/mx.h": "./mx/mx.h", | ||||
|         "arch/aeslanier/aeslanier.h": "./aeslanier/aeslanier.h", | ||||
|         "arch/northstar/northstar.h": "./northstar/northstar.h", | ||||
|         "arch/brother/data_gcr.h": "./brother/data_gcr.h", | ||||
|         "arch/brother/brother.h": "./brother/brother.h", | ||||
|         "arch/brother/header_gcr.h": "./brother/header_gcr.h", | ||||
|         "arch/macintosh/data_gcr.h": "./macintosh/data_gcr.h", | ||||
|         "arch/macintosh/macintosh.h": "./macintosh/macintosh.h", | ||||
|         "arch/agat/agat.h": "./agat/agat.h", | ||||
|         "arch/fb100/fb100.h": "./fb100/fb100.h", | ||||
|         "arch/victor9k/data_gcr.h": "./victor9k/data_gcr.h", | ||||
|         "arch/victor9k/victor9k.h": "./victor9k/victor9k.h", | ||||
|         "arch/rolandd20/rolandd20.h": "./rolandd20/rolandd20.h", | ||||
|         "arch/micropolis/micropolis.h": "./micropolis/micropolis.h", | ||||
|         "arch/c64/data_gcr.h": "./c64/data_gcr.h", | ||||
|         "arch/c64/c64.h": "./c64/c64.h", | ||||
|         "arch/tartu/tartu.h": "./tartu/tartu.h", | ||||
|         "arch/arch.h": "./arch.h", | ||||
|     }, | ||||
|     deps=["lib/core", "lib/data", "lib/config", "lib/encoders", "lib/decoders"], | ||||
|     deps=cls | ||||
|     + ["lib/core", "lib/data", "lib/config", "lib/encoders", "lib/decoders"], | ||||
| ) | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| #include "lib/core/globals.h" | ||||
| #include "c64.h" | ||||
| #include "arch/c64/c64.h" | ||||
|  | ||||
| /* | ||||
|  *   Track   Sectors/track   # Sectors   Storage in Bytes   Clock rate | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
| #include "protocol.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "lib/data/sector.h" | ||||
| #include "c64.h" | ||||
| #include "arch/c64/c64.h" | ||||
| #include "lib/core/crc.h" | ||||
| #include "lib/core/bytes.h" | ||||
| #include "fmt/format.h" | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| #include "lib/core/utils.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "lib/encoders/encoders.h" | ||||
| #include "c64.h" | ||||
| #include "arch/c64/c64.h" | ||||
| #include "lib/core/crc.h" | ||||
| #include "lib/data/sector.h" | ||||
| #include "lib/data/image.h" | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
| #include "protocol.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "lib/data/sector.h" | ||||
| #include "f85.h" | ||||
| #include "arch/f85/f85.h" | ||||
| #include "lib/core/crc.h" | ||||
| #include "lib/core/bytes.h" | ||||
| #include "fmt/format.h" | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
| #include "protocol.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "lib/data/sector.h" | ||||
| #include "fb100.h" | ||||
| #include "arch/fb100/fb100.h" | ||||
| #include "lib/core/crc.h" | ||||
| #include "lib/core/bytes.h" | ||||
| #include "lib/decoders/rawbits.h" | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| #include "lib/core/globals.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "ibm.h" | ||||
| #include "arch/ibm/ibm.h" | ||||
| #include "lib/core/crc.h" | ||||
| #include "lib/data/fluxmap.h" | ||||
| #include "lib/data/fluxmapreader.h" | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| #include "lib/config/config.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "lib/encoders/encoders.h" | ||||
| #include "ibm.h" | ||||
| #include "arch/ibm/ibm.h" | ||||
| #include "lib/core/crc.h" | ||||
| #include "lib/data/image.h" | ||||
| #include "arch/ibm/ibm.pb.h" | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
| #include "protocol.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "lib/data/sector.h" | ||||
| #include "macintosh.h" | ||||
| #include "arch/macintosh/macintosh.h" | ||||
| #include "lib/core/bytes.h" | ||||
| #include "fmt/format.h" | ||||
| #include <string.h> | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| #include "lib/core/utils.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "lib/encoders/encoders.h" | ||||
| #include "macintosh.h" | ||||
| #include "arch/macintosh/macintosh.h" | ||||
| #include "lib/core/crc.h" | ||||
| #include "lib/data/image.h" | ||||
| #include "fmt/format.h" | ||||
|   | ||||
| @@ -4,7 +4,7 @@ | ||||
| #include "lib/data/fluxpattern.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "lib/data/sector.h" | ||||
| #include "micropolis.h" | ||||
| #include "arch/micropolis/micropolis.h" | ||||
| #include "lib/core/bytes.h" | ||||
| #include "fmt/format.h" | ||||
| #include "lib/decoders/decoders.pb.h" | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| #include "lib/core/globals.h" | ||||
| #include "micropolis.h" | ||||
| #include "arch/micropolis/micropolis.h" | ||||
| #include "lib/data/sector.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "lib/encoders/encoders.h" | ||||
|   | ||||
| @@ -17,7 +17,7 @@ | ||||
| #include "lib/data/fluxpattern.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "lib/data/sector.h" | ||||
| #include "northstar.h" | ||||
| #include "arch/northstar/northstar.h" | ||||
| #include "lib/core/bytes.h" | ||||
| #include "lib/decoders/decoders.pb.h" | ||||
| #include "fmt/format.h" | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| #include "lib/core/globals.h" | ||||
| #include "northstar.h" | ||||
| #include "arch/northstar/northstar.h" | ||||
| #include "lib/data/sector.h" | ||||
| #include "lib/core/bytes.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
| #include "lib/data/fluxpattern.h" | ||||
| #include "lib/data/sector.h" | ||||
| #include "lib/core/bytes.h" | ||||
| #include "rolandd20.h" | ||||
| #include "arch/rolandd20/rolandd20.h" | ||||
| #include <string.h> | ||||
|  | ||||
| /* Sector header record: | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
| #include "protocol.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "lib/data/sector.h" | ||||
| #include "smaky6.h" | ||||
| #include "arch/smaky6/smaky6.h" | ||||
| #include "lib/core/bytes.h" | ||||
| #include "lib/core/crc.h" | ||||
| #include "fmt/format.h" | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| #include "lib/core/globals.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "lib/encoders/encoders.h" | ||||
| #include "tids990.h" | ||||
| #include "arch/tids990/tids990.h" | ||||
| #include "lib/core/crc.h" | ||||
| #include "lib/data/image.h" | ||||
| #include "arch/tids990/tids990.pb.h" | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
| #include "protocol.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "lib/data/sector.h" | ||||
| #include "victor9k.h" | ||||
| #include "arch/victor9k/victor9k.h" | ||||
| #include "lib/core/crc.h" | ||||
| #include "lib/core/bytes.h" | ||||
| #include "fmt/format.h" | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| #include "lib/core/utils.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "lib/encoders/encoders.h" | ||||
| #include "victor9k.h" | ||||
| #include "arch/victor9k/victor9k.h" | ||||
| #include "lib/core/crc.h" | ||||
| #include "lib/data/sector.h" | ||||
| #include "lib/data/image.h" | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
| #include "protocol.h" | ||||
| #include "lib/decoders/decoders.h" | ||||
| #include "lib/data/sector.h" | ||||
| #include "zilogmcz.h" | ||||
| #include "arch/zilogmcz/zilogmcz.h" | ||||
| #include "lib/core/bytes.h" | ||||
| #include "lib/core/crc.h" | ||||
| #include "fmt/format.h" | ||||
|   | ||||
							
								
								
									
										18
									
								
								build.py
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								build.py
									
									
									
									
									
								
							| @@ -1,22 +1,22 @@ | ||||
| from build.ab import export | ||||
| from build.c import clibrary, cxxlibrary | ||||
| from build.protobuf import proto, protocc | ||||
| from build.pkg import package, hostpackage | ||||
| from build.pkg import package | ||||
| from build.utils import test | ||||
| from glob import glob | ||||
| import config | ||||
| import re | ||||
|  | ||||
| # Hack for building on Fedora/WSL; executables get the .exe extension, | ||||
| # build the build system detects it as Linux. | ||||
| import build.toolchain | ||||
| toolchain.Toolchain.EXE = "$(EXT)" | ||||
|  | ||||
| package(name="protobuf_lib", package="protobuf") | ||||
| package(name="z_lib", package="zlib") | ||||
| package(name="fmt_lib", package="fmt", fallback="dep/fmt") | ||||
| package(name="sqlite3_lib", package="sqlite3") | ||||
|  | ||||
| hostpackage(name="protobuf_host_lib", package="protobuf") | ||||
| hostpackage(name="z_host_lib", package="zlib") | ||||
| hostpackage(name="fmt_host_lib", package="fmt", fallback="dep/fmt") | ||||
| hostpackage(name="sqlite3_host_lib", package="sqlite3") | ||||
|  | ||||
| clibrary(name="protocol", hdrs={"protocol.h": "./protocol.h"}) | ||||
|  | ||||
| corpustests = [] | ||||
| @@ -84,15 +84,15 @@ else: | ||||
|                 ins=["src+fluxengine"], | ||||
|                 deps=["scripts/encodedecodetest.sh"], | ||||
|                 commands=[ | ||||
|                     "{deps[0]} " | ||||
|                     "$[deps[0]] " | ||||
|                     + c[0] | ||||
|                     + " " | ||||
|                     + format | ||||
|                     + " {ins[0]} '" | ||||
|                     + " $[ins[0]] '" | ||||
|                     + c[1] | ||||
|                     + "' '" | ||||
|                     + c[2] | ||||
|                     + "' $(dir {outs[0]}) > /dev/null" | ||||
|                     + "' $(dir $[outs[0]]) > /dev/null" | ||||
|                 ], | ||||
|                 label="CORPUSTEST", | ||||
|             ) | ||||
|   | ||||
							
								
								
									
										49
									
								
								build/_sandbox.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								build/_sandbox.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| #!/usr/bin/python3 | ||||
|  | ||||
| from os.path import * | ||||
| import argparse | ||||
| import os | ||||
| import shutil | ||||
|  | ||||
|  | ||||
| def main(): | ||||
|     parser = argparse.ArgumentParser() | ||||
|     parser.add_argument("-s", "--sandbox") | ||||
|     parser.add_argument("-v", "--verbose", action="store_true") | ||||
|     parser.add_argument("-l", "--link", action="store_true") | ||||
|     parser.add_argument("-e", "--export", action="store_true") | ||||
|     parser.add_argument("files", nargs="*") | ||||
|     args = parser.parse_args() | ||||
|  | ||||
|     assert args.sandbox, "You must specify a sandbox directory" | ||||
|     assert args.link ^ args.export, "You can't link and export at the same time" | ||||
|  | ||||
|     if args.link: | ||||
|         os.makedirs(args.sandbox, exist_ok=True) | ||||
|         for f in args.files: | ||||
|             sf = join(args.sandbox, f) | ||||
|             if args.verbose: | ||||
|                 print("link", sf) | ||||
|             os.makedirs(dirname(sf), exist_ok=True) | ||||
|             try: | ||||
|                 os.symlink(abspath(f), sf) | ||||
|             except PermissionError: | ||||
|                 shutil.copy(f, sf) | ||||
|  | ||||
|     if args.export: | ||||
|         for f in args.files: | ||||
|             sf = join(args.sandbox, f) | ||||
|             if args.verbose: | ||||
|                 print("export", sf) | ||||
|             df = dirname(f) | ||||
|             if df: | ||||
|                 os.makedirs(df, exist_ok=True) | ||||
|  | ||||
|             try: | ||||
|                 os.remove(f) | ||||
|             except FileNotFoundError: | ||||
|                 pass | ||||
|             os.rename(sf, f) | ||||
|  | ||||
|  | ||||
| main() | ||||
							
								
								
									
										25
									
								
								build/_zip.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										25
									
								
								build/_zip.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| #!/usr/bin/python3 | ||||
|  | ||||
| from os.path import * | ||||
| import argparse | ||||
| import os | ||||
| from zipfile import ZipFile | ||||
|  | ||||
|  | ||||
| def main(): | ||||
|     parser = argparse.ArgumentParser() | ||||
|     parser.add_argument("-z", "--zipfile") | ||||
|     parser.add_argument("-v", "--verbose", action="store_true") | ||||
|     parser.add_argument("-f", "--file", nargs=2, action="append") | ||||
|     args = parser.parse_args() | ||||
|  | ||||
|     assert args.zipfile, "You must specify a zipfile to create" | ||||
|  | ||||
|     with ZipFile(args.zipfile, mode="w") as zf: | ||||
|         for zipname, filename in args.file: | ||||
|             if args.verbose: | ||||
|                 print(filename, "->", zipname) | ||||
|             zf.write(filename, arcname=zipname) | ||||
|  | ||||
|  | ||||
| main() | ||||
							
								
								
									
										53
									
								
								build/ab.mk
									
									
									
									
									
								
							
							
						
						
									
										53
									
								
								build/ab.mk
									
									
									
									
									
								
							| @@ -1,19 +1,28 @@ | ||||
| ifeq ($(findstring 4.,$(MAKE_VERSION)),) | ||||
| MAKENOT4 := $(if $(findstring 3.9999, $(lastword $(sort 3.9999 $(MAKE_VERSION)))),yes,no) | ||||
|  | ||||
| ifeq ($(MAKENOT4),yes) | ||||
| $(error You need GNU Make 4.x for this (if you're on OSX, use gmake).) | ||||
| endif | ||||
|  | ||||
| OBJ ?= .obj | ||||
| PYTHON ?= python3 | ||||
| CC ?= gcc | ||||
| CXX ?= g++ | ||||
| AR ?= ar | ||||
| CFLAGS ?= -g -Og | ||||
| LDFLAGS ?= -g | ||||
| PKG_CONFIG ?= pkg-config | ||||
| HOST_PKG_CONFIG ?= $(PKG_CONFIG) | ||||
| ECHO ?= echo | ||||
| CP ?= cp | ||||
|  | ||||
| HOSTCC ?= gcc | ||||
| HOSTCXX ?= g++ | ||||
| HOSTAR ?= ar | ||||
| HOSTCFLAGS ?= -g -Og | ||||
| HOSTLDFLAGS ?= -g | ||||
|  | ||||
| CC ?= $(HOSTCC) | ||||
| CXX ?= $(HOSTCXX) | ||||
| AR ?= $(HOSTAR) | ||||
| CFLAGS ?= $(HOSTCFLAGS) | ||||
| LDFLAGS ?= $(HOSTLDFLAGS) | ||||
|  | ||||
| export PKG_CONFIG | ||||
| export HOST_PKG_CONFIG | ||||
|  | ||||
| @@ -27,6 +36,11 @@ else | ||||
| 	endif | ||||
| endif | ||||
|  | ||||
| # If enabled, shows a nice display of how far through the build you are. This | ||||
| # doubles Make startup time. Also, on Make 4.3 and above, rebuilds don't show | ||||
| # correct progress information. | ||||
| AB_ENABLE_PROGRESS_INFO ?= true | ||||
|  | ||||
| WINDOWS := no | ||||
| OSX := no | ||||
| LINUX := no | ||||
| @@ -47,13 +61,19 @@ ifeq ($(OS), Windows_NT) | ||||
| endif | ||||
| EXT ?= | ||||
|  | ||||
| ifeq ($(PROGRESSINFO),) | ||||
| # The first make invocation here has to have its output discarded or else it | ||||
| # produces spurious 'Leaving directory' messages... don't know why. | ||||
| rulecount := $(strip $(shell $(MAKE) --no-print-directory -q $(OBJ)/build.mk PROGRESSINFO=1 > /dev/null \ | ||||
| 	&& $(MAKE) --no-print-directory -n $(MAKECMDGOALS) PROGRESSINFO=XXXPROGRESSINFOXXX | grep XXXPROGRESSINFOXXX | wc -l)) | ||||
| ruleindex := 1 | ||||
| PROGRESSINFO = "[$(ruleindex)/$(rulecount)]$(eval ruleindex := $(shell expr $(ruleindex) + 1))" | ||||
| CWD=$(shell pwd) | ||||
|  | ||||
| ifeq ($(AB_ENABLE_PROGRESS_INFO),true) | ||||
| 	ifeq ($(PROGRESSINFO),) | ||||
| 	# The first make invocation here has to have its output discarded or else it | ||||
| 	# produces spurious 'Leaving directory' messages... don't know why. | ||||
| 	rulecount := $(strip $(shell $(MAKE) --no-print-directory -q $(OBJ)/build.mk PROGRESSINFO=1 > /dev/null \ | ||||
| 		&& $(MAKE) --no-print-directory -n $(MAKECMDGOALS) PROGRESSINFO=XXXPROGRESSINFOXXX | grep XXXPROGRESSINFOXXX | wc -l)) | ||||
| 	ruleindex := 1 | ||||
| 	PROGRESSINFO = "[$(ruleindex)/$(rulecount)]$(eval ruleindex := $(shell expr $(ruleindex) + 1)) " | ||||
| 	endif | ||||
| else | ||||
| 	PROGRESSINFO = "" | ||||
| endif | ||||
|  | ||||
| PKG_CONFIG_HASHES = $(OBJ)/.pkg-config-hashes/target-$(word 1, $(shell $(PKG_CONFIG) --list-all | md5sum)) | ||||
| @@ -67,7 +87,12 @@ $(PKG_CONFIG_HASHES) $(HOST_PKG_CONFIG_HASHES) &: | ||||
|  | ||||
| include $(OBJ)/build.mk | ||||
|  | ||||
| MAKEFLAGS += -r -j$(shell nproc) | ||||
| ifeq ($(OSX),yes) | ||||
| 	MAKEFLAGS += -r -j$(shell sysctl -n hw.logicalcpu) | ||||
| else | ||||
| 	MAKEFLAGS += -r -j$(shell nproc) | ||||
| endif | ||||
|  | ||||
| .DELETE_ON_ERROR: | ||||
|  | ||||
| .PHONY: update-ab | ||||
|   | ||||
							
								
								
									
										162
									
								
								build/ab.py
									
									
									
									
									
								
							
							
						
						
									
										162
									
								
								build/ab.py
									
									
									
									
									
								
							| @@ -6,7 +6,6 @@ import builtins | ||||
| from copy import copy | ||||
| import functools | ||||
| import importlib | ||||
| import importlib.abc | ||||
| import importlib.util | ||||
| from importlib.machinery import ( | ||||
|     SourceFileLoader, | ||||
| @@ -17,6 +16,11 @@ import inspect | ||||
| import string | ||||
| import sys | ||||
| import hashlib | ||||
| import re | ||||
| import ast | ||||
| from collections import namedtuple | ||||
|  | ||||
| VERBOSE_MK_FILE = False | ||||
|  | ||||
| verbose = False | ||||
| quiet = False | ||||
| @@ -25,6 +29,24 @@ targets = {} | ||||
| unmaterialisedTargets = {}  # dict, not set, to get consistent ordering | ||||
| materialisingStack = [] | ||||
| defaultGlobals = {} | ||||
| globalId = 1 | ||||
| wordCache = {} | ||||
|  | ||||
| RE_FORMAT_SPEC = re.compile( | ||||
|     r"(?:(?P<fill>[\s\S])?(?P<align>[<>=^]))?" | ||||
|     r"(?P<sign>[- +])?" | ||||
|     r"(?P<pos_zero>z)?" | ||||
|     r"(?P<alt>#)?" | ||||
|     r"(?P<zero_padding>0)?" | ||||
|     r"(?P<width_str>\d+)?" | ||||
|     r"(?P<grouping>[_,])?" | ||||
|     r"(?:(?P<decimal>\.)(?P<precision_str>\d+))?" | ||||
|     r"(?P<type>[bcdeEfFgGnosxX%])?" | ||||
| ) | ||||
|  | ||||
| CommandFormatSpec = namedtuple( | ||||
|     "CommandFormatSpec", RE_FORMAT_SPEC.groupindex.keys() | ||||
| ) | ||||
|  | ||||
| sys.path += ["."] | ||||
| old_import = builtins.__import__ | ||||
| @@ -32,7 +54,8 @@ old_import = builtins.__import__ | ||||
|  | ||||
| class PathFinderImpl(PathFinder): | ||||
|     def find_spec(self, fullname, path, target=None): | ||||
|         if not path: | ||||
|         # The second test here is needed for Python 3.9. | ||||
|         if not path or not path[0]: | ||||
|             path = ["."] | ||||
|         if len(path) != 1: | ||||
|             return None | ||||
| @@ -79,6 +102,29 @@ def error(message): | ||||
|     raise ABException(message) | ||||
|  | ||||
|  | ||||
| class BracketedFormatter(string.Formatter): | ||||
|     def parse(self, format_string): | ||||
|         while format_string: | ||||
|             left, *right = format_string.split("$[", 1) | ||||
|             if not right: | ||||
|                 yield (left, None, None, None) | ||||
|                 break | ||||
|             right = right[0] | ||||
|  | ||||
|             offset = len(right) + 1 | ||||
|             try: | ||||
|                 ast.parse(right) | ||||
|             except SyntaxError as e: | ||||
|                 if not str(e).startswith("unmatched ']'"): | ||||
|                     raise e | ||||
|                 offset = e.offset | ||||
|  | ||||
|             expr = right[0 : offset - 1] | ||||
|             format_string = right[offset:] | ||||
|  | ||||
|             yield (left if left else None, expr, None, None) | ||||
|  | ||||
|  | ||||
| def Rule(func): | ||||
|     sig = inspect.signature(func) | ||||
|  | ||||
| @@ -114,7 +160,8 @@ def Rule(func): | ||||
|         t.callback = func | ||||
|         t.traits.add(func.__name__) | ||||
|         if "args" in kwargs: | ||||
|             t.args |= kwargs["args"] | ||||
|             t.explicit_args = kwargs["args"] | ||||
|             t.args.update(t.explicit_args) | ||||
|             del kwargs["args"] | ||||
|         if "traits" in kwargs: | ||||
|             t.traits |= kwargs["traits"] | ||||
| @@ -165,7 +212,7 @@ class Target: | ||||
|         return f"Target('{self.name}')" | ||||
|  | ||||
|     def templateexpand(selfi, s): | ||||
|         class Formatter(string.Formatter): | ||||
|         class Formatter(BracketedFormatter): | ||||
|             def get_field(self, name, a1, a2): | ||||
|                 return ( | ||||
|                     eval(name, selfi.callback.__globals__, selfi.args), | ||||
| @@ -354,9 +401,26 @@ class TargetsMap: | ||||
|         return output | ||||
|  | ||||
|  | ||||
| def _removesuffix(self, suffix): | ||||
|     # suffix='' should not call self[:-0]. | ||||
|     if suffix and self.endswith(suffix): | ||||
|         return self[: -len(suffix)] | ||||
|     else: | ||||
|         return self[:] | ||||
|  | ||||
|  | ||||
| def loadbuildfile(filename): | ||||
|     filename = filename.replace("/", ".").removesuffix(".py") | ||||
|     builtins.__import__(filename) | ||||
|     modulename = _removesuffix(filename.replace("/", "."), ".py") | ||||
|     if modulename not in sys.modules: | ||||
|         spec = importlib.util.spec_from_file_location( | ||||
|             name=modulename, | ||||
|             location=filename, | ||||
|             loader=BuildFileLoaderImpl(fullname=modulename, path=filename), | ||||
|             submodule_search_locations=[], | ||||
|         ) | ||||
|         module = importlib.util.module_from_spec(spec) | ||||
|         sys.modules[modulename] = module | ||||
|         spec.loader.exec_module(module) | ||||
|  | ||||
|  | ||||
| def flatten(items): | ||||
| @@ -382,6 +446,7 @@ def filenamesof(items): | ||||
|     def generate(xs): | ||||
|         for x in xs: | ||||
|             if isinstance(x, Target): | ||||
|                 x.materialise() | ||||
|                 yield from generate(x.outs) | ||||
|             else: | ||||
|                 yield x | ||||
| @@ -405,41 +470,75 @@ def emit(*args, into=None): | ||||
|         outputFp.write(s) | ||||
|  | ||||
|  | ||||
| def emit_rule(name, ins, outs, cmds=[], label=None): | ||||
|     fins = filenamesof(ins) | ||||
| def emit_rule(self, ins, outs, cmds=[], label=None): | ||||
|     name = self.name | ||||
|     fins_list = filenamesof(ins) | ||||
|     fins = set(fins_list) | ||||
|     fouts = filenamesof(outs) | ||||
|     nonobjs = [f for f in fouts if not f.startswith("$(OBJ)")] | ||||
|  | ||||
|     emit("") | ||||
|     if VERBOSE_MK_FILE: | ||||
|         for k, v in self.args.items(): | ||||
|             emit(f"# {k} = {v}") | ||||
|  | ||||
|     lines = [] | ||||
|     if nonobjs: | ||||
|         emit("clean::", into=lines) | ||||
|         emit("\t$(hide) rm -f", *nonobjs, into=lines) | ||||
|  | ||||
|     hashable = cmds + fins_list + fouts | ||||
|     hash = hashlib.sha1(bytes("\n".join(hashable), "utf-8")).hexdigest() | ||||
|     hashfile = join(self.dir, f"hash_{hash}") | ||||
|  | ||||
|     global globalId | ||||
|     emit(".PHONY:", name, into=lines) | ||||
|     if outs: | ||||
|         emit(name, ":", *fouts, into=lines) | ||||
|         emit(*fouts, "&:" if len(fouts) > 1 else ":", *fins, "\x01", into=lines) | ||||
|         outsn = globalId | ||||
|         globalId = globalId + 1 | ||||
|         insn = globalId | ||||
|         globalId = globalId + 1 | ||||
|  | ||||
|         emit(f"OUTS_{outsn}", "=", *fouts, into=lines) | ||||
|         emit(f"INS_{insn}", "=", *fins, into=lines) | ||||
|         emit(name, ":", f"$(OUTS_{outsn})", into=lines) | ||||
|         emit(hashfile, ":", into=lines) | ||||
|         emit(f"\t@mkdir -p {self.dir}", into=lines) | ||||
|         emit(f"\t@touch {hashfile}", into=lines) | ||||
|         emit( | ||||
|             f"$(OUTS_{outsn})", | ||||
|             "&:" if len(fouts) > 1 else ":", | ||||
|             f"$(INS_{insn})", | ||||
|             hashfile, | ||||
|             into=lines, | ||||
|         ) | ||||
|  | ||||
|         if label: | ||||
|             emit("\t$(hide)", "$(ECHO) $(PROGRESSINFO)", label, into=lines) | ||||
|             emit("\t$(hide)", "$(ECHO) $(PROGRESSINFO)" + label, into=lines) | ||||
|  | ||||
|         sandbox = join(self.dir, "sandbox") | ||||
|         emit("\t$(hide)", f"rm -rf {sandbox}", into=lines) | ||||
|         emit( | ||||
|             "\t$(hide)", | ||||
|             "$(PYTHON) build/_sandbox.py --link -s", | ||||
|             sandbox, | ||||
|             f"$(INS_{insn})", | ||||
|             into=lines, | ||||
|         ) | ||||
|         for c in cmds: | ||||
|             emit("\t$(hide)", c, into=lines) | ||||
|             emit(f"\t$(hide) cd {sandbox} && (", c, ")", into=lines) | ||||
|         emit( | ||||
|             "\t$(hide)", | ||||
|             "$(PYTHON) build/_sandbox.py --export -s", | ||||
|             sandbox, | ||||
|             f"$(OUTS_{outsn})", | ||||
|             into=lines, | ||||
|         ) | ||||
|     else: | ||||
|         assert len(cmds) == 0, "rules with no outputs cannot have commands" | ||||
|         emit(name, ":", *fins, into=lines) | ||||
|  | ||||
|     cmd = "".join(lines) | ||||
|     hash = hashlib.sha1(bytes(cmd, "utf-8")).hexdigest() | ||||
|  | ||||
|     outputFp.write(cmd.replace("\x01", f"$(OBJ)/.hashes/{hash}")) | ||||
|  | ||||
|     if outs: | ||||
|         emit(f"$(OBJ)/.hashes/{hash}:") | ||||
|         emit( | ||||
|             f"\t$(hide) mkdir -p $(OBJ)/.hashes && touch $(OBJ)/.hashes/{hash}" | ||||
|         ) | ||||
|     outputFp.write("".join(lines)) | ||||
|     emit("") | ||||
|  | ||||
|  | ||||
| @@ -470,10 +569,10 @@ def simplerule( | ||||
|         cs += [self.templateexpand(c)] | ||||
|  | ||||
|     emit_rule( | ||||
|         name=self.name, | ||||
|         self=self, | ||||
|         ins=ins + deps, | ||||
|         outs=outs, | ||||
|         label=self.templateexpand("{label} {name}") if label else None, | ||||
|         label=self.templateexpand("$[label] $[name]") if label else None, | ||||
|         cmds=cs, | ||||
|     ) | ||||
|  | ||||
| @@ -498,18 +597,17 @@ def export(self, name=None, items: TargetsMap = {}, deps: Targets = []): | ||||
|             cwd=self.cwd, | ||||
|             ins=[srcs[0]], | ||||
|             outs=[destf], | ||||
|             commands=["$(CP) %s %s" % (srcs[0], destf)], | ||||
|             commands=["$(CP) -H %s %s" % (srcs[0], destf)], | ||||
|             label="", | ||||
|         ) | ||||
|         subrule.materialise() | ||||
|  | ||||
|     simplerule( | ||||
|         replaces=self, | ||||
|         ins=outs + deps, | ||||
|         outs=["=sentinel"], | ||||
|         commands=["touch {outs[0]}"], | ||||
|         label="EXPORT", | ||||
|     ) | ||||
|     self.ins = [] | ||||
|     self.outs = deps + outs | ||||
|  | ||||
|     emit("") | ||||
|     emit(".PHONY:", name) | ||||
|     emit(name, ":", *filenamesof(outs + deps)) | ||||
|  | ||||
|  | ||||
| def main(): | ||||
|   | ||||
							
								
								
									
										423
									
								
								build/c.py
									
									
									
									
									
								
							
							
						
						
									
										423
									
								
								build/c.py
									
									
									
									
									
								
							| @@ -9,59 +9,111 @@ from build.ab import ( | ||||
|     emit, | ||||
| ) | ||||
| from build.utils import filenamesmatchingof, stripext, collectattrs | ||||
| from build.toolchain import Toolchain, HostToolchain | ||||
| from os.path import * | ||||
|  | ||||
| emit( | ||||
|     """ | ||||
| ifeq ($(OSX),no) | ||||
| HOSTSTARTGROUP ?= -Wl,--start-group | ||||
| HOSTENDGROUP ?= -Wl,--end-group | ||||
| STARTGROUP ?= -Wl,--start-group | ||||
| ENDGROUP ?= -Wl,--end-group | ||||
| endif | ||||
| STARTGROUP ?= $(HOSTSTARTGROUP) | ||||
| ENDGROUP ?= $(HOSTENDGROUP) | ||||
| """ | ||||
| ) | ||||
|  | ||||
| Toolchain.CC = ["$(CC) -c -o $[outs[0]] $[ins[0]] $(CFLAGS) $[cflags]"] | ||||
| Toolchain.CPP = ["$(CC) -E -P -o $[outs] $[cflags] -x c $[ins]"] | ||||
| Toolchain.CXX = ["$(CXX) -c -o $[outs[0]] $[ins[0]] $(CFLAGS) $[cflags]"] | ||||
| Toolchain.AR = ["$(AR) cqs $[outs[0]] $[ins]"] | ||||
| Toolchain.ARXX = ["$(AR) cqs $[outs[0]] $[ins]"] | ||||
| Toolchain.CLINK = [ | ||||
|     "$(CC) -o $[outs[0]] $(STARTGROUP) $[ins] $[ldflags] $(LDFLAGS) $(ENDGROUP)" | ||||
| ] | ||||
| Toolchain.CXXLINK = [ | ||||
|     "$(CXX) -o $[outs[0]] $(STARTGROUP) $[ins] $[ldflags] $(LDFLAGS) $(ENDGROUP)" | ||||
| ] | ||||
|  | ||||
| class Toolchain: | ||||
|     label = "" | ||||
|     cfile = ["$(CC) -c -o {outs[0]} {ins[0]} $(CFLAGS) {cflags}"] | ||||
|     cxxfile = ["$(CXX) -c -o {outs[0]} {ins[0]} $(CFLAGS) {cflags}"] | ||||
|     clibrary = ["rm -f {outs[0]} && $(AR) cqs {outs[0]} {ins}"] | ||||
|     cxxlibrary = ["rm -f {outs[0]} && $(AR) cqs {outs[0]} {ins}"] | ||||
|     cprogram = [ | ||||
|         "$(CC) -o {outs[0]} $(STARTGROUP) {ins} {ldflags} $(LDFLAGS) $(ENDGROUP)" | ||||
|     ] | ||||
|     cxxprogram = [ | ||||
|         "$(CXX) -o {outs[0]} $(STARTGROUP) {ins} {ldflags} $(LDFLAGS) $(ENDGROUP)" | ||||
|     ] | ||||
| Toolchain.is_source_file = ( | ||||
|     lambda f: f.endswith(".c") | ||||
|     or f.endswith(".cc") | ||||
|     or f.endswith(".cpp") | ||||
|     or f.endswith(".S") | ||||
|     or f.endswith(".s") | ||||
|     or f.endswith(".m") | ||||
|     or f.endswith(".mm") | ||||
| ) | ||||
|  | ||||
|  | ||||
| class HostToolchain: | ||||
|     label = "HOST " | ||||
|     cfile = ["$(HOSTCC) -c -o {outs[0]} {ins[0]} $(HOSTCFLAGS) {cflags}"] | ||||
|     cxxfile = ["$(HOSTCXX) -c -o {outs[0]} {ins[0]} $(HOSTCFLAGS) {cflags}"] | ||||
|     clibrary = ["rm -f {outs[0]} && $(HOSTAR) cqs {outs[0]} {ins}"] | ||||
|     cxxlibrary = ["rm -f {outs[0]} && $(HOSTAR) cqs {outs[0]} {ins}"] | ||||
|     cprogram = [ | ||||
|         "$(HOSTCC) -o {outs[0]} $(HOSTSTARTGROUP) {ins} {ldflags} $(HOSTLDFLAGS) $(HOSTENDGROUP)" | ||||
|     ] | ||||
|     cxxprogram = [ | ||||
|         "$(HOSTCXX) -o {outs[0]} $(HOSTSTARTGROUP) {ins} {ldflags} $(HOSTLDFLAGS) $(HOSTENDGROUP)" | ||||
|     ] | ||||
|  | ||||
|  | ||||
| def _indirect(deps, name): | ||||
|     r = set() | ||||
| # Given a set of dependencies, finds the set of relevant library targets (i.e. | ||||
| # contributes *.a files) for compiling C programs.  The actual list of libraries | ||||
| # is in dep.clibrary_files. | ||||
| def _toolchain_find_library_targets(deps): | ||||
|     lib_deps = [] | ||||
|     for d in deps: | ||||
|         r.update(d.args.get(name, {d})) | ||||
|         lib_deps = _combine(lib_deps, d.args.get("clibrary_deps", [])) | ||||
|     return lib_deps | ||||
|  | ||||
|  | ||||
| Toolchain.find_c_library_targets = _toolchain_find_library_targets | ||||
|  | ||||
|  | ||||
| # Given a set of dependencies, finds the set of relevant header targets (i.e. | ||||
| # contributes *.h files) for compiling C programs.  The actual list of libraries | ||||
| # is in dep.cheader_files. | ||||
| def _toolchain_find_header_targets(deps, initial=[]): | ||||
|     hdr_deps = initial | ||||
|     for d in deps: | ||||
|         hdr_deps = _combine(hdr_deps, d.args.get("cheader_deps", [])) | ||||
|     return hdr_deps | ||||
|  | ||||
|  | ||||
| Toolchain.find_c_header_targets = _toolchain_find_header_targets | ||||
|  | ||||
|  | ||||
| HostToolchain.CC = [ | ||||
|     "$(HOSTCC) -c -o $[outs[0]] $[ins[0]] $(HOSTCFLAGS) $[cflags]" | ||||
| ] | ||||
| HostToolchain.CPP = ["$(HOSTCC) -E -P -o $[outs] $[cflags] -x c $[ins]"] | ||||
| HostToolchain.CXX = [ | ||||
|     "$(HOSTCXX) -c -o $[outs[0]] $[ins[0]] $(HOSTCFLAGS) $[cflags]" | ||||
| ] | ||||
| HostToolchain.AR = ["$(HOSTAR) cqs $[outs[0]] $[ins]"] | ||||
| HostToolchain.ARXX = ["$(HOSTAR) cqs $[outs[0]] $[ins]"] | ||||
| HostToolchain.CLINK = [ | ||||
|     "$(HOSTCC) -o $[outs[0]] $(STARTGROUP) $[ins] $[ldflags] $(HOSTLDFLAGS) $(ENDGROUP)" | ||||
| ] | ||||
| HostToolchain.CXXLINK = [ | ||||
|     "$(HOSTCXX) -o $[outs[0]] $(STARTGROUP) $[ins] $[ldflags] $(HOSTLDFLAGS) $(ENDGROUP)" | ||||
| ] | ||||
|  | ||||
|  | ||||
| def _combine(list1, list2): | ||||
|     r = list(list1) | ||||
|     for i in list2: | ||||
|         if i not in r: | ||||
|             r.append(i) | ||||
|     return r | ||||
|  | ||||
|  | ||||
| def cfileimpl(self, name, srcs, deps, suffix, commands, label, kind, cflags): | ||||
| def _indirect(deps, name): | ||||
|     r = [] | ||||
|     for d in deps: | ||||
|         r = _combine(r, d.args.get(name, [d])) | ||||
|     return r | ||||
|  | ||||
|  | ||||
| def cfileimpl( | ||||
|     self, name, srcs, deps, suffix, commands, label, toolchain, cflags | ||||
| ): | ||||
|     outleaf = "=" + stripext(basename(filenameof(srcs[0]))) + suffix | ||||
|  | ||||
|     hdr_deps = _indirect(deps, "cheader_deps") | ||||
|     hdr_deps = toolchain.find_c_header_targets(deps) | ||||
|     other_deps = [ | ||||
|         d | ||||
|         for d in deps | ||||
|         if ("cheader_deps" not in d.args) and ("clibrary_deps" not in d.args) | ||||
|     ] | ||||
|     hdr_files = collectattrs(targets=hdr_deps, name="cheader_files") | ||||
|     cflags = collectattrs( | ||||
|         targets=hdr_deps, name="caller_cflags", initial=cflags | ||||
|     ) | ||||
| @@ -69,7 +121,7 @@ def cfileimpl(self, name, srcs, deps, suffix, commands, label, kind, cflags): | ||||
|     t = simplerule( | ||||
|         replaces=self, | ||||
|         ins=srcs, | ||||
|         deps=sorted(_indirect(hdr_deps, "cheader_files")), | ||||
|         deps=other_deps + hdr_files, | ||||
|         outs=[outleaf], | ||||
|         label=label, | ||||
|         commands=commands, | ||||
| @@ -86,14 +138,19 @@ def cfile( | ||||
|     cflags=[], | ||||
|     suffix=".o", | ||||
|     toolchain=Toolchain, | ||||
|     commands=None, | ||||
|     label=None, | ||||
|     label="CC", | ||||
| ): | ||||
|     if not label: | ||||
|         label = toolchain.label + "CC" | ||||
|     if not commands: | ||||
|         commands = toolchain.cfile | ||||
|     cfileimpl(self, name, srcs, deps, suffix, commands, label, "cfile", cflags) | ||||
|     cfileimpl( | ||||
|         self, | ||||
|         name, | ||||
|         srcs, | ||||
|         deps, | ||||
|         suffix, | ||||
|         toolchain.CC, | ||||
|         toolchain.PREFIX + label, | ||||
|         toolchain, | ||||
|         cflags, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| @@ -105,40 +162,48 @@ def cxxfile( | ||||
|     cflags=[], | ||||
|     suffix=".o", | ||||
|     toolchain=Toolchain, | ||||
|     commands=None, | ||||
|     label=None, | ||||
|     label="CXX", | ||||
| ): | ||||
|     if not label: | ||||
|         label = toolchain.label + "CXX" | ||||
|     if not commands: | ||||
|         commands = toolchain.cxxfile | ||||
|     cfileimpl( | ||||
|         self, name, srcs, deps, suffix, commands, label, "cxxfile", cflags | ||||
|         self, | ||||
|         name, | ||||
|         srcs, | ||||
|         deps, | ||||
|         suffix, | ||||
|         toolchain.CXX, | ||||
|         toolchain.PREFIX + label, | ||||
|         toolchain, | ||||
|         cflags, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def findsources(name, srcs, deps, cflags, toolchain, filerule, cwd): | ||||
| def _removeprefix(self, prefix): | ||||
|     if self.startswith(prefix): | ||||
|         return self[len(prefix) :] | ||||
|     else: | ||||
|         return self[:] | ||||
|  | ||||
|  | ||||
| def findsources(self, srcs, deps, cflags, filerule, toolchain, cwd): | ||||
|     for f in filenamesof(srcs): | ||||
|         if f.endswith(".h") or f.endswith(".hh"): | ||||
|         if not toolchain.is_source_file(f): | ||||
|             cflags = cflags + [f"-I{dirname(f)}"] | ||||
|             deps = deps + [f] | ||||
|  | ||||
|     objs = [] | ||||
|     for s in flatten(srcs): | ||||
|         objs += [ | ||||
|             filerule( | ||||
|                 name=join(name, f.removeprefix("$(OBJ)/")), | ||||
|                 name=join(self.localname, _removeprefix(f, "$(OBJ)/")), | ||||
|                 srcs=[f], | ||||
|                 deps=deps, | ||||
|                 cflags=sorted(set(cflags)), | ||||
|                 toolchain=toolchain, | ||||
|                 cwd=cwd, | ||||
|                 args=getattr(self, "explicit_args", {}), | ||||
|             ) | ||||
|             for f in filenamesof([s]) | ||||
|             if f.endswith(".c") | ||||
|             or f.endswith(".cc") | ||||
|             or f.endswith(".cpp") | ||||
|             or f.endswith(".S") | ||||
|             or f.endswith(".s") | ||||
|             if toolchain.is_source_file(f) | ||||
|         ] | ||||
|         if any(f.endswith(".o") for f in filenamesof([s])): | ||||
|             objs += [s] | ||||
| @@ -159,10 +224,10 @@ def libraryimpl( | ||||
|     toolchain, | ||||
|     commands, | ||||
|     label, | ||||
|     kind, | ||||
|     filerule, | ||||
| ): | ||||
|     hdr_deps = _indirect(deps, "cheader_deps") | {self} | ||||
|     lib_deps = _indirect(deps, "clibrary_deps") | {self} | ||||
|     hdr_deps = toolchain.find_c_header_targets(deps) + [self] | ||||
|     lib_deps = toolchain.find_c_library_targets(deps) + [self] | ||||
|  | ||||
|     hr = None | ||||
|     hf = [] | ||||
| @@ -178,7 +243,7 @@ def libraryimpl( | ||||
|                 len(s) == 1 | ||||
|             ), "the target of a header must return exactly one file" | ||||
|  | ||||
|             cs += ["$(CP) {ins[" + str(i) + "]} {outs[" + str(i) + "]}"] | ||||
|             cs += [f"$(CP) $[ins[{i}]] $[outs[{i}]]"] | ||||
|             outs += ["=" + dest] | ||||
|             i = i + 1 | ||||
|  | ||||
| @@ -187,19 +252,22 @@ def libraryimpl( | ||||
|             ins=ins, | ||||
|             outs=outs, | ||||
|             commands=cs, | ||||
|             label="CHEADERS", | ||||
|             label=toolchain.PREFIX + "CHEADERS", | ||||
|         ) | ||||
|         hr.materialise() | ||||
|         hr.args["cheader_deps"] = [hr] | ||||
|         hr.args["cheader_files"] = [hr] | ||||
|         hf = [f"-I{hr.dir}"] | ||||
|  | ||||
|     if srcs: | ||||
|         # Can't depend on the current target to get the library headers, because | ||||
|         # if we do it'll cause a dependency loop. | ||||
|         objs = findsources( | ||||
|             self.localname, | ||||
|             self, | ||||
|             srcs, | ||||
|             deps + ([hr] if hr else []), | ||||
|             cflags + hf, | ||||
|             filerule, | ||||
|             toolchain, | ||||
|             kind, | ||||
|             self.cwd, | ||||
|         ) | ||||
|  | ||||
| @@ -207,6 +275,7 @@ def libraryimpl( | ||||
|             name=f"{self.localname}_lib", | ||||
|             ins=objs, | ||||
|             outs=[f"={self.localname}.a"], | ||||
|             deps=deps, | ||||
|             label=label, | ||||
|             commands=commands, | ||||
|         ) | ||||
| @@ -234,14 +303,9 @@ def clibrary( | ||||
|     cflags=[], | ||||
|     ldflags=[], | ||||
|     toolchain=Toolchain, | ||||
|     commands=None, | ||||
|     label=None, | ||||
|     label="LIB", | ||||
|     cfilerule=cfile, | ||||
| ): | ||||
|     if not label: | ||||
|         label = toolchain.label + "LIB" | ||||
|     if not commands: | ||||
|         commands = toolchain.clibrary | ||||
|     libraryimpl( | ||||
|         self, | ||||
|         name, | ||||
| @@ -253,8 +317,40 @@ def clibrary( | ||||
|         cflags, | ||||
|         ldflags, | ||||
|         toolchain, | ||||
|         commands, | ||||
|         label, | ||||
|         toolchain.AR, | ||||
|         toolchain.PREFIX + label, | ||||
|         cfilerule, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def hostclibrary( | ||||
|     self, | ||||
|     name, | ||||
|     srcs: Targets = None, | ||||
|     deps: Targets = None, | ||||
|     hdrs: TargetsMap = None, | ||||
|     caller_cflags=[], | ||||
|     caller_ldflags=[], | ||||
|     cflags=[], | ||||
|     ldflags=[], | ||||
|     toolchain=HostToolchain, | ||||
|     label="LIB", | ||||
|     cfilerule=cfile, | ||||
| ): | ||||
|     libraryimpl( | ||||
|         self, | ||||
|         name, | ||||
|         srcs, | ||||
|         deps, | ||||
|         hdrs, | ||||
|         caller_cflags, | ||||
|         caller_ldflags, | ||||
|         cflags, | ||||
|         ldflags, | ||||
|         toolchain, | ||||
|         toolchain.AR, | ||||
|         toolchain.PREFIX + label, | ||||
|         cfilerule, | ||||
|     ) | ||||
|  | ||||
| @@ -271,14 +367,9 @@ def cxxlibrary( | ||||
|     cflags=[], | ||||
|     ldflags=[], | ||||
|     toolchain=Toolchain, | ||||
|     commands=None, | ||||
|     label=None, | ||||
|     label="CXXLIB", | ||||
|     cxxfilerule=cxxfile, | ||||
| ): | ||||
|     if not label: | ||||
|         label = toolchain.label + "LIB" | ||||
|     if not commands: | ||||
|         commands = toolchain.cxxlibrary | ||||
|     libraryimpl( | ||||
|         self, | ||||
|         name, | ||||
| @@ -290,8 +381,40 @@ def cxxlibrary( | ||||
|         cflags, | ||||
|         ldflags, | ||||
|         toolchain, | ||||
|         commands, | ||||
|         label, | ||||
|         toolchain.ARXX, | ||||
|         toolchain.PREFIX + label, | ||||
|         cxxfilerule, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def hostcxxlibrary( | ||||
|     self, | ||||
|     name, | ||||
|     srcs: Targets = None, | ||||
|     deps: Targets = None, | ||||
|     hdrs: TargetsMap = None, | ||||
|     caller_cflags=[], | ||||
|     caller_ldflags=[], | ||||
|     cflags=[], | ||||
|     ldflags=[], | ||||
|     toolchain=HostToolchain, | ||||
|     label="CXXLIB", | ||||
|     cxxfilerule=cxxfile, | ||||
| ): | ||||
|     libraryimpl( | ||||
|         self, | ||||
|         name, | ||||
|         srcs, | ||||
|         deps, | ||||
|         hdrs, | ||||
|         caller_cflags, | ||||
|         caller_ldflags, | ||||
|         cflags, | ||||
|         ldflags, | ||||
|         toolchain, | ||||
|         toolchain.ARXX, | ||||
|         toolchain.PREFIX + label, | ||||
|         cxxfilerule, | ||||
|     ) | ||||
|  | ||||
| @@ -307,16 +430,13 @@ def programimpl( | ||||
|     commands, | ||||
|     label, | ||||
|     filerule, | ||||
|     kind, | ||||
| ): | ||||
|     cfiles = findsources( | ||||
|         self.localname, srcs, deps, cflags, toolchain, filerule, self.cwd | ||||
|         self, srcs, deps, cflags, filerule, toolchain, self.cwd | ||||
|     ) | ||||
|  | ||||
|     lib_deps = set() | ||||
|     for d in deps: | ||||
|         lib_deps.update(d.args.get("clibrary_deps", {d})) | ||||
|     libs = sorted(filenamesmatchingof(lib_deps, "*.a")) | ||||
|     lib_deps = toolchain.find_c_library_targets(deps) | ||||
|     libs = collectattrs(targets=lib_deps, name="clibrary_files") | ||||
|     ldflags = collectattrs( | ||||
|         targets=lib_deps, name="caller_ldflags", initial=ldflags | ||||
|     ) | ||||
| @@ -324,15 +444,11 @@ def programimpl( | ||||
|     simplerule( | ||||
|         replaces=self, | ||||
|         ins=cfiles + libs, | ||||
|         outs=[f"={self.localname}$(EXT)"], | ||||
|         deps=sorted(_indirect(lib_deps, "clibrary_files")), | ||||
|         label=toolchain.label + label, | ||||
|         outs=[f"={self.localname}{toolchain.EXE}"], | ||||
|         deps=deps, | ||||
|         label=label, | ||||
|         commands=commands, | ||||
|         args={ | ||||
|             "ldflags": collectattrs( | ||||
|                 targets=lib_deps, name="caller_ldflags", initial=ldflags | ||||
|             ) | ||||
|         }, | ||||
|         args={"ldflags": ldflags}, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @@ -345,11 +461,9 @@ def cprogram( | ||||
|     cflags=[], | ||||
|     ldflags=[], | ||||
|     toolchain=Toolchain, | ||||
|     commands=None, | ||||
|     label="CLINK", | ||||
|     cfilerule=cfile, | ||||
| ): | ||||
|     if not commands: | ||||
|         commands = toolchain.cprogram | ||||
|     programimpl( | ||||
|         self, | ||||
|         name, | ||||
| @@ -358,10 +472,35 @@ def cprogram( | ||||
|         cflags, | ||||
|         ldflags, | ||||
|         toolchain, | ||||
|         commands, | ||||
|         label, | ||||
|         cfile, | ||||
|         "cprogram", | ||||
|         toolchain.CLINK, | ||||
|         toolchain.PREFIX + label, | ||||
|         cfilerule, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def hostcprogram( | ||||
|     self, | ||||
|     name, | ||||
|     srcs: Targets = None, | ||||
|     deps: Targets = None, | ||||
|     cflags=[], | ||||
|     ldflags=[], | ||||
|     toolchain=HostToolchain, | ||||
|     label="CLINK", | ||||
|     cfilerule=cfile, | ||||
| ): | ||||
|     programimpl( | ||||
|         self, | ||||
|         name, | ||||
|         srcs, | ||||
|         deps, | ||||
|         cflags, | ||||
|         ldflags, | ||||
|         toolchain, | ||||
|         toolchain.CLINK, | ||||
|         toolchain.PREFIX + label, | ||||
|         cfilerule, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @@ -374,11 +513,9 @@ def cxxprogram( | ||||
|     cflags=[], | ||||
|     ldflags=[], | ||||
|     toolchain=Toolchain, | ||||
|     commands=None, | ||||
|     label="CXXLINK", | ||||
|     cxxfilerule=cxxfile, | ||||
| ): | ||||
|     if not commands: | ||||
|         commands = toolchain.cxxprogram | ||||
|     programimpl( | ||||
|         self, | ||||
|         name, | ||||
| @@ -387,8 +524,74 @@ def cxxprogram( | ||||
|         cflags, | ||||
|         ldflags, | ||||
|         toolchain, | ||||
|         commands, | ||||
|         label, | ||||
|         cxxfile, | ||||
|         "cxxprogram", | ||||
|         toolchain.CXXLINK, | ||||
|         toolchain.PREFIX + label, | ||||
|         cxxfilerule, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def hostcxxprogram( | ||||
|     self, | ||||
|     name, | ||||
|     srcs: Targets = None, | ||||
|     deps: Targets = None, | ||||
|     cflags=[], | ||||
|     ldflags=[], | ||||
|     toolchain=HostToolchain, | ||||
|     label="CXXLINK", | ||||
|     cxxfilerule=cxxfile, | ||||
| ): | ||||
|     programimpl( | ||||
|         self, | ||||
|         name, | ||||
|         srcs, | ||||
|         deps, | ||||
|         cflags, | ||||
|         ldflags, | ||||
|         toolchain, | ||||
|         toolchain.CXXLINK, | ||||
|         toolchain.PREFIX + label, | ||||
|         cxxfilerule, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def _cppfileimpl(self, name, srcs, deps, cflags, toolchain): | ||||
|     hdr_deps = _indirect(deps, "cheader_deps") | ||||
|     cflags = collectattrs( | ||||
|         targets=hdr_deps, name="caller_cflags", initial=cflags | ||||
|     ) | ||||
|  | ||||
|     simplerule( | ||||
|         replaces=self, | ||||
|         ins=srcs, | ||||
|         outs=[f"={self.localname}"], | ||||
|         deps=deps, | ||||
|         commands=toolchain.CPP, | ||||
|         args={"cflags": cflags}, | ||||
|         label=toolchain.PREFIX + "CPPFILE", | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def cppfile( | ||||
|     self, | ||||
|     name, | ||||
|     srcs: Targets = [], | ||||
|     deps: Targets = [], | ||||
|     cflags=[], | ||||
|     toolchain=Toolchain, | ||||
| ): | ||||
|     _cppfileimpl(self, name, srcs, deps, cflags, toolchain) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def hostcppfile( | ||||
|     self, | ||||
|     name, | ||||
|     srcs: Targets = [], | ||||
|     deps: Targets = [], | ||||
|     cflags=[], | ||||
|     toolchain=HostToolchain, | ||||
| ): | ||||
|     _cppfileimpl(self, name, srcs, deps, cflags, toolchain) | ||||
|   | ||||
							
								
								
									
										18
									
								
								build/pkg.py
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								build/pkg.py
									
									
									
									
									
								
							| @@ -1,5 +1,4 @@ | ||||
| from build.ab import Rule, emit, Target, filenamesof | ||||
| from types import SimpleNamespace | ||||
| from build.ab import Rule, Target | ||||
| import os | ||||
| import subprocess | ||||
|  | ||||
| @@ -45,13 +44,14 @@ def _package(self, name, package, fallback, pkgconfig): | ||||
|             self.args["caller_cflags"] = [cflags] | ||||
|         if ldflags: | ||||
|             self.args["caller_ldflags"] = [ldflags] | ||||
|         self.traits.add("clibrary") | ||||
|         self.traits.add("cheaders") | ||||
|         self.args["clibrary_deps"] = [self] | ||||
|         self.args["cheader_deps"] = [self] | ||||
|         self.traits.update({"clibrary", "cxxlibrary"}) | ||||
|         return | ||||
|  | ||||
|     assert ( | ||||
|         fallback | ||||
|     ), f"Required package '{package}' not installed when materialising target '{name}'" | ||||
|     ), f"Required package '{package}' not installed when materialising target '$[name]'" | ||||
|  | ||||
|     if "cheader_deps" in fallback.args: | ||||
|         self.args["cheader_deps"] = fallback.args["cheader_deps"] | ||||
| @@ -75,3 +75,11 @@ def package(self, name, package=None, fallback: Target = None): | ||||
| @Rule | ||||
| def hostpackage(self, name, package=None, fallback: Target = None): | ||||
|     _package(self, name, package, fallback, HostPkgConfig) | ||||
|  | ||||
|  | ||||
| def has_package(name): | ||||
|     return TargetPkgConfig.has_package(name) | ||||
|  | ||||
|  | ||||
| def has_host_package(name): | ||||
|     return HostPkgConfig.has_package(name) | ||||
|   | ||||
| @@ -1,17 +1,16 @@ | ||||
| from build.ab import Rule, Targets, emit, simplerule, filenamesof | ||||
| from build.utils import filenamesmatchingof, collectattrs | ||||
| from os.path import join, abspath, dirname, relpath | ||||
| import build.pkg  # to get the protobuf package check | ||||
| from build.pkg import has_package | ||||
|  | ||||
| emit( | ||||
|     """ | ||||
| PROTOC ?= protoc | ||||
| HOSTPROTOC ?= protoc | ||||
| """ | ||||
| ) | ||||
|  | ||||
| assert build.pkg.TargetPkgConfig.has_package( | ||||
|     "protobuf" | ||||
| ), "required package 'protobuf' not installed" | ||||
| assert has_package("protobuf"), "required package 'protobuf' not installed" | ||||
|  | ||||
|  | ||||
| def _getprotodeps(deps): | ||||
| @@ -31,7 +30,7 @@ def proto(self, name, srcs: Targets = [], deps: Targets = []): | ||||
|         ] | ||||
|     ) | ||||
|  | ||||
|     dirs = sorted({"{dir}/" + dirname(f) for f in filenamesof(srcs)}) | ||||
|     dirs = sorted({"$[dir]/" + dirname(f) for f in filenamesof(srcs)}) | ||||
|     simplerule( | ||||
|         replaces=self, | ||||
|         ins=srcs, | ||||
| @@ -39,9 +38,9 @@ def proto(self, name, srcs: Targets = [], deps: Targets = []): | ||||
|         deps=protodeps, | ||||
|         commands=( | ||||
|             ["mkdir -p " + (" ".join(dirs))] | ||||
|             + [f"$(CP) {f} {{dir}}/{f}" for f in filenamesof(srcs)] | ||||
|             + [f"$(CP) {f} $[dir]/{f}" for f in filenamesof(srcs)] | ||||
|             + [ | ||||
|                 "cd {dir} && " | ||||
|                 "cd $[dir] && " | ||||
|                 + ( | ||||
|                     " ".join( | ||||
|                         [ | ||||
| @@ -55,7 +54,7 @@ def proto(self, name, srcs: Targets = [], deps: Targets = []): | ||||
|                             if descriptorlist | ||||
|                             else [] | ||||
|                         ) | ||||
|                         + ["{ins}"] | ||||
|                         + ["$[ins]"] | ||||
|                     ) | ||||
|                 ) | ||||
|             ] | ||||
| @@ -68,6 +67,18 @@ def proto(self, name, srcs: Targets = [], deps: Targets = []): | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def protolib(self, name, srcs: Targets = []): | ||||
|     simplerule( | ||||
|         replaces=self, | ||||
|         label="PROTOLIB", | ||||
|         args={ | ||||
|             "protosrcs": collectattrs(targets=srcs, name="protosrcs"), | ||||
|             "protodeps": set(_getprotodeps(srcs)), | ||||
|         }, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @Rule | ||||
| def protocc(self, name, srcs: Targets = [], deps: Targets = []): | ||||
|     outs = [] | ||||
| @@ -96,7 +107,7 @@ def protocc(self, name, srcs: Targets = [], deps: Targets = []): | ||||
|         outs=outs, | ||||
|         deps=protodeps, | ||||
|         commands=[ | ||||
|             "cd {dir} && " | ||||
|             "cd $[dir] && " | ||||
|             + ( | ||||
|                 " ".join( | ||||
|                     [ | ||||
| @@ -146,8 +157,8 @@ def protojava(self, name, srcs: Targets = [], deps: Targets = []): | ||||
|         outs=[f"={self.localname}.srcjar"], | ||||
|         deps=srcs + deps, | ||||
|         commands=[ | ||||
|             "mkdir -p {dir}/srcs", | ||||
|             "cd {dir} && " | ||||
|             "mkdir -p $[dir]/srcs", | ||||
|             "cd $[dir]/srcs && " | ||||
|             + ( | ||||
|                 " ".join( | ||||
|                     [ | ||||
| @@ -159,7 +170,7 @@ def protojava(self, name, srcs: Targets = [], deps: Targets = []): | ||||
|                     + protos | ||||
|                 ) | ||||
|             ), | ||||
|             "$(JAR) cf {outs[0]} -C {dir}/srcs .", | ||||
|             "$(JAR) cf $[outs[0]] -C $[dir]/srcs .", | ||||
|         ], | ||||
|         traits={"srcjar"}, | ||||
|         label="PROTOJAVA", | ||||
|   | ||||
							
								
								
									
										11
									
								
								build/toolchain.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								build/toolchain.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| import platform | ||||
|  | ||||
| _is_windows = (platform.system() == "Windows") | ||||
|  | ||||
| class Toolchain: | ||||
|     PREFIX = "" | ||||
|     EXE = ".exe" if _is_windows else "" | ||||
|  | ||||
|  | ||||
| class HostToolchain(Toolchain): | ||||
|     PREFIX = "HOST" | ||||
| @@ -11,7 +11,6 @@ from build.ab import ( | ||||
| from os.path import relpath, splitext, join, basename, isfile | ||||
| from glob import iglob | ||||
| import fnmatch | ||||
| import itertools | ||||
|  | ||||
|  | ||||
| def filenamesmatchingof(xs, pattern): | ||||
| @@ -58,7 +57,7 @@ def objectify(self, name, src: Target, symbol): | ||||
|         replaces=self, | ||||
|         ins=["build/_objectify.py", src], | ||||
|         outs=[f"={basename(filenameof(src))}.h"], | ||||
|         commands=["$(PYTHON) {ins[0]} {ins[1]} " + symbol + " > {outs}"], | ||||
|         commands=["$(PYTHON) $[ins[0]] $[ins[1]] " + symbol + " > $[outs]"], | ||||
|         label="OBJECTIFY", | ||||
|     ) | ||||
|  | ||||
| @@ -78,7 +77,7 @@ def test( | ||||
|             replaces=self, | ||||
|             ins=[command], | ||||
|             outs=["=sentinel"], | ||||
|             commands=["{ins[0]}", "touch {outs}"], | ||||
|             commands=["$[ins[0]]", "touch $[outs[0]]"], | ||||
|             deps=deps, | ||||
|             label=label, | ||||
|         ) | ||||
| @@ -87,7 +86,7 @@ def test( | ||||
|             replaces=self, | ||||
|             ins=ins, | ||||
|             outs=["=sentinel"], | ||||
|             commands=commands + ["touch {outs}"], | ||||
|             commands=commands + ["touch $[outs[0]]"], | ||||
|             deps=deps, | ||||
|             label=label, | ||||
|         ) | ||||
|   | ||||
							
								
								
									
										18
									
								
								build/zip.py
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								build/zip.py
									
									
									
									
									
								
							| @@ -3,14 +3,6 @@ from build.ab import ( | ||||
|     simplerule, | ||||
|     TargetsMap, | ||||
|     filenameof, | ||||
|     emit, | ||||
| ) | ||||
|  | ||||
| emit( | ||||
|     """ | ||||
| ZIP ?= zip | ||||
| ZIPNOTE ?= zipnote | ||||
| """ | ||||
| ) | ||||
|  | ||||
|  | ||||
| @@ -18,20 +10,18 @@ ZIPNOTE ?= zipnote | ||||
| def zip( | ||||
|     self, name, flags="", items: TargetsMap = {}, extension="zip", label="ZIP" | ||||
| ): | ||||
|     cs = ["rm -f {outs[0]}"] | ||||
|     cs = ["$(PYTHON) build/_zip.py -z $[outs]"] | ||||
|  | ||||
|     ins = [] | ||||
|     for k, v in items.items(): | ||||
|         cs += [ | ||||
|             "cat %s | $(ZIP) -q %s {outs[0]} -" % (filenameof(v), flags), | ||||
|             "printf '@ -\\n@=%s\\n' | $(ZIPNOTE) -w {outs[0]}" % k, | ||||
|         ] | ||||
|         cs += [f"-f {k} {filenameof(v)}"] | ||||
|         ins += [v] | ||||
|  | ||||
|     simplerule( | ||||
|         replaces=self, | ||||
|         ins=ins, | ||||
|         deps=["build/_zip.py"], | ||||
|         outs=[f"={self.localname}." + extension], | ||||
|         commands=cs, | ||||
|         commands=[" ".join(cs)], | ||||
|         label=label, | ||||
|     ) | ||||
|   | ||||
							
								
								
									
										2
									
								
								dep/alphanum/UPSTREAM.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								dep/alphanum/UPSTREAM.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| Downloaded from: | ||||
| https://web.archive.org/web/20210918044134/http://davekoelle.com/files/alphanum.hpp | ||||
							
								
								
									
										450
									
								
								dep/alphanum/alphanum.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										450
									
								
								dep/alphanum/alphanum.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,450 @@ | ||||
| #ifndef ALPHANUM__HPP | ||||
| #define ALPHANUM__HPP | ||||
|  | ||||
| /* | ||||
|  Released under the MIT License - https://opensource.org/licenses/MIT | ||||
|  | ||||
|  Permission is hereby granted, free of charge, to any person obtaining | ||||
|  a copy of this software and associated documentation files (the "Software"), | ||||
|  to deal in the Software without restriction, including without limitation | ||||
|  the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||||
|  and/or sell copies of the Software, and to permit persons to whom the | ||||
|  Software is furnished to do so, subject to the following conditions: | ||||
|  | ||||
|  The above copyright notice and this permission notice shall be included | ||||
|  in all copies or substantial portions of the Software. | ||||
|  | ||||
|  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||||
|  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||||
|  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, | ||||
|  DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | ||||
|  OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | ||||
|  USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
| */ | ||||
|  | ||||
| /* $Header: /code/doj/alphanum.hpp,v 1.3 2008/01/28 23:06:47 doj Exp $ */ | ||||
|  | ||||
| #include <cassert> | ||||
| #include <functional> | ||||
| #include <string> | ||||
| #include <sstream> | ||||
|  | ||||
| #ifdef ALPHANUM_LOCALE | ||||
| #include <cctype> | ||||
| #endif | ||||
|  | ||||
| #ifdef DOJDEBUG | ||||
| #include <iostream> | ||||
| #include <typeinfo> | ||||
| #endif | ||||
|  | ||||
| // TODO: make comparison with hexadecimal numbers. Extend the alphanum_comp() function by traits to choose between decimal and hexadecimal. | ||||
|  | ||||
| namespace doj | ||||
| { | ||||
|  | ||||
|   // anonymous namespace for functions we use internally. But if you | ||||
|   // are coding in C, you can use alphanum_impl() directly, since it | ||||
|   // uses not C++ features. | ||||
|   namespace { | ||||
|  | ||||
|     // if you want to honour the locale settings for detecting digit | ||||
|     // characters, you should define ALPHANUM_LOCALE | ||||
| #ifdef ALPHANUM_LOCALE | ||||
|     /** wrapper function for ::isdigit() */ | ||||
|     bool alphanum_isdigit(int c) | ||||
|     { | ||||
|       return isdigit(c); | ||||
|     } | ||||
| #else | ||||
|     /** this function does not consider the current locale and only | ||||
| 	works with ASCII digits. | ||||
| 	@return true if c is a digit character | ||||
|     */ | ||||
|     bool alphanum_isdigit(const char c) | ||||
|     { | ||||
|       return c>='0' && c<='9'; | ||||
|     } | ||||
| #endif | ||||
|  | ||||
|     /** | ||||
|        compare l and r with strcmp() semantics, but using | ||||
|        the "Alphanum Algorithm". This function is designed to read | ||||
|        through the l and r strings only one time, for | ||||
|        maximum performance. It does not allocate memory for | ||||
|        substrings. It can either use the C-library functions isdigit() | ||||
|        and atoi() to honour your locale settings, when recognizing | ||||
|        digit characters when you "#define ALPHANUM_LOCALE=1" or use | ||||
|        it's own digit character handling which only works with ASCII | ||||
|        digit characters, but provides better performance. | ||||
|  | ||||
|        @param l NULL-terminated C-style string | ||||
|        @param r NULL-terminated C-style string | ||||
|        @return negative if l<r, 0 if l equals r, positive if l>r | ||||
|      */ | ||||
|     int alphanum_impl(const char *l, const char *r) | ||||
|     { | ||||
|       enum mode_t { STRING, NUMBER } mode=STRING; | ||||
|  | ||||
|       while(*l && *r) | ||||
| 	{ | ||||
| 	  if(mode == STRING) | ||||
| 	    { | ||||
| 	      char l_char, r_char; | ||||
| 	      while((l_char=*l) && (r_char=*r)) | ||||
| 		{ | ||||
| 		  // check if this are digit characters | ||||
| 		  const bool l_digit=alphanum_isdigit(l_char), r_digit=alphanum_isdigit(r_char); | ||||
| 		  // if both characters are digits, we continue in NUMBER mode | ||||
| 		  if(l_digit && r_digit) | ||||
| 		    { | ||||
| 		      mode=NUMBER; | ||||
| 		      break; | ||||
| 		    } | ||||
| 		  // if only the left character is a digit, we have a result | ||||
| 		  if(l_digit) return -1; | ||||
| 		  // if only the right character is a digit, we have a result | ||||
| 		  if(r_digit) return +1; | ||||
| 		  // compute the difference of both characters | ||||
| 		  const int diff=l_char - r_char; | ||||
| 		  // if they differ we have a result | ||||
| 		  if(diff != 0) return diff; | ||||
| 		  // otherwise process the next characters | ||||
| 		  ++l; | ||||
| 		  ++r; | ||||
| 		} | ||||
| 	    } | ||||
| 	  else // mode==NUMBER | ||||
| 	    { | ||||
| #ifdef ALPHANUM_LOCALE | ||||
| 	      // get the left number | ||||
| 	      char *end; | ||||
| 	      unsigned long l_int=strtoul(l, &end, 0); | ||||
| 	      l=end; | ||||
|  | ||||
| 	      // get the right number | ||||
| 	      unsigned long r_int=strtoul(r, &end, 0); | ||||
| 	      r=end; | ||||
| #else | ||||
| 	      // get the left number | ||||
| 	      unsigned long l_int=0; | ||||
| 	      while(*l && alphanum_isdigit(*l)) | ||||
| 		{ | ||||
| 		  // TODO: this can overflow | ||||
| 		  l_int=l_int*10 + *l-'0'; | ||||
| 		  ++l; | ||||
| 		} | ||||
|  | ||||
| 	      // get the right number | ||||
| 	      unsigned long r_int=0; | ||||
| 	      while(*r && alphanum_isdigit(*r)) | ||||
| 		{ | ||||
| 		  // TODO: this can overflow | ||||
| 		  r_int=r_int*10 + *r-'0'; | ||||
| 		  ++r; | ||||
| 		} | ||||
| #endif | ||||
|  | ||||
| 	      // if the difference is not equal to zero, we have a comparison result | ||||
| 	      const long diff=l_int-r_int; | ||||
| 	      if(diff != 0) | ||||
| 		return diff; | ||||
|  | ||||
| 	      // otherwise we process the next substring in STRING mode | ||||
| 	      mode=STRING; | ||||
| 	    } | ||||
| 	} | ||||
|  | ||||
|       if(*r) return -1; | ||||
|       if(*l) return +1; | ||||
|       return 0; | ||||
|     } | ||||
|  | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|      Compare left and right with the same semantics as strcmp(), but with the | ||||
|      "Alphanum Algorithm" which produces more human-friendly | ||||
|      results. The classes lT and rT must implement "std::ostream | ||||
|      operator<< (std::ostream&, const Ty&)". | ||||
|  | ||||
|      @return negative if left<right, 0 if left==right, positive if left>right. | ||||
|   */ | ||||
|   template <typename lT, typename rT> | ||||
|   int alphanum_comp(const lT& left, const rT& right) | ||||
|   { | ||||
| #ifdef DOJDEBUG | ||||
|     std::clog << "alphanum_comp<" << typeid(left).name() << "," << typeid(right).name() << "> " << left << "," << right << std::endl; | ||||
| #endif | ||||
|     std::ostringstream l; l << left; | ||||
|     std::ostringstream r; r << right; | ||||
|     return alphanum_impl(l.str().c_str(), r.str().c_str()); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|      Compare l and r with the same semantics as strcmp(), but with | ||||
|      the "Alphanum Algorithm" which produces more human-friendly | ||||
|      results. | ||||
|  | ||||
|      @return negative if l<r, 0 if l==r, positive if l>r. | ||||
|   */ | ||||
|   template <> | ||||
|   int alphanum_comp<std::string>(const std::string& l, const std::string& r) | ||||
|   { | ||||
| #ifdef DOJDEBUG | ||||
|     std::clog << "alphanum_comp<std::string,std::string> " << l << "," << r << std::endl; | ||||
| #endif | ||||
|     return alphanum_impl(l.c_str(), r.c_str()); | ||||
|   } | ||||
|  | ||||
|   //////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|   // now follow a lot of overloaded alphanum_comp() functions to get a | ||||
|   // direct call to alphanum_impl() upon the various combinations of c | ||||
|   // and c++ strings. | ||||
|  | ||||
|   /** | ||||
|      Compare l and r with the same semantics as strcmp(), but with | ||||
|      the "Alphanum Algorithm" which produces more human-friendly | ||||
|      results. | ||||
|  | ||||
|      @return negative if l<r, 0 if l==r, positive if l>r. | ||||
|   */ | ||||
|   int alphanum_comp(char* l, char* r) | ||||
|   { | ||||
|     assert(l); | ||||
|     assert(r); | ||||
| #ifdef DOJDEBUG | ||||
|     std::clog << "alphanum_comp<char*,char*> " << l << "," << r << std::endl; | ||||
| #endif | ||||
|     return alphanum_impl(l, r); | ||||
|   } | ||||
|  | ||||
|   int alphanum_comp(const char* l, const char* r) | ||||
|   { | ||||
|     assert(l); | ||||
|     assert(r); | ||||
| #ifdef DOJDEBUG | ||||
|     std::clog << "alphanum_comp<const char*,const char*> " << l << "," << r << std::endl; | ||||
| #endif | ||||
|     return alphanum_impl(l, r); | ||||
|   } | ||||
|  | ||||
|   int alphanum_comp(char* l, const char* r) | ||||
|   { | ||||
|     assert(l); | ||||
|     assert(r); | ||||
| #ifdef DOJDEBUG | ||||
|     std::clog << "alphanum_comp<char*,const char*> " << l << "," << r << std::endl; | ||||
| #endif | ||||
|     return alphanum_impl(l, r); | ||||
|   } | ||||
|  | ||||
|   int alphanum_comp(const char* l, char* r) | ||||
|   { | ||||
|     assert(l); | ||||
|     assert(r); | ||||
| #ifdef DOJDEBUG | ||||
|     std::clog << "alphanum_comp<const char*,char*> " << l << "," << r << std::endl; | ||||
| #endif | ||||
|     return alphanum_impl(l, r); | ||||
|   } | ||||
|  | ||||
|   int alphanum_comp(const std::string& l, char* r) | ||||
|   { | ||||
|     assert(r); | ||||
| #ifdef DOJDEBUG | ||||
|     std::clog << "alphanum_comp<std::string,char*> " << l << "," << r << std::endl; | ||||
| #endif | ||||
|     return alphanum_impl(l.c_str(), r); | ||||
|   } | ||||
|  | ||||
|   int alphanum_comp(char* l, const std::string& r) | ||||
|   { | ||||
|     assert(l); | ||||
| #ifdef DOJDEBUG | ||||
|     std::clog << "alphanum_comp<char*,std::string> " << l << "," << r << std::endl; | ||||
| #endif | ||||
|     return alphanum_impl(l, r.c_str()); | ||||
|   } | ||||
|  | ||||
|   int alphanum_comp(const std::string& l, const char* r) | ||||
|   { | ||||
|     assert(r); | ||||
| #ifdef DOJDEBUG | ||||
|     std::clog << "alphanum_comp<std::string,const char*> " << l << "," << r << std::endl; | ||||
| #endif | ||||
|     return alphanum_impl(l.c_str(), r); | ||||
|   } | ||||
|  | ||||
|   int alphanum_comp(const char* l, const std::string& r) | ||||
|   { | ||||
|     assert(l); | ||||
| #ifdef DOJDEBUG | ||||
|     std::clog << "alphanum_comp<const char*,std::string> " << l << "," << r << std::endl; | ||||
| #endif | ||||
|     return alphanum_impl(l, r.c_str()); | ||||
|   } | ||||
|  | ||||
|   //////////////////////////////////////////////////////////////////////////// | ||||
|  | ||||
|   /** | ||||
|      Functor class to compare two objects with the "Alphanum | ||||
|      Algorithm". If the objects are no std::string, they must | ||||
|      implement "std::ostream operator<< (std::ostream&, const Ty&)". | ||||
|   */ | ||||
|   template<class Ty> | ||||
|   struct alphanum_less | ||||
|   { | ||||
|     bool operator()(const Ty& left, const Ty& right) const | ||||
|     { | ||||
|       return alphanum_comp(left, right) < 0; | ||||
|     } | ||||
|   }; | ||||
|  | ||||
| } | ||||
|  | ||||
| #ifdef TESTMAIN | ||||
| #include <algorithm> | ||||
| #include <iostream> | ||||
| #include <iterator> | ||||
| #include <map> | ||||
| #include <set> | ||||
| #include <vector> | ||||
| int main() | ||||
| { | ||||
|   // testcases for the algorithm | ||||
|   assert(doj::alphanum_comp("","") == 0); | ||||
|   assert(doj::alphanum_comp("","a") < 0); | ||||
|   assert(doj::alphanum_comp("a","") > 0); | ||||
|   assert(doj::alphanum_comp("a","a") == 0); | ||||
|   assert(doj::alphanum_comp("","9") < 0); | ||||
|   assert(doj::alphanum_comp("9","") > 0); | ||||
|   assert(doj::alphanum_comp("1","1") == 0); | ||||
|   assert(doj::alphanum_comp("1","2") < 0); | ||||
|   assert(doj::alphanum_comp("3","2") > 0); | ||||
|   assert(doj::alphanum_comp("a1","a1") == 0); | ||||
|   assert(doj::alphanum_comp("a1","a2") < 0); | ||||
|   assert(doj::alphanum_comp("a2","a1") > 0); | ||||
|   assert(doj::alphanum_comp("a1a2","a1a3") < 0); | ||||
|   assert(doj::alphanum_comp("a1a2","a1a0") > 0); | ||||
|   assert(doj::alphanum_comp("134","122") > 0); | ||||
|   assert(doj::alphanum_comp("12a3","12a3") == 0); | ||||
|   assert(doj::alphanum_comp("12a1","12a0") > 0); | ||||
|   assert(doj::alphanum_comp("12a1","12a2") < 0); | ||||
|   assert(doj::alphanum_comp("a","aa") < 0); | ||||
|   assert(doj::alphanum_comp("aaa","aa") > 0); | ||||
|   assert(doj::alphanum_comp("Alpha 2","Alpha 2") == 0); | ||||
|   assert(doj::alphanum_comp("Alpha 2","Alpha 2A") < 0); | ||||
|   assert(doj::alphanum_comp("Alpha 2 B","Alpha 2") > 0); | ||||
|  | ||||
|   assert(doj::alphanum_comp(1,1) == 0); | ||||
|   assert(doj::alphanum_comp(1,2) < 0); | ||||
|   assert(doj::alphanum_comp(2,1) > 0); | ||||
|   assert(doj::alphanum_comp(1.2,3.14) < 0); | ||||
|   assert(doj::alphanum_comp(3.14,2.71) > 0); | ||||
|   assert(doj::alphanum_comp(true,true) == 0); | ||||
|   assert(doj::alphanum_comp(true,false) > 0); | ||||
|   assert(doj::alphanum_comp(false,true) < 0); | ||||
|  | ||||
|   std::string str("Alpha 2"); | ||||
|   assert(doj::alphanum_comp(str,"Alpha 2") == 0); | ||||
|   assert(doj::alphanum_comp(str,"Alpha 2A") < 0); | ||||
|   assert(doj::alphanum_comp("Alpha 2 B",str) > 0); | ||||
|  | ||||
|   assert(doj::alphanum_comp(str,strdup("Alpha 2")) == 0); | ||||
|   assert(doj::alphanum_comp(str,strdup("Alpha 2A")) < 0); | ||||
|   assert(doj::alphanum_comp(strdup("Alpha 2 B"),str) > 0); | ||||
|  | ||||
| #if 1 | ||||
|   // show usage of the comparison functor with a set | ||||
|   std::set<std::string, doj::alphanum_less<std::string> > s; | ||||
|   s.insert("Xiph Xlater 58"); | ||||
|   s.insert("Xiph Xlater 5000"); | ||||
|   s.insert("Xiph Xlater 500"); | ||||
|   s.insert("Xiph Xlater 50"); | ||||
|   s.insert("Xiph Xlater 5"); | ||||
|   s.insert("Xiph Xlater 40"); | ||||
|   s.insert("Xiph Xlater 300"); | ||||
|   s.insert("Xiph Xlater 2000"); | ||||
|   s.insert("Xiph Xlater 10000"); | ||||
|   s.insert("QRS-62F Intrinsia Machine"); | ||||
|   s.insert("QRS-62 Intrinsia Machine"); | ||||
|   s.insert("QRS-60F Intrinsia Machine"); | ||||
|   s.insert("QRS-60 Intrinsia Machine"); | ||||
|   s.insert("Callisto Morphamax 7000 SE2"); | ||||
|   s.insert("Callisto Morphamax 7000 SE"); | ||||
|   s.insert("Callisto Morphamax 7000"); | ||||
|   s.insert("Callisto Morphamax 700"); | ||||
|   s.insert("Callisto Morphamax 600"); | ||||
|   s.insert("Callisto Morphamax 5000"); | ||||
|   s.insert("Callisto Morphamax 500"); | ||||
|   s.insert("Callisto Morphamax"); | ||||
|   s.insert("Alpha 2A-900"); | ||||
|   s.insert("Alpha 2A-8000"); | ||||
|   s.insert("Alpha 2A"); | ||||
|   s.insert("Alpha 200"); | ||||
|   s.insert("Alpha 2"); | ||||
|   s.insert("Alpha 100"); | ||||
|   s.insert("Allegia 60 Clasteron"); | ||||
|   s.insert("Allegia 52 Clasteron"); | ||||
|   s.insert("Allegia 51B Clasteron"); | ||||
|   s.insert("Allegia 51 Clasteron"); | ||||
|   s.insert("Allegia 500 Clasteron"); | ||||
|   s.insert("Allegia 50 Clasteron"); | ||||
|   s.insert("40X Radonius"); | ||||
|   s.insert("30X Radonius"); | ||||
|   s.insert("20X Radonius Prime"); | ||||
|   s.insert("20X Radonius"); | ||||
|   s.insert("200X Radonius"); | ||||
|   s.insert("10X Radonius"); | ||||
|   s.insert("1000X Radonius Maximus"); | ||||
|   // print sorted set to cout | ||||
|   std::copy(s.begin(), s.end(), std::ostream_iterator<std::string>(std::cout, "\n")); | ||||
|  | ||||
|   // show usage of comparision functor with a map | ||||
|   typedef std::map<std::string, int, doj::alphanum_less<std::string> > m_t; | ||||
|   m_t m; | ||||
|   m["z1.doc"]=1; | ||||
|   m["z10.doc"]=2; | ||||
|   m["z100.doc"]=3; | ||||
|   m["z101.doc"]=4; | ||||
|   m["z102.doc"]=5; | ||||
|   m["z11.doc"]=6; | ||||
|   m["z12.doc"]=7; | ||||
|   m["z13.doc"]=8; | ||||
|   m["z14.doc"]=9; | ||||
|   m["z15.doc"]=10; | ||||
|   m["z16.doc"]=11; | ||||
|   m["z17.doc"]=12; | ||||
|   m["z18.doc"]=13; | ||||
|   m["z19.doc"]=14; | ||||
|   m["z2.doc"]=15; | ||||
|   m["z20.doc"]=16; | ||||
|   m["z3.doc"]=17; | ||||
|   m["z4.doc"]=18; | ||||
|   m["z5.doc"]=19; | ||||
|   m["z6.doc"]=20; | ||||
|   m["z7.doc"]=21; | ||||
|   m["z8.doc"]=22; | ||||
|   m["z9.doc"]=23; | ||||
|   // print sorted map to cout | ||||
|   for(m_t::iterator i=m.begin(); i!=m.end(); ++i) | ||||
|     std::cout << i->first << '\t' << i->second << std::endl; | ||||
|  | ||||
|   // show usage of comparison functor with an STL algorithm on a vector | ||||
|   std::vector<std::string> v; | ||||
|   // vector contents are reversed sorted contents of the old set | ||||
|   std::copy(s.rbegin(), s.rend(), std::back_inserter(v)); | ||||
|   // now sort the vector with the algorithm | ||||
|   std::sort(v.begin(), v.end(), doj::alphanum_less<std::string>()); | ||||
|   // and print the vector to cout | ||||
|   std::copy(v.begin(), v.end(), std::ostream_iterator<std::string>(std::cout, "\n")); | ||||
| #endif | ||||
|  | ||||
|   return 0; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										8
									
								
								dep/alphanum/build.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								dep/alphanum/build.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| from build.c import clibrary | ||||
|  | ||||
| clibrary( | ||||
|     name="alphanum", | ||||
|     srcs=[], | ||||
|     hdrs={"dep/alphanum/alphanum.h": "./alphanum.h"}, | ||||
| ) | ||||
|  | ||||
| @@ -1,3 +1,7 @@ | ||||
| from build.c import clibrary | ||||
|  | ||||
| clibrary(name="emu", srcs=["./fnmatch.c"], hdrs={"fnmatch.h": "./fnmatch.h"}) | ||||
| clibrary( | ||||
|     name="emu", | ||||
|     srcs=["./fnmatch.c", "./charclass.h"], | ||||
|     hdrs={"fnmatch.h": "./fnmatch.h"}, | ||||
| ) | ||||
|   | ||||
| @@ -6,3 +6,9 @@ IndentPPDirectives: AfterHash | ||||
| IndentCaseLabels: false | ||||
| AlwaysBreakTemplateDeclarations: false | ||||
| DerivePointerAlignment: false | ||||
| AllowShortCaseLabelsOnASingleLine: true | ||||
| AlignConsecutiveShortCaseStatements: | ||||
|   Enabled: true | ||||
|   AcrossEmptyLines: true | ||||
|   AcrossComments: true | ||||
|   AlignCaseColons: false | ||||
| @@ -1,453 +0,0 @@ | ||||
| cmake_minimum_required(VERSION 3.8...3.26) | ||||
|  | ||||
| # Fallback for using newer policies on CMake <3.12. | ||||
| if (${CMAKE_VERSION} VERSION_LESS 3.12) | ||||
|   cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) | ||||
| endif () | ||||
|  | ||||
| # Determine if fmt is built as a subproject (using add_subdirectory) | ||||
| # or if it is the master project. | ||||
| if (NOT DEFINED FMT_MASTER_PROJECT) | ||||
|   set(FMT_MASTER_PROJECT OFF) | ||||
|   if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) | ||||
|     set(FMT_MASTER_PROJECT ON) | ||||
|     message(STATUS "CMake version: ${CMAKE_VERSION}") | ||||
|   endif () | ||||
| endif () | ||||
|  | ||||
| # Joins arguments and places the results in ${result_var}. | ||||
| function(join result_var) | ||||
|   set(result "") | ||||
|   foreach (arg ${ARGN}) | ||||
|     set(result "${result}${arg}") | ||||
|   endforeach () | ||||
|   set(${result_var} "${result}" PARENT_SCOPE) | ||||
| endfunction() | ||||
|  | ||||
| # DEPRECATED! Should be merged into add_module_library. | ||||
| function(enable_module target) | ||||
|   if (MSVC) | ||||
|     set(BMI ${CMAKE_CURRENT_BINARY_DIR}/${target}.ifc) | ||||
|     target_compile_options(${target} | ||||
|       PRIVATE /interface /ifcOutput ${BMI} | ||||
|       INTERFACE /reference fmt=${BMI}) | ||||
|     set_target_properties(${target} PROPERTIES ADDITIONAL_CLEAN_FILES ${BMI}) | ||||
|     set_source_files_properties(${BMI} PROPERTIES GENERATED ON) | ||||
|   endif () | ||||
| endfunction() | ||||
|  | ||||
| # Adds a library compiled with C++20 module support. | ||||
| # `enabled` is a CMake variables that specifies if modules are enabled. | ||||
| # If modules are disabled `add_module_library` falls back to creating a | ||||
| # non-modular library. | ||||
| # | ||||
| # Usage: | ||||
| #   add_module_library(<name> [sources...] FALLBACK [sources...] [IF enabled]) | ||||
| function(add_module_library name) | ||||
|   cmake_parse_arguments(AML "" "IF" "FALLBACK" ${ARGN}) | ||||
|   set(sources ${AML_UNPARSED_ARGUMENTS}) | ||||
|  | ||||
|   add_library(${name}) | ||||
|   set_target_properties(${name} PROPERTIES LINKER_LANGUAGE CXX) | ||||
|  | ||||
|   if (NOT ${${AML_IF}}) | ||||
|     # Create a non-modular library. | ||||
|     target_sources(${name} PRIVATE ${AML_FALLBACK}) | ||||
|     return() | ||||
|   endif () | ||||
|  | ||||
|   # Modules require C++20. | ||||
|   target_compile_features(${name} PUBLIC cxx_std_20) | ||||
|   if (CMAKE_COMPILER_IS_GNUCXX) | ||||
|     target_compile_options(${name} PUBLIC -fmodules-ts) | ||||
|   endif () | ||||
|  | ||||
|   # `std` is affected by CMake options and may be higher than C++20. | ||||
|   get_target_property(std ${name} CXX_STANDARD) | ||||
|  | ||||
|   if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") | ||||
|     set(pcms) | ||||
|     foreach (src ${sources}) | ||||
|       get_filename_component(pcm ${src} NAME_WE) | ||||
|       set(pcm ${pcm}.pcm) | ||||
|  | ||||
|       # Propagate -fmodule-file=*.pcm to targets that link with this library. | ||||
|       target_compile_options( | ||||
|         ${name} PUBLIC -fmodule-file=${CMAKE_CURRENT_BINARY_DIR}/${pcm}) | ||||
|  | ||||
|       # Use an absolute path to prevent target_link_libraries prepending -l | ||||
|       # to it. | ||||
|       set(pcms ${pcms} ${CMAKE_CURRENT_BINARY_DIR}/${pcm}) | ||||
|       add_custom_command( | ||||
|         OUTPUT ${pcm} | ||||
|         COMMAND ${CMAKE_CXX_COMPILER} | ||||
|                 -std=c++${std} -x c++-module --precompile -c | ||||
|                 -o ${pcm} ${CMAKE_CURRENT_SOURCE_DIR}/${src} | ||||
|                 "-I$<JOIN:$<TARGET_PROPERTY:${name},INCLUDE_DIRECTORIES>,;-I>" | ||||
|         # Required by the -I generator expression above. | ||||
|         COMMAND_EXPAND_LISTS | ||||
|         DEPENDS ${src}) | ||||
|     endforeach () | ||||
|  | ||||
|     # Add .pcm files as sources to make sure they are built before the library. | ||||
|     set(sources) | ||||
|     foreach (pcm ${pcms}) | ||||
|       get_filename_component(pcm_we ${pcm} NAME_WE) | ||||
|       set(obj ${pcm_we}.o) | ||||
|       # Use an absolute path to prevent target_link_libraries prepending -l. | ||||
|       set(sources ${sources} ${pcm} ${CMAKE_CURRENT_BINARY_DIR}/${obj}) | ||||
|       add_custom_command( | ||||
|         OUTPUT ${obj} | ||||
|         COMMAND ${CMAKE_CXX_COMPILER} $<TARGET_PROPERTY:${name},COMPILE_OPTIONS> | ||||
|                 -c -o ${obj} ${pcm} | ||||
|         DEPENDS ${pcm}) | ||||
|     endforeach () | ||||
|   endif () | ||||
|   target_sources(${name} PRIVATE ${sources}) | ||||
| endfunction() | ||||
|  | ||||
| include(CMakeParseArguments) | ||||
|  | ||||
| # Sets a cache variable with a docstring joined from multiple arguments: | ||||
| #   set(<variable> <value>... CACHE <type> <docstring>...) | ||||
| # This allows splitting a long docstring for readability. | ||||
| function(set_verbose) | ||||
|   # cmake_parse_arguments is broken in CMake 3.4 (cannot parse CACHE) so use | ||||
|   # list instead. | ||||
|   list(GET ARGN 0 var) | ||||
|   list(REMOVE_AT ARGN 0) | ||||
|   list(GET ARGN 0 val) | ||||
|   list(REMOVE_AT ARGN 0) | ||||
|   list(REMOVE_AT ARGN 0) | ||||
|   list(GET ARGN 0 type) | ||||
|   list(REMOVE_AT ARGN 0) | ||||
|   join(doc ${ARGN}) | ||||
|   set(${var} ${val} CACHE ${type} ${doc}) | ||||
| endfunction() | ||||
|  | ||||
| # Set the default CMAKE_BUILD_TYPE to Release. | ||||
| # This should be done before the project command since the latter can set | ||||
| # CMAKE_BUILD_TYPE itself (it does so for nmake). | ||||
| if (FMT_MASTER_PROJECT AND NOT CMAKE_BUILD_TYPE) | ||||
|   set_verbose(CMAKE_BUILD_TYPE Release CACHE STRING | ||||
|               "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or " | ||||
|               "CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.") | ||||
| endif () | ||||
|  | ||||
| project(FMT CXX) | ||||
| include(GNUInstallDirs) | ||||
| set_verbose(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE STRING | ||||
|             "Installation directory for include files, a relative path that " | ||||
|             "will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute path.") | ||||
|  | ||||
| option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF) | ||||
| option(FMT_WERROR "Halt the compilation with an error on compiler warnings." | ||||
|        OFF) | ||||
|  | ||||
| # Options that control generation of various targets. | ||||
| option(FMT_DOC "Generate the doc target." ${FMT_MASTER_PROJECT}) | ||||
| option(FMT_INSTALL "Generate the install target." ON) | ||||
| option(FMT_TEST "Generate the test target." ${FMT_MASTER_PROJECT}) | ||||
| option(FMT_FUZZ "Generate the fuzz target." OFF) | ||||
| option(FMT_CUDA_TEST "Generate the cuda-test target." OFF) | ||||
| option(FMT_OS "Include core requiring OS (Windows/Posix) " ON) | ||||
| option(FMT_MODULE "Build a module instead of a traditional library." OFF) | ||||
| option(FMT_SYSTEM_HEADERS "Expose headers with marking them as system." OFF) | ||||
|  | ||||
| if (FMT_TEST AND FMT_MODULE) | ||||
|   # The tests require {fmt} to be compiled as traditional library | ||||
|   message(STATUS "Testing is incompatible with build mode 'module'.") | ||||
| endif () | ||||
| set(FMT_SYSTEM_HEADERS_ATTRIBUTE "") | ||||
| if (FMT_SYSTEM_HEADERS) | ||||
|   set(FMT_SYSTEM_HEADERS_ATTRIBUTE SYSTEM) | ||||
| endif () | ||||
| if (CMAKE_SYSTEM_NAME STREQUAL "MSDOS") | ||||
|   set(FMT_TEST OFF) | ||||
|   message(STATUS "MSDOS is incompatible with gtest") | ||||
| endif () | ||||
|  | ||||
| # Get version from core.h | ||||
| file(READ include/fmt/core.h core_h) | ||||
| if (NOT core_h MATCHES "FMT_VERSION ([0-9]+)([0-9][0-9])([0-9][0-9])") | ||||
|   message(FATAL_ERROR "Cannot get FMT_VERSION from core.h.") | ||||
| endif () | ||||
| # Use math to skip leading zeros if any. | ||||
| math(EXPR CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1}) | ||||
| math(EXPR CPACK_PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2}) | ||||
| math(EXPR CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3}) | ||||
| join(FMT_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}. | ||||
|                  ${CPACK_PACKAGE_VERSION_PATCH}) | ||||
| message(STATUS "Version: ${FMT_VERSION}") | ||||
|  | ||||
| message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") | ||||
|  | ||||
| if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) | ||||
|   set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) | ||||
| endif () | ||||
|  | ||||
| set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} | ||||
|   "${CMAKE_CURRENT_SOURCE_DIR}/support/cmake") | ||||
|  | ||||
| include(CheckCXXCompilerFlag) | ||||
| include(JoinPaths) | ||||
|  | ||||
| if (FMT_MASTER_PROJECT AND NOT DEFINED CMAKE_CXX_VISIBILITY_PRESET) | ||||
|   set_verbose(CMAKE_CXX_VISIBILITY_PRESET hidden CACHE STRING | ||||
|               "Preset for the export of private symbols") | ||||
|   set_property(CACHE CMAKE_CXX_VISIBILITY_PRESET PROPERTY STRINGS | ||||
|                hidden default) | ||||
| endif () | ||||
|  | ||||
| if (FMT_MASTER_PROJECT AND NOT DEFINED CMAKE_VISIBILITY_INLINES_HIDDEN) | ||||
|   set_verbose(CMAKE_VISIBILITY_INLINES_HIDDEN ON CACHE BOOL | ||||
|               "Whether to add a compile flag to hide symbols of inline functions") | ||||
| endif () | ||||
|  | ||||
| if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") | ||||
|   set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic | ||||
|       -Wold-style-cast -Wundef | ||||
|       -Wredundant-decls -Wwrite-strings -Wpointer-arith | ||||
|       -Wcast-qual -Wformat=2 -Wmissing-include-dirs | ||||
|       -Wcast-align | ||||
|       -Wctor-dtor-privacy -Wdisabled-optimization | ||||
|       -Winvalid-pch -Woverloaded-virtual | ||||
|       -Wconversion -Wundef | ||||
|       -Wno-ctor-dtor-privacy -Wno-format-nonliteral) | ||||
|   if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6) | ||||
|       set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} | ||||
|          -Wno-dangling-else -Wno-unused-local-typedefs) | ||||
|   endif () | ||||
|   if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) | ||||
|       set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wdouble-promotion | ||||
|           -Wtrampolines -Wzero-as-null-pointer-constant -Wuseless-cast | ||||
|           -Wvector-operation-performance -Wsized-deallocation -Wshadow) | ||||
|   endif () | ||||
|   if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0) | ||||
|       set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wshift-overflow=2 | ||||
|           -Wnull-dereference -Wduplicated-cond) | ||||
|   endif () | ||||
|   set(WERROR_FLAG -Werror) | ||||
| endif () | ||||
|  | ||||
| if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") | ||||
|   set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion -Wundef | ||||
|       -Wdeprecated -Wweak-vtables -Wshadow | ||||
|       -Wno-gnu-zero-variadic-macro-arguments) | ||||
|   check_cxx_compiler_flag(-Wzero-as-null-pointer-constant HAS_NULLPTR_WARNING) | ||||
|   if (HAS_NULLPTR_WARNING) | ||||
|     set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} | ||||
|         -Wzero-as-null-pointer-constant) | ||||
|   endif () | ||||
|   set(WERROR_FLAG -Werror) | ||||
| endif () | ||||
|  | ||||
| if (MSVC) | ||||
|   set(PEDANTIC_COMPILE_FLAGS /W3) | ||||
|   set(WERROR_FLAG /WX) | ||||
| endif () | ||||
|  | ||||
| if (FMT_MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio") | ||||
|   # If Microsoft SDK is installed create script run-msbuild.bat that | ||||
|   # calls SetEnv.cmd to set up build environment and runs msbuild. | ||||
|   # It is useful when building Visual Studio projects with the SDK | ||||
|   # toolchain rather than Visual Studio. | ||||
|   include(FindSetEnv) | ||||
|   if (WINSDK_SETENV) | ||||
|     set(MSBUILD_SETUP "call \"${WINSDK_SETENV}\"") | ||||
|   endif () | ||||
|   # Set FrameworkPathOverride to get rid of MSB3644 warnings. | ||||
|   join(netfxpath | ||||
|        "C:\\Program Files\\Reference Assemblies\\Microsoft\\Framework\\" | ||||
|        ".NETFramework\\v4.0") | ||||
|   file(WRITE run-msbuild.bat " | ||||
|     ${MSBUILD_SETUP} | ||||
|     ${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*") | ||||
| endif () | ||||
|  | ||||
| function(add_headers VAR) | ||||
|   set(headers ${${VAR}}) | ||||
|   foreach (header ${ARGN}) | ||||
|     set(headers ${headers} include/fmt/${header}) | ||||
|   endforeach() | ||||
|   set(${VAR} ${headers} PARENT_SCOPE) | ||||
| endfunction() | ||||
|  | ||||
| # Define the fmt library, its includes and the needed defines. | ||||
| add_headers(FMT_HEADERS args.h chrono.h color.h compile.h core.h format.h | ||||
|                         format-inl.h os.h ostream.h printf.h ranges.h std.h | ||||
|                         xchar.h) | ||||
| set(FMT_SOURCES src/format.cc) | ||||
| if (FMT_OS) | ||||
|   set(FMT_SOURCES ${FMT_SOURCES} src/os.cc) | ||||
| endif () | ||||
|  | ||||
| add_module_library(fmt src/fmt.cc FALLBACK | ||||
|                    ${FMT_SOURCES} ${FMT_HEADERS} README.md ChangeLog.md | ||||
|                    IF FMT_MODULE) | ||||
| add_library(fmt::fmt ALIAS fmt) | ||||
| if (FMT_MODULE) | ||||
|   enable_module(fmt) | ||||
| endif () | ||||
|  | ||||
| if (FMT_WERROR) | ||||
|   target_compile_options(fmt PRIVATE ${WERROR_FLAG}) | ||||
| endif () | ||||
| if (FMT_PEDANTIC) | ||||
|   target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS}) | ||||
| endif () | ||||
|  | ||||
| if (cxx_std_11 IN_LIST CMAKE_CXX_COMPILE_FEATURES) | ||||
|   target_compile_features(fmt PUBLIC cxx_std_11) | ||||
| else () | ||||
|   message(WARNING "Feature cxx_std_11 is unknown for the CXX compiler") | ||||
| endif () | ||||
|  | ||||
| target_include_directories(fmt ${FMT_SYSTEM_HEADERS_ATTRIBUTE} PUBLIC | ||||
|   $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> | ||||
|   $<INSTALL_INTERFACE:${FMT_INC_DIR}>) | ||||
|  | ||||
| set(FMT_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.") | ||||
|  | ||||
| set_target_properties(fmt PROPERTIES | ||||
|   VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR} | ||||
|   PUBLIC_HEADER "${FMT_HEADERS}" | ||||
|   DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}" | ||||
|  | ||||
|   # Workaround for Visual Studio 2017: | ||||
|   # Ensure the .pdb is created with the same name and in the same directory | ||||
|   # as the .lib. Newer VS versions already do this by default, but there is no | ||||
|   # harm in setting it for those too. Ignored by other generators. | ||||
|   COMPILE_PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" | ||||
|   COMPILE_PDB_NAME "fmt" | ||||
|   COMPILE_PDB_NAME_DEBUG "fmt${FMT_DEBUG_POSTFIX}") | ||||
|  | ||||
| # Set FMT_LIB_NAME for pkg-config fmt.pc. We cannot use the OUTPUT_NAME target | ||||
| # property because it's not set by default. | ||||
| set(FMT_LIB_NAME fmt) | ||||
| if (CMAKE_BUILD_TYPE STREQUAL "Debug") | ||||
|   set(FMT_LIB_NAME ${FMT_LIB_NAME}${FMT_DEBUG_POSTFIX}) | ||||
| endif () | ||||
|  | ||||
| if (BUILD_SHARED_LIBS) | ||||
|   target_compile_definitions(fmt PRIVATE FMT_LIB_EXPORT INTERFACE FMT_SHARED) | ||||
| endif () | ||||
| if (FMT_SAFE_DURATION_CAST) | ||||
|   target_compile_definitions(fmt PUBLIC FMT_SAFE_DURATION_CAST) | ||||
| endif () | ||||
|  | ||||
| add_library(fmt-header-only INTERFACE) | ||||
| add_library(fmt::fmt-header-only ALIAS fmt-header-only) | ||||
|  | ||||
| target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1) | ||||
| target_compile_features(fmt-header-only INTERFACE cxx_std_11) | ||||
|  | ||||
| target_include_directories(fmt-header-only | ||||
|   ${FMT_SYSTEM_HEADERS_ATTRIBUTE} INTERFACE | ||||
|   $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> | ||||
|   $<INSTALL_INTERFACE:${FMT_INC_DIR}>) | ||||
|  | ||||
| # Install targets. | ||||
| if (FMT_INSTALL) | ||||
|   include(CMakePackageConfigHelpers) | ||||
|   set_verbose(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING | ||||
|               "Installation directory for cmake files, a relative path that " | ||||
|               "will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute " | ||||
|               "path.") | ||||
|   set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake) | ||||
|   set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake) | ||||
|   set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc) | ||||
|   set(targets_export_name fmt-targets) | ||||
|  | ||||
|   set_verbose(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING | ||||
|               "Installation directory for libraries, a relative path that " | ||||
|               "will be joined to ${CMAKE_INSTALL_PREFIX} or an absolute path.") | ||||
|  | ||||
|   set_verbose(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE STRING | ||||
|               "Installation directory for pkgconfig (.pc) files, a relative " | ||||
|               "path that will be joined with ${CMAKE_INSTALL_PREFIX} or an " | ||||
|               "absolute path.") | ||||
|  | ||||
|   # Generate the version, config and target files into the build directory. | ||||
|   write_basic_package_version_file( | ||||
|     ${version_config} | ||||
|     VERSION ${FMT_VERSION} | ||||
|     COMPATIBILITY AnyNewerVersion) | ||||
|  | ||||
|   join_paths(libdir_for_pc_file "\${exec_prefix}" "${FMT_LIB_DIR}") | ||||
|   join_paths(includedir_for_pc_file "\${prefix}" "${FMT_INC_DIR}") | ||||
|  | ||||
|   configure_file( | ||||
|     "${PROJECT_SOURCE_DIR}/support/cmake/fmt.pc.in" | ||||
|     "${pkgconfig}" | ||||
|     @ONLY) | ||||
|   configure_package_config_file( | ||||
|     ${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in | ||||
|     ${project_config} | ||||
|     INSTALL_DESTINATION ${FMT_CMAKE_DIR}) | ||||
|  | ||||
|   set(INSTALL_TARGETS fmt fmt-header-only) | ||||
|  | ||||
|   # Install the library and headers. | ||||
|   install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name} | ||||
|           LIBRARY DESTINATION ${FMT_LIB_DIR} | ||||
|           ARCHIVE DESTINATION ${FMT_LIB_DIR} | ||||
|           PUBLIC_HEADER DESTINATION "${FMT_INC_DIR}/fmt" | ||||
|           RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) | ||||
|  | ||||
|   # Use a namespace because CMake provides better diagnostics for namespaced | ||||
|   # imported targets. | ||||
|   export(TARGETS ${INSTALL_TARGETS} NAMESPACE fmt:: | ||||
|          FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake) | ||||
|  | ||||
|   # Install version, config and target files. | ||||
|   install( | ||||
|     FILES ${project_config} ${version_config} | ||||
|     DESTINATION ${FMT_CMAKE_DIR}) | ||||
|   install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR} | ||||
|           NAMESPACE fmt::) | ||||
|  | ||||
|   install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}") | ||||
| endif () | ||||
|  | ||||
| if (FMT_DOC) | ||||
|   add_subdirectory(doc) | ||||
| endif () | ||||
|  | ||||
| if (FMT_TEST) | ||||
|   enable_testing() | ||||
|   add_subdirectory(test) | ||||
| endif () | ||||
|  | ||||
| # Control fuzzing independent of the unit tests. | ||||
| if (FMT_FUZZ) | ||||
|   add_subdirectory(test/fuzzing) | ||||
|  | ||||
|   # The FMT_FUZZ macro is used to prevent resource exhaustion in fuzzing | ||||
|   # mode and make fuzzing practically possible. It is similar to | ||||
|   # FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION but uses a different name to | ||||
|   # avoid interfering with fuzzing of projects that use {fmt}. | ||||
|   # See also https://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode. | ||||
|   target_compile_definitions(fmt PUBLIC FMT_FUZZ) | ||||
| endif () | ||||
|  | ||||
| set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore) | ||||
| if (FMT_MASTER_PROJECT AND EXISTS ${gitignore}) | ||||
|   # Get the list of ignored files from .gitignore. | ||||
|   file (STRINGS ${gitignore} lines) | ||||
|   list(REMOVE_ITEM lines /doc/html) | ||||
|   foreach (line ${lines}) | ||||
|     string(REPLACE "." "[.]" line "${line}") | ||||
|     string(REPLACE "*" ".*" line "${line}") | ||||
|     set(ignored_files ${ignored_files} "${line}$" "${line}/") | ||||
|   endforeach () | ||||
|   set(ignored_files ${ignored_files} | ||||
|     /.git /breathe /format-benchmark sphinx/ .buildinfo .doctrees) | ||||
|  | ||||
|   set(CPACK_SOURCE_GENERATOR ZIP) | ||||
|   set(CPACK_SOURCE_IGNORE_FILES ${ignored_files}) | ||||
|   set(CPACK_SOURCE_PACKAGE_FILE_NAME fmt-${FMT_VERSION}) | ||||
|   set(CPACK_PACKAGE_NAME fmt) | ||||
|   set(CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR}/README.md) | ||||
|   include(CPack) | ||||
| endif () | ||||
							
								
								
									
										7849
									
								
								dep/fmt/ChangeLog.md
									
									
									
									
									
								
							
							
						
						
									
										7849
									
								
								dep/fmt/ChangeLog.md
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -20,16 +20,16 @@ that help victims of the war in Ukraine: <https://www.stopputin.net/>. | ||||
| Q&A: ask questions on [StackOverflow with the tag | ||||
| fmt](https://stackoverflow.com/questions/tagged/fmt). | ||||
|  | ||||
| Try {fmt} in [Compiler Explorer](https://godbolt.org/z/Eq5763). | ||||
| Try {fmt} in [Compiler Explorer](https://godbolt.org/z/8Mx1EW73v). | ||||
|  | ||||
| # Features | ||||
|  | ||||
| - Simple [format API](https://fmt.dev/latest/api.html) with positional | ||||
| - Simple [format API](https://fmt.dev/latest/api/) with positional | ||||
|   arguments for localization | ||||
| - Implementation of [C++20 | ||||
|   std::format](https://en.cppreference.com/w/cpp/utility/format) and | ||||
|   [C++23 std::print](https://en.cppreference.com/w/cpp/io/print) | ||||
| - [Format string syntax](https://fmt.dev/latest/syntax.html) similar | ||||
| - [Format string syntax](https://fmt.dev/latest/syntax/) similar | ||||
|   to Python\'s | ||||
|   [format](https://docs.python.org/3/library/stdtypes.html#str.format) | ||||
| - Fast IEEE 754 floating-point formatter with correct rounding, | ||||
| @@ -37,10 +37,10 @@ Try {fmt} in [Compiler Explorer](https://godbolt.org/z/Eq5763). | ||||
|   [Dragonbox](https://github.com/jk-jeon/dragonbox) algorithm | ||||
| - Portable Unicode support | ||||
| - Safe [printf | ||||
|   implementation](https://fmt.dev/latest/api.html#printf-formatting) | ||||
|   implementation](https://fmt.dev/latest/api/#printf-formatting) | ||||
|   including the POSIX extension for positional arguments | ||||
| - Extensibility: [support for user-defined | ||||
|   types](https://fmt.dev/latest/api.html#formatting-user-defined-types) | ||||
|   types](https://fmt.dev/latest/api/#formatting-user-defined-types) | ||||
| - High performance: faster than common standard library | ||||
|   implementations of `(s)printf`, iostreams, `to_string` and | ||||
|   `to_chars`, see [Speed tests](#speed-tests) and [Converting a | ||||
| @@ -58,8 +58,8 @@ Try {fmt} in [Compiler Explorer](https://godbolt.org/z/Eq5763). | ||||
|   buffer overflow errors | ||||
| - Ease of use: small self-contained code base, no external | ||||
|   dependencies, permissive MIT | ||||
|   [license](https://github.com/fmtlib/fmt/blob/master/LICENSE.rst) | ||||
| - [Portability](https://fmt.dev/latest/index.html#portability) with | ||||
|   [license](https://github.com/fmtlib/fmt/blob/master/LICENSE) | ||||
| - [Portability](https://fmt.dev/latest/#portability) with | ||||
|   consistent output across platforms and support for older compilers | ||||
| - Clean warning-free codebase even on high warning levels such as | ||||
|   `-Wall -Wextra -pedantic` | ||||
| @@ -203,43 +203,38 @@ and [ryu](https://github.com/ulfjack/ryu): | ||||
|  | ||||
| ## Compile time and code bloat | ||||
|  | ||||
| The script | ||||
| [bloat-test.py](https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py) | ||||
| from [format-benchmark](https://github.com/fmtlib/format-benchmark) | ||||
| tests compile time and code bloat for nontrivial projects. It generates | ||||
| 100 translation units and uses `printf()` or its alternative five times | ||||
| in each to simulate a medium-sized project. The resulting executable | ||||
| size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42), macOS | ||||
| Sierra, best of three) is shown in the following tables. | ||||
| The script [bloat-test.py][test] from [format-benchmark][bench] tests compile | ||||
| time and code bloat for nontrivial projects. It generates 100 translation units | ||||
| and uses `printf()` or its alternative five times in each to simulate a | ||||
| medium-sized project. The resulting executable size and compile time (Apple | ||||
| clang version 15.0.0 (clang-1500.1.0.2.5), macOS Sonoma, best of three) is shown | ||||
| in the following tables. | ||||
|  | ||||
| [test]: https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py | ||||
| [bench]: https://github.com/fmtlib/format-benchmark | ||||
|  | ||||
| **Optimized build (-O3)** | ||||
|  | ||||
| | Method        | Compile Time, s | Executable size, KiB | Stripped size, KiB | | ||||
| |---------------|-----------------|----------------------|--------------------| | ||||
| | printf        |   2.6           |   29                 |   26               | | ||||
| | printf+string |   16.4          |   29                 |   26               | | ||||
| | iostreams     |   31.1          |   59                 |   55               | | ||||
| | {fmt}         |   19.0          |   37                 |   34               | | ||||
| | Boost Format  |   91.9          |   226                |   203              | | ||||
| | Folly Format  |   115.7         |   101                |   88               | | ||||
| | printf        |             1.6 |                   54 |                 50 | | ||||
| | IOStreams     |            25.9 |                   98 |                 84 | | ||||
| | fmt 83652df   |             4.8 |                   54 |                 50 | | ||||
| | tinyformat    |            29.1 |                  161 |                136 | | ||||
| | Boost Format  |            55.0 |                  530 |                317 | | ||||
|  | ||||
| As you can see, {fmt} has 60% less overhead in terms of resulting binary | ||||
| code size compared to iostreams and comes pretty close to `printf`. | ||||
| Boost Format and Folly Format have the largest overheads. | ||||
|  | ||||
| `printf+string` is the same as `printf` but with an extra `<string>` | ||||
| include to measure the overhead of the latter. | ||||
| {fmt} is fast to compile and is comparable to `printf` in terms of per-call | ||||
| binary size (within a rounding error on this system). | ||||
|  | ||||
| **Non-optimized build** | ||||
|  | ||||
| | Method        | Compile Time, s | Executable size, KiB | Stripped size, KiB | | ||||
| |---------------|-----------------|----------------------|--------------------| | ||||
| | printf        |   2.2           |   33                 |   30               | | ||||
| | printf+string |   16.0          |   33                 |   30               | | ||||
| | iostreams     |   28.3          |   56                 |   52               | | ||||
| | {fmt}         |   18.2          |   59                 |   50               | | ||||
| | Boost Format  |   54.1          |   365                |   303              | | ||||
| | Folly Format  |   79.9          |   445                |   430              | | ||||
| | printf        |             1.4 |                   54 |                 50 | | ||||
| | IOStreams     |            23.4 |                   92 |                 68 | | ||||
| | {fmt} 83652df |             4.4 |                   89 |                 85 | | ||||
| | tinyformat    |            24.5 |                  204 |                161 | | ||||
| | Boost Format  |            36.4 |                  831 |                462 | | ||||
|  | ||||
| `libc`, `lib(std)c++`, and `libfmt` are all linked as shared libraries | ||||
| to compare formatting function overhead only. Boost Format is a | ||||
| @@ -248,7 +243,7 @@ header-only library so it doesn\'t provide any linkage options. | ||||
| ## Running the tests | ||||
|  | ||||
| Please refer to [Building the | ||||
| library](https://fmt.dev/latest/usage.html#building-the-library) for | ||||
| library](https://fmt.dev/latest/get-started/#building-from-source) for | ||||
| instructions on how to build the library and run the unit tests. | ||||
|  | ||||
| Benchmarks reside in a separate repository, | ||||
| @@ -270,8 +265,7 @@ or the bloat test: | ||||
|  | ||||
| # Migrating code | ||||
|  | ||||
| [clang-tidy](https://clang.llvm.org/extra/clang-tidy/) v17 (not yet | ||||
| released) provides the | ||||
| [clang-tidy](https://clang.llvm.org/extra/clang-tidy/) v18 provides the | ||||
| [modernize-use-std-print](https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-std-print.html) | ||||
| check that is capable of converting occurrences of `printf` and | ||||
| `fprintf` to `fmt::print` if configured to do so. (By default it | ||||
| @@ -297,13 +291,14 @@ converts to `std::print`.) | ||||
| - [ccache](https://ccache.dev/): a compiler cache | ||||
| - [ClickHouse](https://github.com/ClickHouse/ClickHouse): an | ||||
|   analytical database management system | ||||
| - [ContextVision](https://www.contextvision.com/): medical imaging software | ||||
| - [Contour](https://github.com/contour-terminal/contour/): a modern | ||||
|   terminal emulator | ||||
| - [CUAUV](https://cuauv.org/): Cornell University\'s autonomous | ||||
|   underwater vehicle | ||||
| - [Drake](https://drake.mit.edu/): a planning, control, and analysis | ||||
|   toolbox for nonlinear dynamical systems (MIT) | ||||
| - [Envoy](https://lyft.github.io/envoy/): C++ L7 proxy and | ||||
| - [Envoy](https://github.com/envoyproxy/envoy): C++ L7 proxy and | ||||
|   communication bus (Lyft) | ||||
| - [FiveM](https://fivem.net/): a modification framework for GTA V | ||||
| - [fmtlog](https://github.com/MengRao/fmtlog): a performant | ||||
| @@ -343,7 +338,7 @@ converts to `std::print`.) | ||||
| - [Quill](https://github.com/odygrd/quill): asynchronous low-latency | ||||
|   logging library | ||||
| - [QKW](https://github.com/ravijanjam/qkw): generalizing aliasing to | ||||
|   simplify navigation, and executing complex multi-line terminal | ||||
|   simplify navigation, and execute complex multi-line terminal | ||||
|   command sequences | ||||
| - [redis-cerberus](https://github.com/HunanTV/redis-cerberus): a Redis | ||||
|   cluster proxy | ||||
| @@ -432,7 +427,7 @@ code bloat issues (see [Benchmarks](#benchmarks)). | ||||
|  | ||||
| ## FastFormat | ||||
|  | ||||
| This is an interesting library that is fast, safe, and has positional | ||||
| This is an interesting library that is fast, safe and has positional | ||||
| arguments. However, it has significant limitations, citing its author: | ||||
|  | ||||
| > Three features that have no hope of being accommodated within the | ||||
| @@ -442,8 +437,8 @@ arguments. However, it has significant limitations, citing its author: | ||||
| > - Octal/hexadecimal encoding | ||||
| > - Runtime width/alignment specification | ||||
|  | ||||
| It is also quite big and has a heavy dependency, STLSoft, which might be | ||||
| too restrictive for using it in some projects. | ||||
| It is also quite big and has a heavy dependency, on STLSoft, which might be | ||||
| too restrictive for use in some projects. | ||||
|  | ||||
| ## Boost Spirit.Karma | ||||
|  | ||||
| @@ -462,7 +457,7 @@ second](http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html). | ||||
|  | ||||
| # Documentation License | ||||
|  | ||||
| The [Format String Syntax](https://fmt.dev/latest/syntax.html) section | ||||
| The [Format String Syntax](https://fmt.dev/latest/syntax/) section | ||||
| in the documentation is based on the one from Python [string module | ||||
| documentation](https://docs.python.org/3/library/string.html#module-string). | ||||
| For this reason, the documentation is distributed under the Python | ||||
| @@ -486,5 +481,5 @@ To report a security issue, please disclose it at [security | ||||
| advisory](https://github.com/fmtlib/fmt/security/advisories/new). | ||||
|  | ||||
| This project is maintained by a team of volunteers on a | ||||
| reasonable-effort basis. As such, please give us at least 90 days to | ||||
| reasonable-effort basis. As such, please give us at least *90* days to | ||||
| work on a fix before public exposure. | ||||
|   | ||||
| @@ -1,2 +1,2 @@ | ||||
| This is a pruned version of fmt 10.2.1, obtained from | ||||
| https://github.com/fmtlib/fmt/releases/tag/10.2.1. | ||||
| This is a pruned version of fmt 11.1.4, obtained from | ||||
| https://github.com/fmtlib/fmt/releases/tag/11.1.4. | ||||
| @@ -1,4 +1,4 @@ | ||||
| from build.c import cxxlibrary, HostToolchain | ||||
| from build.c import cxxlibrary | ||||
|  | ||||
| cxxlibrary( | ||||
|     name="fmt", | ||||
| @@ -9,10 +9,18 @@ cxxlibrary( | ||||
|     cflags=["-Idep/fmt/include"], | ||||
|     hdrs={ | ||||
|         "fmt/args.h": "./include/fmt/args.h", | ||||
|         "fmt/base.h": "./include/fmt/base.h", | ||||
|         "fmt/chrono.h": "./include/fmt/chrono.h", | ||||
|         "fmt/color.h": "./include/fmt/color.h", | ||||
|         "fmt/compile.h": "./include/fmt/compile.h", | ||||
|         "fmt/core.h": "./include/fmt/core.h", | ||||
|         "fmt/format.h": "./include/fmt/format.h", | ||||
|         "fmt/format-inl.h": "./include/fmt/format-inl.h", | ||||
|         "fmt/os.h": "./include/fmt/os.h", | ||||
|         "fmt/ostream.h": "./include/fmt/ostream.h", | ||||
|         "fmt/printf.h": "./include/fmt/printf.h", | ||||
|         "fmt/ranges.h": "./include/fmt/ranges.h", | ||||
|         "fmt/std.h": "./include/fmt/std.h", | ||||
|         "fmt/xchar.h": "./include/fmt/xchar.h", | ||||
|     }, | ||||
| ) | ||||
|   | ||||
| @@ -8,14 +8,15 @@ | ||||
| #ifndef FMT_ARGS_H_ | ||||
| #define FMT_ARGS_H_ | ||||
|  | ||||
| #include <functional>  // std::reference_wrapper | ||||
| #include <memory>      // std::unique_ptr | ||||
| #include <vector> | ||||
| #ifndef FMT_MODULE | ||||
| #  include <functional>  // std::reference_wrapper | ||||
| #  include <memory>      // std::unique_ptr | ||||
| #  include <vector> | ||||
| #endif | ||||
|  | ||||
| #include "core.h" | ||||
| #include "format.h"  // std_string_view | ||||
|  | ||||
| FMT_BEGIN_NAMESPACE | ||||
|  | ||||
| namespace detail { | ||||
|  | ||||
| template <typename T> struct is_reference_wrapper : std::false_type {}; | ||||
| @@ -28,15 +29,18 @@ auto unwrap(const std::reference_wrapper<T>& v) -> const T& { | ||||
|   return static_cast<const T&>(v); | ||||
| } | ||||
|  | ||||
| class dynamic_arg_list { | ||||
|   // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for | ||||
|   // templates it doesn't complain about inability to deduce single translation | ||||
|   // unit for placing vtable. So storage_node_base is made a fake template. | ||||
|   template <typename = void> struct node { | ||||
|     virtual ~node() = default; | ||||
|     std::unique_ptr<node<>> next; | ||||
|   }; | ||||
| // node is defined outside dynamic_arg_list to workaround a C2504 bug in MSVC | ||||
| // 2022 (v17.10.0). | ||||
| // | ||||
| // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for | ||||
| // templates it doesn't complain about inability to deduce single translation | ||||
| // unit for placing vtable. So node is made a fake template. | ||||
| template <typename = void> struct node { | ||||
|   virtual ~node() = default; | ||||
|   std::unique_ptr<node<>> next; | ||||
| }; | ||||
|  | ||||
| class dynamic_arg_list { | ||||
|   template <typename T> struct typed_node : node<> { | ||||
|     T value; | ||||
|  | ||||
| @@ -62,28 +66,18 @@ class dynamic_arg_list { | ||||
| }  // namespace detail | ||||
|  | ||||
| /** | ||||
|   \rst | ||||
|   A dynamic version of `fmt::format_arg_store`. | ||||
|   It's equipped with a storage to potentially temporary objects which lifetimes | ||||
|   could be shorter than the format arguments object. | ||||
|  | ||||
|   It can be implicitly converted into `~fmt::basic_format_args` for passing | ||||
|   into type-erased formatting functions such as `~fmt::vformat`. | ||||
|   \endrst | ||||
|  * A dynamic list of formatting arguments with storage. | ||||
|  * | ||||
|  * It can be implicitly converted into `fmt::basic_format_args` for passing | ||||
|  * into type-erased formatting functions such as `fmt::vformat`. | ||||
|  */ | ||||
| template <typename Context> | ||||
| class dynamic_format_arg_store | ||||
| #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 | ||||
|     // Workaround a GCC template argument substitution bug. | ||||
|     : public basic_format_args<Context> | ||||
| #endif | ||||
| { | ||||
| template <typename Context> class dynamic_format_arg_store { | ||||
|  private: | ||||
|   using char_type = typename Context::char_type; | ||||
|  | ||||
|   template <typename T> struct need_copy { | ||||
|     static constexpr detail::type mapped_type = | ||||
|         detail::mapped_type_constant<T, Context>::value; | ||||
|         detail::mapped_type_constant<T, char_type>::value; | ||||
|  | ||||
|     enum { | ||||
|       value = !(detail::is_reference_wrapper<T>::value || | ||||
| @@ -96,7 +90,7 @@ class dynamic_format_arg_store | ||||
|   }; | ||||
|  | ||||
|   template <typename T> | ||||
|   using stored_type = conditional_t< | ||||
|   using stored_t = conditional_t< | ||||
|       std::is_convertible<T, std::basic_string<char_type>>::value && | ||||
|           !detail::is_reference_wrapper<T>::value, | ||||
|       std::basic_string<char_type>, T>; | ||||
| @@ -111,80 +105,72 @@ class dynamic_format_arg_store | ||||
|  | ||||
|   friend class basic_format_args<Context>; | ||||
|  | ||||
|   auto get_types() const -> unsigned long long { | ||||
|     return detail::is_unpacked_bit | data_.size() | | ||||
|            (named_info_.empty() | ||||
|                 ? 0ULL | ||||
|                 : static_cast<unsigned long long>(detail::has_named_args_bit)); | ||||
|   } | ||||
|  | ||||
|   auto data() const -> const basic_format_arg<Context>* { | ||||
|     return named_info_.empty() ? data_.data() : data_.data() + 1; | ||||
|   } | ||||
|  | ||||
|   template <typename T> void emplace_arg(const T& arg) { | ||||
|     data_.emplace_back(detail::make_arg<Context>(arg)); | ||||
|     data_.emplace_back(arg); | ||||
|   } | ||||
|  | ||||
|   template <typename T> | ||||
|   void emplace_arg(const detail::named_arg<char_type, T>& arg) { | ||||
|     if (named_info_.empty()) { | ||||
|       constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr}; | ||||
|       data_.insert(data_.begin(), {zero_ptr, 0}); | ||||
|     } | ||||
|     data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value))); | ||||
|     if (named_info_.empty()) | ||||
|       data_.insert(data_.begin(), basic_format_arg<Context>(nullptr, 0)); | ||||
|     data_.emplace_back(detail::unwrap(arg.value)); | ||||
|     auto pop_one = [](std::vector<basic_format_arg<Context>>* data) { | ||||
|       data->pop_back(); | ||||
|     }; | ||||
|     std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)> | ||||
|         guard{&data_, pop_one}; | ||||
|     named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)}); | ||||
|     data_[0].value_.named_args = {named_info_.data(), named_info_.size()}; | ||||
|     data_[0] = {named_info_.data(), named_info_.size()}; | ||||
|     guard.release(); | ||||
|   } | ||||
|  | ||||
|  public: | ||||
|   constexpr dynamic_format_arg_store() = default; | ||||
|  | ||||
|   operator basic_format_args<Context>() const { | ||||
|     return basic_format_args<Context>(data(), static_cast<int>(data_.size()), | ||||
|                                       !named_info_.empty()); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|     \rst | ||||
|     Adds an argument into the dynamic store for later passing to a formatting | ||||
|     function. | ||||
|  | ||||
|     Note that custom types and string types (but not string views) are copied | ||||
|     into the store dynamically allocating memory if necessary. | ||||
|  | ||||
|     **Example**:: | ||||
|  | ||||
|       fmt::dynamic_format_arg_store<fmt::format_context> store; | ||||
|       store.push_back(42); | ||||
|       store.push_back("abc"); | ||||
|       store.push_back(1.5f); | ||||
|       std::string result = fmt::vformat("{} and {} and {}", store); | ||||
|     \endrst | ||||
|   */ | ||||
|    * Adds an argument into the dynamic store for later passing to a formatting | ||||
|    * function. | ||||
|    * | ||||
|    * Note that custom types and string types (but not string views) are copied | ||||
|    * into the store dynamically allocating memory if necessary. | ||||
|    * | ||||
|    * **Example**: | ||||
|    * | ||||
|    *     fmt::dynamic_format_arg_store<fmt::format_context> store; | ||||
|    *     store.push_back(42); | ||||
|    *     store.push_back("abc"); | ||||
|    *     store.push_back(1.5f); | ||||
|    *     std::string result = fmt::vformat("{} and {} and {}", store); | ||||
|    */ | ||||
|   template <typename T> void push_back(const T& arg) { | ||||
|     if (detail::const_check(need_copy<T>::value)) | ||||
|       emplace_arg(dynamic_args_.push<stored_type<T>>(arg)); | ||||
|       emplace_arg(dynamic_args_.push<stored_t<T>>(arg)); | ||||
|     else | ||||
|       emplace_arg(detail::unwrap(arg)); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|     \rst | ||||
|     Adds a reference to the argument into the dynamic store for later passing to | ||||
|     a formatting function. | ||||
|  | ||||
|     **Example**:: | ||||
|  | ||||
|       fmt::dynamic_format_arg_store<fmt::format_context> store; | ||||
|       char band[] = "Rolling Stones"; | ||||
|       store.push_back(std::cref(band)); | ||||
|       band[9] = 'c'; // Changing str affects the output. | ||||
|       std::string result = fmt::vformat("{}", store); | ||||
|       // result == "Rolling Scones" | ||||
|     \endrst | ||||
|   */ | ||||
|    * Adds a reference to the argument into the dynamic store for later passing | ||||
|    * to a formatting function. | ||||
|    * | ||||
|    * **Example**: | ||||
|    * | ||||
|    *     fmt::dynamic_format_arg_store<fmt::format_context> store; | ||||
|    *     char band[] = "Rolling Stones"; | ||||
|    *     store.push_back(std::cref(band)); | ||||
|    *     band[9] = 'c'; // Changing str affects the output. | ||||
|    *     std::string result = fmt::vformat("{}", store); | ||||
|    *     // result == "Rolling Scones" | ||||
|    */ | ||||
|   template <typename T> void push_back(std::reference_wrapper<T> arg) { | ||||
|     static_assert( | ||||
|         need_copy<T>::value, | ||||
| @@ -193,41 +179,40 @@ class dynamic_format_arg_store | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|     Adds named argument into the dynamic store for later passing to a formatting | ||||
|     function. ``std::reference_wrapper`` is supported to avoid copying of the | ||||
|     argument. The name is always copied into the store. | ||||
|   */ | ||||
|    * Adds named argument into the dynamic store for later passing to a | ||||
|    * formatting function. `std::reference_wrapper` is supported to avoid | ||||
|    * copying of the argument. The name is always copied into the store. | ||||
|    */ | ||||
|   template <typename T> | ||||
|   void push_back(const detail::named_arg<char_type, T>& arg) { | ||||
|     const char_type* arg_name = | ||||
|         dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str(); | ||||
|     if (detail::const_check(need_copy<T>::value)) { | ||||
|       emplace_arg( | ||||
|           fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value))); | ||||
|           fmt::arg(arg_name, dynamic_args_.push<stored_t<T>>(arg.value))); | ||||
|     } else { | ||||
|       emplace_arg(fmt::arg(arg_name, arg.value)); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** Erase all elements from the store */ | ||||
|   /// Erase all elements from the store. | ||||
|   void clear() { | ||||
|     data_.clear(); | ||||
|     named_info_.clear(); | ||||
|     dynamic_args_ = detail::dynamic_arg_list(); | ||||
|     dynamic_args_ = {}; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|     \rst | ||||
|     Reserves space to store at least *new_cap* arguments including | ||||
|     *new_cap_named* named arguments. | ||||
|     \endrst | ||||
|   */ | ||||
|   /// Reserves space to store at least `new_cap` arguments including | ||||
|   /// `new_cap_named` named arguments. | ||||
|   void reserve(size_t new_cap, size_t new_cap_named) { | ||||
|     FMT_ASSERT(new_cap >= new_cap_named, | ||||
|                "Set of arguments includes set of named arguments"); | ||||
|                "set of arguments includes set of named arguments"); | ||||
|     data_.reserve(new_cap); | ||||
|     named_info_.reserve(new_cap_named); | ||||
|   } | ||||
|  | ||||
|   /// Returns the number of elements in the store. | ||||
|   size_t size() const noexcept { return data_.size(); } | ||||
| }; | ||||
|  | ||||
| FMT_END_NAMESPACE | ||||
|   | ||||
							
								
								
									
										2962
									
								
								dep/fmt/include/fmt/base.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2962
									
								
								dep/fmt/include/fmt/base.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -227,7 +227,7 @@ struct color_type { | ||||
| }; | ||||
| }  // namespace detail | ||||
|  | ||||
| /** A text style consisting of foreground and background colors and emphasis. */ | ||||
| /// A text style consisting of foreground and background colors and emphasis. | ||||
| class text_style { | ||||
|  public: | ||||
|   FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept | ||||
| @@ -239,7 +239,7 @@ class text_style { | ||||
|       foreground_color = rhs.foreground_color; | ||||
|     } else if (rhs.set_foreground_color) { | ||||
|       if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) | ||||
|         FMT_THROW(format_error("can't OR a terminal color")); | ||||
|         report_error("can't OR a terminal color"); | ||||
|       foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color; | ||||
|     } | ||||
|  | ||||
| @@ -248,7 +248,7 @@ class text_style { | ||||
|       background_color = rhs.background_color; | ||||
|     } else if (rhs.set_background_color) { | ||||
|       if (!background_color.is_rgb || !rhs.background_color.is_rgb) | ||||
|         FMT_THROW(format_error("can't OR a terminal color")); | ||||
|         report_error("can't OR a terminal color"); | ||||
|       background_color.value.rgb_color |= rhs.background_color.value.rgb_color; | ||||
|     } | ||||
|  | ||||
| @@ -310,13 +310,13 @@ class text_style { | ||||
|   emphasis ems; | ||||
| }; | ||||
|  | ||||
| /** Creates a text style from the foreground (text) color. */ | ||||
| /// Creates a text style from the foreground (text) color. | ||||
| FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept | ||||
|     -> text_style { | ||||
|   return text_style(true, foreground); | ||||
| } | ||||
|  | ||||
| /** Creates a text style from the background color. */ | ||||
| /// Creates a text style from the background color. | ||||
| FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept | ||||
|     -> text_style { | ||||
|   return text_style(false, background); | ||||
| @@ -330,7 +330,7 @@ FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept | ||||
| namespace detail { | ||||
|  | ||||
| template <typename Char> struct ansi_color_escape { | ||||
|   FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, | ||||
|   FMT_CONSTEXPR ansi_color_escape(color_type text_color, | ||||
|                                   const char* esc) noexcept { | ||||
|     // If we have a terminal color, we need to output another escape code | ||||
|     // sequence. | ||||
| @@ -390,8 +390,8 @@ template <typename Char> struct ansi_color_escape { | ||||
|   FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; } | ||||
|  | ||||
|   FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; } | ||||
|   FMT_CONSTEXPR_CHAR_TRAITS auto end() const noexcept -> const Char* { | ||||
|     return buffer + std::char_traits<Char>::length(buffer); | ||||
|   FMT_CONSTEXPR20 auto end() const noexcept -> const Char* { | ||||
|     return buffer + basic_string_view<Char>(buffer).size(); | ||||
|   } | ||||
|  | ||||
|  private: | ||||
| @@ -412,13 +412,13 @@ template <typename Char> struct ansi_color_escape { | ||||
| }; | ||||
|  | ||||
| template <typename Char> | ||||
| FMT_CONSTEXPR auto make_foreground_color(detail::color_type foreground) noexcept | ||||
| FMT_CONSTEXPR auto make_foreground_color(color_type foreground) noexcept | ||||
|     -> ansi_color_escape<Char> { | ||||
|   return ansi_color_escape<Char>(foreground, "\x1b[38;2;"); | ||||
| } | ||||
|  | ||||
| template <typename Char> | ||||
| FMT_CONSTEXPR auto make_background_color(detail::color_type background) noexcept | ||||
| FMT_CONSTEXPR auto make_background_color(color_type background) noexcept | ||||
|     -> ansi_color_escape<Char> { | ||||
|   return ansi_color_escape<Char>(background, "\x1b[48;2;"); | ||||
| } | ||||
| @@ -434,7 +434,7 @@ template <typename Char> inline void reset_color(buffer<Char>& buffer) { | ||||
|   buffer.append(reset_color.begin(), reset_color.end()); | ||||
| } | ||||
|  | ||||
| template <typename T> struct styled_arg : detail::view { | ||||
| template <typename T> struct styled_arg : view { | ||||
|   const T& value; | ||||
|   text_style style; | ||||
|   styled_arg(const T& v, text_style s) : value(v), style(s) {} | ||||
| @@ -442,144 +442,115 @@ template <typename T> struct styled_arg : detail::view { | ||||
|  | ||||
| template <typename Char> | ||||
| void vformat_to(buffer<Char>& buf, const text_style& ts, | ||||
|                 basic_string_view<Char> format_str, | ||||
|                 basic_format_args<buffer_context<type_identity_t<Char>>> args) { | ||||
|                 basic_string_view<Char> fmt, | ||||
|                 basic_format_args<buffered_context<Char>> args) { | ||||
|   bool has_style = false; | ||||
|   if (ts.has_emphasis()) { | ||||
|     has_style = true; | ||||
|     auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis()); | ||||
|     auto emphasis = make_emphasis<Char>(ts.get_emphasis()); | ||||
|     buf.append(emphasis.begin(), emphasis.end()); | ||||
|   } | ||||
|   if (ts.has_foreground()) { | ||||
|     has_style = true; | ||||
|     auto foreground = detail::make_foreground_color<Char>(ts.get_foreground()); | ||||
|     auto foreground = make_foreground_color<Char>(ts.get_foreground()); | ||||
|     buf.append(foreground.begin(), foreground.end()); | ||||
|   } | ||||
|   if (ts.has_background()) { | ||||
|     has_style = true; | ||||
|     auto background = detail::make_background_color<Char>(ts.get_background()); | ||||
|     auto background = make_background_color<Char>(ts.get_background()); | ||||
|     buf.append(background.begin(), background.end()); | ||||
|   } | ||||
|   detail::vformat_to(buf, format_str, args, {}); | ||||
|   if (has_style) detail::reset_color<Char>(buf); | ||||
|   vformat_to(buf, fmt, args); | ||||
|   if (has_style) reset_color<Char>(buf); | ||||
| } | ||||
|  | ||||
| }  // namespace detail | ||||
|  | ||||
| inline void vprint(std::FILE* f, const text_style& ts, string_view fmt, | ||||
| inline void vprint(FILE* f, const text_style& ts, string_view fmt, | ||||
|                    format_args args) { | ||||
|   // Legacy wide streams are not supported. | ||||
|   auto buf = memory_buffer(); | ||||
|   detail::vformat_to(buf, ts, fmt, args); | ||||
|   if (detail::is_utf8()) { | ||||
|     detail::print(f, string_view(buf.begin(), buf.size())); | ||||
|     return; | ||||
|   } | ||||
|   buf.push_back('\0'); | ||||
|   int result = std::fputs(buf.data(), f); | ||||
|   if (result < 0) | ||||
|     FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); | ||||
|   print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size())); | ||||
| } | ||||
|  | ||||
| /** | ||||
|   \rst | ||||
|   Formats a string and prints it to the specified file stream using ANSI | ||||
|   escape sequences to specify text formatting. | ||||
|  | ||||
|   **Example**:: | ||||
|  | ||||
|     fmt::print(fmt::emphasis::bold | fg(fmt::color::red), | ||||
|                "Elapsed time: {0:.2f} seconds", 1.23); | ||||
|   \endrst | ||||
|  * Formats a string and prints it to the specified file stream using ANSI | ||||
|  * escape sequences to specify text formatting. | ||||
|  * | ||||
|  * **Example**: | ||||
|  * | ||||
|  *     fmt::print(fmt::emphasis::bold | fg(fmt::color::red), | ||||
|  *                "Elapsed time: {0:.2f} seconds", 1.23); | ||||
|  */ | ||||
| template <typename S, typename... Args, | ||||
|           FMT_ENABLE_IF(detail::is_string<S>::value)> | ||||
| void print(std::FILE* f, const text_style& ts, const S& format_str, | ||||
|            const Args&... args) { | ||||
|   vprint(f, ts, format_str, | ||||
|          fmt::make_format_args<buffer_context<char_t<S>>>(args...)); | ||||
| template <typename... T> | ||||
| void print(FILE* f, const text_style& ts, format_string<T...> fmt, | ||||
|            T&&... args) { | ||||
|   vprint(f, ts, fmt.str, vargs<T...>{{args...}}); | ||||
| } | ||||
|  | ||||
| /** | ||||
|   \rst | ||||
|   Formats a string and prints it to stdout using ANSI escape sequences to | ||||
|   specify text formatting. | ||||
|  | ||||
|   **Example**:: | ||||
|  | ||||
|     fmt::print(fmt::emphasis::bold | fg(fmt::color::red), | ||||
|                "Elapsed time: {0:.2f} seconds", 1.23); | ||||
|   \endrst | ||||
|  * Formats a string and prints it to stdout using ANSI escape sequences to | ||||
|  * specify text formatting. | ||||
|  * | ||||
|  * **Example**: | ||||
|  * | ||||
|  *     fmt::print(fmt::emphasis::bold | fg(fmt::color::red), | ||||
|  *                "Elapsed time: {0:.2f} seconds", 1.23); | ||||
|  */ | ||||
| template <typename S, typename... Args, | ||||
|           FMT_ENABLE_IF(detail::is_string<S>::value)> | ||||
| void print(const text_style& ts, const S& format_str, const Args&... args) { | ||||
|   return print(stdout, ts, format_str, args...); | ||||
| template <typename... T> | ||||
| void print(const text_style& ts, format_string<T...> fmt, T&&... args) { | ||||
|   return print(stdout, ts, fmt, std::forward<T>(args)...); | ||||
| } | ||||
|  | ||||
| template <typename S, typename Char = char_t<S>> | ||||
| inline auto vformat( | ||||
|     const text_style& ts, const S& format_str, | ||||
|     basic_format_args<buffer_context<type_identity_t<Char>>> args) | ||||
|     -> std::basic_string<Char> { | ||||
|   basic_memory_buffer<Char> buf; | ||||
|   detail::vformat_to(buf, ts, detail::to_string_view(format_str), args); | ||||
| inline auto vformat(const text_style& ts, string_view fmt, format_args args) | ||||
|     -> std::string { | ||||
|   auto buf = memory_buffer(); | ||||
|   detail::vformat_to(buf, ts, fmt, args); | ||||
|   return fmt::to_string(buf); | ||||
| } | ||||
|  | ||||
| /** | ||||
|   \rst | ||||
|   Formats arguments and returns the result as a string using ANSI | ||||
|   escape sequences to specify text formatting. | ||||
|  | ||||
|   **Example**:: | ||||
|  | ||||
|     #include <fmt/color.h> | ||||
|     std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), | ||||
|                                       "The answer is {}", 42); | ||||
|   \endrst | ||||
| */ | ||||
| template <typename S, typename... Args, typename Char = char_t<S>> | ||||
| inline auto format(const text_style& ts, const S& format_str, | ||||
|                    const Args&... args) -> std::basic_string<Char> { | ||||
|   return fmt::vformat(ts, detail::to_string_view(format_str), | ||||
|                       fmt::make_format_args<buffer_context<Char>>(args...)); | ||||
|  * Formats arguments and returns the result as a string using ANSI escape | ||||
|  * sequences to specify text formatting. | ||||
|  * | ||||
|  * **Example**: | ||||
|  * | ||||
|  * ``` | ||||
|  * #include <fmt/color.h> | ||||
|  * std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), | ||||
|  *                                   "The answer is {}", 42); | ||||
|  * ``` | ||||
|  */ | ||||
| template <typename... T> | ||||
| inline auto format(const text_style& ts, format_string<T...> fmt, T&&... args) | ||||
|     -> std::string { | ||||
|   return fmt::vformat(ts, fmt.str, vargs<T...>{{args...}}); | ||||
| } | ||||
|  | ||||
| /** | ||||
|   Formats a string with the given text_style and writes the output to ``out``. | ||||
|  */ | ||||
| template <typename OutputIt, typename Char, | ||||
|           FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)> | ||||
| auto vformat_to(OutputIt out, const text_style& ts, | ||||
|                 basic_string_view<Char> format_str, | ||||
|                 basic_format_args<buffer_context<type_identity_t<Char>>> args) | ||||
|     -> OutputIt { | ||||
|   auto&& buf = detail::get_buffer<Char>(out); | ||||
|   detail::vformat_to(buf, ts, format_str, args); | ||||
| /// Formats a string with the given text_style and writes the output to `out`. | ||||
| template <typename OutputIt, | ||||
|           FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)> | ||||
| auto vformat_to(OutputIt out, const text_style& ts, string_view fmt, | ||||
|                 format_args args) -> OutputIt { | ||||
|   auto&& buf = detail::get_buffer<char>(out); | ||||
|   detail::vformat_to(buf, ts, fmt, args); | ||||
|   return detail::get_iterator(buf, out); | ||||
| } | ||||
|  | ||||
| /** | ||||
|   \rst | ||||
|   Formats arguments with the given text_style, writes the result to the output | ||||
|   iterator ``out`` and returns the iterator past the end of the output range. | ||||
|  | ||||
|   **Example**:: | ||||
|  | ||||
|     std::vector<char> out; | ||||
|     fmt::format_to(std::back_inserter(out), | ||||
|                    fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); | ||||
|   \endrst | ||||
| */ | ||||
| template <typename OutputIt, typename S, typename... Args, | ||||
|           bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&& | ||||
|               detail::is_string<S>::value> | ||||
| inline auto format_to(OutputIt out, const text_style& ts, const S& format_str, | ||||
|                       Args&&... args) -> | ||||
|     typename std::enable_if<enable, OutputIt>::type { | ||||
|   return vformat_to(out, ts, detail::to_string_view(format_str), | ||||
|                     fmt::make_format_args<buffer_context<char_t<S>>>(args...)); | ||||
|  * Formats arguments with the given text style, writes the result to the output | ||||
|  * iterator `out` and returns the iterator past the end of the output range. | ||||
|  * | ||||
|  * **Example**: | ||||
|  * | ||||
|  *     std::vector<char> out; | ||||
|  *     fmt::format_to(std::back_inserter(out), | ||||
|  *                    fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); | ||||
|  */ | ||||
| template <typename OutputIt, typename... T, | ||||
|           FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)> | ||||
| inline auto format_to(OutputIt out, const text_style& ts, | ||||
|                       format_string<T...> fmt, T&&... args) -> OutputIt { | ||||
|   return vformat_to(out, ts, fmt.str, vargs<T...>{{args...}}); | ||||
| } | ||||
|  | ||||
| template <typename T, typename Char> | ||||
| @@ -588,47 +559,44 @@ struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> { | ||||
|   auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const | ||||
|       -> decltype(ctx.out()) { | ||||
|     const auto& ts = arg.style; | ||||
|     const auto& value = arg.value; | ||||
|     auto out = ctx.out(); | ||||
|  | ||||
|     bool has_style = false; | ||||
|     if (ts.has_emphasis()) { | ||||
|       has_style = true; | ||||
|       auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis()); | ||||
|       out = std::copy(emphasis.begin(), emphasis.end(), out); | ||||
|       out = detail::copy<Char>(emphasis.begin(), emphasis.end(), out); | ||||
|     } | ||||
|     if (ts.has_foreground()) { | ||||
|       has_style = true; | ||||
|       auto foreground = | ||||
|           detail::make_foreground_color<Char>(ts.get_foreground()); | ||||
|       out = std::copy(foreground.begin(), foreground.end(), out); | ||||
|       out = detail::copy<Char>(foreground.begin(), foreground.end(), out); | ||||
|     } | ||||
|     if (ts.has_background()) { | ||||
|       has_style = true; | ||||
|       auto background = | ||||
|           detail::make_background_color<Char>(ts.get_background()); | ||||
|       out = std::copy(background.begin(), background.end(), out); | ||||
|       out = detail::copy<Char>(background.begin(), background.end(), out); | ||||
|     } | ||||
|     out = formatter<T, Char>::format(value, ctx); | ||||
|     out = formatter<T, Char>::format(arg.value, ctx); | ||||
|     if (has_style) { | ||||
|       auto reset_color = string_view("\x1b[0m"); | ||||
|       out = std::copy(reset_color.begin(), reset_color.end(), out); | ||||
|       out = detail::copy<Char>(reset_color.begin(), reset_color.end(), out); | ||||
|     } | ||||
|     return out; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| /** | ||||
|   \rst | ||||
|   Returns an argument that will be formatted using ANSI escape sequences, | ||||
|   to be used in a formatting function. | ||||
|  | ||||
|   **Example**:: | ||||
|  | ||||
|     fmt::print("Elapsed time: {0:.2f} seconds", | ||||
|                fmt::styled(1.23, fmt::fg(fmt::color::green) | | ||||
|                                  fmt::bg(fmt::color::blue))); | ||||
|   \endrst | ||||
|  * Returns an argument that will be formatted using ANSI escape sequences, | ||||
|  * to be used in a formatting function. | ||||
|  * | ||||
|  * **Example**: | ||||
|  * | ||||
|  *     fmt::print("Elapsed time: {0:.2f} seconds", | ||||
|  *                fmt::styled(1.23, fmt::fg(fmt::color::green) | | ||||
|  *                                  fmt::bg(fmt::color::blue))); | ||||
|  */ | ||||
| template <typename T> | ||||
| FMT_CONSTEXPR auto styled(const T& value, text_style ts) | ||||
|   | ||||
| @@ -8,54 +8,39 @@ | ||||
| #ifndef FMT_COMPILE_H_ | ||||
| #define FMT_COMPILE_H_ | ||||
|  | ||||
| #ifndef FMT_MODULE | ||||
| #  include <iterator>  // std::back_inserter | ||||
| #endif | ||||
|  | ||||
| #include "format.h" | ||||
|  | ||||
| FMT_BEGIN_NAMESPACE | ||||
| namespace detail { | ||||
|  | ||||
| template <typename Char, typename InputIt> | ||||
| FMT_CONSTEXPR inline auto copy_str(InputIt begin, InputIt end, | ||||
|                                    counting_iterator it) -> counting_iterator { | ||||
|   return it + (end - begin); | ||||
| } | ||||
|  | ||||
| // A compile-time string which is compiled into fast formatting code. | ||||
| class compiled_string {}; | ||||
| FMT_EXPORT class compiled_string {}; | ||||
|  | ||||
| template <typename S> | ||||
| struct is_compiled_string : std::is_base_of<compiled_string, S> {}; | ||||
|  | ||||
| namespace detail { | ||||
|  | ||||
| /** | ||||
|   \rst | ||||
|   Converts a string literal *s* into a format string that will be parsed at | ||||
|   compile time and converted into efficient formatting code. Requires C++17 | ||||
|   ``constexpr if`` compiler support. | ||||
|  | ||||
|   **Example**:: | ||||
|  | ||||
|     // Converts 42 into std::string using the most efficient method and no | ||||
|     // runtime format string processing. | ||||
|     std::string s = fmt::format(FMT_COMPILE("{}"), 42); | ||||
|   \endrst | ||||
|  * Converts a string literal `s` into a format string that will be parsed at | ||||
|  * compile time and converted into efficient formatting code. Requires C++17 | ||||
|  * `constexpr if` compiler support. | ||||
|  * | ||||
|  * **Example**: | ||||
|  * | ||||
|  *     // Converts 42 into std::string using the most efficient method and no | ||||
|  *     // runtime format string processing. | ||||
|  *     std::string s = fmt::format(FMT_COMPILE("{}"), 42); | ||||
|  */ | ||||
| #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) | ||||
| #  define FMT_COMPILE(s) \ | ||||
|     FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit) | ||||
| #  define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string) | ||||
| #else | ||||
| #  define FMT_COMPILE(s) FMT_STRING(s) | ||||
| #endif | ||||
|  | ||||
| #if FMT_USE_NONTYPE_TEMPLATE_ARGS | ||||
| template <typename Char, size_t N, | ||||
|           fmt::detail_exported::fixed_string<Char, N> Str> | ||||
| struct udl_compiled_string : compiled_string { | ||||
|   using char_type = Char; | ||||
|   explicit constexpr operator basic_string_view<char_type>() const { | ||||
|     return {Str.data, N - 1}; | ||||
|   } | ||||
| }; | ||||
| #endif | ||||
|  | ||||
| template <typename T, typename... Tail> | ||||
| auto first(const T& value, const Tail&...) -> const T& { | ||||
|   return value; | ||||
| @@ -75,6 +60,29 @@ constexpr const auto& get([[maybe_unused]] const T& first, | ||||
|     return detail::get<N - 1>(rest...); | ||||
| } | ||||
|  | ||||
| #  if FMT_USE_NONTYPE_TEMPLATE_ARGS | ||||
| template <int N, typename T, typename... Args, typename Char> | ||||
| constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int { | ||||
|   if constexpr (is_static_named_arg<T>()) { | ||||
|     if (name == T::name) return N; | ||||
|   } | ||||
|   if constexpr (sizeof...(Args) > 0) | ||||
|     return get_arg_index_by_name<N + 1, Args...>(name); | ||||
|   (void)name;  // Workaround an MSVC bug about "unused" parameter. | ||||
|   return -1; | ||||
| } | ||||
| #  endif | ||||
|  | ||||
| template <typename... Args, typename Char> | ||||
| FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int { | ||||
| #  if FMT_USE_NONTYPE_TEMPLATE_ARGS | ||||
|   if constexpr (sizeof...(Args) > 0) | ||||
|     return get_arg_index_by_name<0, Args...>(name); | ||||
| #  endif | ||||
|   (void)name; | ||||
|   return -1; | ||||
| } | ||||
|  | ||||
| template <typename Char, typename... Args> | ||||
| constexpr int get_arg_index_by_name(basic_string_view<Char> name, | ||||
|                                     type_list<Args...>) { | ||||
| @@ -144,11 +152,12 @@ template <typename Char, typename T, int N> struct field { | ||||
|   template <typename OutputIt, typename... Args> | ||||
|   constexpr OutputIt format(OutputIt out, const Args&... args) const { | ||||
|     const T& arg = get_arg_checked<T, N>(args...); | ||||
|     if constexpr (std::is_convertible_v<T, basic_string_view<Char>>) { | ||||
|     if constexpr (std::is_convertible<T, basic_string_view<Char>>::value) { | ||||
|       auto s = basic_string_view<Char>(arg); | ||||
|       return copy_str<Char>(s.begin(), s.end(), out); | ||||
|       return copy<Char>(s.begin(), s.end(), out); | ||||
|     } else { | ||||
|       return write<Char>(out, arg); | ||||
|     } | ||||
|     return write<Char>(out, arg); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| @@ -236,13 +245,12 @@ constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) { | ||||
| } | ||||
|  | ||||
| template <typename Args, size_t POS, int ID, typename S> | ||||
| constexpr auto compile_format_string(S format_str); | ||||
| constexpr auto compile_format_string(S fmt); | ||||
|  | ||||
| template <typename Args, size_t POS, int ID, typename T, typename S> | ||||
| constexpr auto parse_tail(T head, S format_str) { | ||||
|   if constexpr (POS != | ||||
|                 basic_string_view<typename S::char_type>(format_str).size()) { | ||||
|     constexpr auto tail = compile_format_string<Args, POS, ID>(format_str); | ||||
| constexpr auto parse_tail(T head, S fmt) { | ||||
|   if constexpr (POS != basic_string_view<typename S::char_type>(fmt).size()) { | ||||
|     constexpr auto tail = compile_format_string<Args, POS, ID>(fmt); | ||||
|     if constexpr (std::is_same<remove_cvref_t<decltype(tail)>, | ||||
|                                unknown_format>()) | ||||
|       return tail; | ||||
| @@ -274,6 +282,7 @@ constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str, | ||||
| } | ||||
|  | ||||
| template <typename Char> struct arg_id_handler { | ||||
|   arg_id_kind kind; | ||||
|   arg_ref<Char> arg_id; | ||||
|  | ||||
|   constexpr int on_auto() { | ||||
| @@ -281,25 +290,28 @@ template <typename Char> struct arg_id_handler { | ||||
|     return 0; | ||||
|   } | ||||
|   constexpr int on_index(int id) { | ||||
|     kind = arg_id_kind::index; | ||||
|     arg_id = arg_ref<Char>(id); | ||||
|     return 0; | ||||
|   } | ||||
|   constexpr int on_name(basic_string_view<Char> id) { | ||||
|     kind = arg_id_kind::name; | ||||
|     arg_id = arg_ref<Char>(id); | ||||
|     return 0; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| template <typename Char> struct parse_arg_id_result { | ||||
|   arg_id_kind kind; | ||||
|   arg_ref<Char> arg_id; | ||||
|   const Char* arg_id_end; | ||||
| }; | ||||
|  | ||||
| template <int ID, typename Char> | ||||
| constexpr auto parse_arg_id(const Char* begin, const Char* end) { | ||||
|   auto handler = arg_id_handler<Char>{arg_ref<Char>{}}; | ||||
|   auto handler = arg_id_handler<Char>{arg_id_kind::none, arg_ref<Char>{}}; | ||||
|   auto arg_id_end = parse_arg_id(begin, end, handler); | ||||
|   return parse_arg_id_result<Char>{handler.arg_id, arg_id_end}; | ||||
|   return parse_arg_id_result<Char>{handler.kind, handler.arg_id, arg_id_end}; | ||||
| } | ||||
|  | ||||
| template <typename T, typename Enable = void> struct field_type { | ||||
| @@ -313,14 +325,13 @@ struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> { | ||||
|  | ||||
| template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID, | ||||
|           typename S> | ||||
| constexpr auto parse_replacement_field_then_tail(S format_str) { | ||||
| constexpr auto parse_replacement_field_then_tail(S fmt) { | ||||
|   using char_type = typename S::char_type; | ||||
|   constexpr auto str = basic_string_view<char_type>(format_str); | ||||
|   constexpr auto str = basic_string_view<char_type>(fmt); | ||||
|   constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type(); | ||||
|   if constexpr (c == '}') { | ||||
|     return parse_tail<Args, END_POS + 1, NEXT_ID>( | ||||
|         field<char_type, typename field_type<T>::type, ARG_INDEX>(), | ||||
|         format_str); | ||||
|         field<char_type, typename field_type<T>::type, ARG_INDEX>(), fmt); | ||||
|   } else if constexpr (c != ':') { | ||||
|     FMT_THROW(format_error("expected ':'")); | ||||
|   } else { | ||||
| @@ -333,7 +344,7 @@ constexpr auto parse_replacement_field_then_tail(S format_str) { | ||||
|       return parse_tail<Args, result.end + 1, result.next_arg_id>( | ||||
|           spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{ | ||||
|               result.fmt}, | ||||
|           format_str); | ||||
|           fmt); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -341,22 +352,21 @@ constexpr auto parse_replacement_field_then_tail(S format_str) { | ||||
| // Compiles a non-empty format string and returns the compiled representation | ||||
| // or unknown_format() on unrecognized input. | ||||
| template <typename Args, size_t POS, int ID, typename S> | ||||
| constexpr auto compile_format_string(S format_str) { | ||||
| constexpr auto compile_format_string(S fmt) { | ||||
|   using char_type = typename S::char_type; | ||||
|   constexpr auto str = basic_string_view<char_type>(format_str); | ||||
|   constexpr auto str = basic_string_view<char_type>(fmt); | ||||
|   if constexpr (str[POS] == '{') { | ||||
|     if constexpr (POS + 1 == str.size()) | ||||
|       FMT_THROW(format_error("unmatched '{' in format string")); | ||||
|     if constexpr (str[POS + 1] == '{') { | ||||
|       return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); | ||||
|       return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt); | ||||
|     } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') { | ||||
|       static_assert(ID != manual_indexing_id, | ||||
|                     "cannot switch from manual to automatic argument indexing"); | ||||
|       constexpr auto next_id = | ||||
|           ID != manual_indexing_id ? ID + 1 : manual_indexing_id; | ||||
|       return parse_replacement_field_then_tail<get_type<ID, Args>, Args, | ||||
|                                                POS + 1, ID, next_id>( | ||||
|           format_str); | ||||
|                                                POS + 1, ID, next_id>(fmt); | ||||
|     } else { | ||||
|       constexpr auto arg_id_result = | ||||
|           parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size()); | ||||
| @@ -364,28 +374,27 @@ constexpr auto compile_format_string(S format_str) { | ||||
|       constexpr char_type c = | ||||
|           arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type(); | ||||
|       static_assert(c == '}' || c == ':', "missing '}' in format string"); | ||||
|       if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) { | ||||
|       if constexpr (arg_id_result.kind == arg_id_kind::index) { | ||||
|         static_assert( | ||||
|             ID == manual_indexing_id || ID == 0, | ||||
|             "cannot switch from automatic to manual argument indexing"); | ||||
|         constexpr auto arg_index = arg_id_result.arg_id.val.index; | ||||
|         constexpr auto arg_index = arg_id_result.arg_id.index; | ||||
|         return parse_replacement_field_then_tail<get_type<arg_index, Args>, | ||||
|                                                  Args, arg_id_end_pos, | ||||
|                                                  arg_index, manual_indexing_id>( | ||||
|             format_str); | ||||
|       } else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) { | ||||
|             fmt); | ||||
|       } else if constexpr (arg_id_result.kind == arg_id_kind::name) { | ||||
|         constexpr auto arg_index = | ||||
|             get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{}); | ||||
|             get_arg_index_by_name(arg_id_result.arg_id.name, Args{}); | ||||
|         if constexpr (arg_index >= 0) { | ||||
|           constexpr auto next_id = | ||||
|               ID != manual_indexing_id ? ID + 1 : manual_indexing_id; | ||||
|           return parse_replacement_field_then_tail< | ||||
|               decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos, | ||||
|               arg_index, next_id>(format_str); | ||||
|               arg_index, next_id>(fmt); | ||||
|         } else if constexpr (c == '}') { | ||||
|           return parse_tail<Args, arg_id_end_pos + 1, ID>( | ||||
|               runtime_named_field<char_type>{arg_id_result.arg_id.val.name}, | ||||
|               format_str); | ||||
|               runtime_named_field<char_type>{arg_id_result.arg_id.name}, fmt); | ||||
|         } else if constexpr (c == ':') { | ||||
|           return unknown_format();  // no type info for specs parsing | ||||
|         } | ||||
| @@ -394,29 +403,26 @@ constexpr auto compile_format_string(S format_str) { | ||||
|   } else if constexpr (str[POS] == '}') { | ||||
|     if constexpr (POS + 1 == str.size()) | ||||
|       FMT_THROW(format_error("unmatched '}' in format string")); | ||||
|     return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); | ||||
|     return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt); | ||||
|   } else { | ||||
|     constexpr auto end = parse_text(str, POS + 1); | ||||
|     if constexpr (end - POS > 1) { | ||||
|       return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), | ||||
|                                        format_str); | ||||
|       return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), fmt); | ||||
|     } else { | ||||
|       return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]}, | ||||
|                                        format_str); | ||||
|       return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]}, fmt); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| template <typename... Args, typename S, | ||||
|           FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> | ||||
| constexpr auto compile(S format_str) { | ||||
|   constexpr auto str = basic_string_view<typename S::char_type>(format_str); | ||||
|           FMT_ENABLE_IF(is_compiled_string<S>::value)> | ||||
| constexpr auto compile(S fmt) { | ||||
|   constexpr auto str = basic_string_view<typename S::char_type>(fmt); | ||||
|   if constexpr (str.size() == 0) { | ||||
|     return detail::make_text(str, 0, 0); | ||||
|   } else { | ||||
|     constexpr auto result = | ||||
|         detail::compile_format_string<detail::type_list<Args...>, 0, 0>( | ||||
|             format_str); | ||||
|         detail::compile_format_string<detail::type_list<Args...>, 0, 0>(fmt); | ||||
|     return result; | ||||
|   } | ||||
| } | ||||
| @@ -445,7 +451,7 @@ constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf, | ||||
| } | ||||
|  | ||||
| template <typename S, typename... Args, | ||||
|           FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> | ||||
|           FMT_ENABLE_IF(is_compiled_string<S>::value)> | ||||
| FMT_INLINE std::basic_string<typename S::char_type> format(const S&, | ||||
|                                                            Args&&... args) { | ||||
|   if constexpr (std::is_same<typename S::char_type, char>::value) { | ||||
| @@ -472,7 +478,7 @@ FMT_INLINE std::basic_string<typename S::char_type> format(const S&, | ||||
| } | ||||
|  | ||||
| template <typename OutputIt, typename S, typename... Args, | ||||
|           FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> | ||||
|           FMT_ENABLE_IF(is_compiled_string<S>::value)> | ||||
| FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { | ||||
|   constexpr auto compiled = detail::compile<Args...>(S()); | ||||
|   if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>, | ||||
| @@ -487,44 +493,42 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { | ||||
| #endif | ||||
|  | ||||
| template <typename OutputIt, typename S, typename... Args, | ||||
|           FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> | ||||
| auto format_to_n(OutputIt out, size_t n, const S& format_str, Args&&... args) | ||||
|           FMT_ENABLE_IF(is_compiled_string<S>::value)> | ||||
| auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args) | ||||
|     -> format_to_n_result<OutputIt> { | ||||
|   using traits = detail::fixed_buffer_traits; | ||||
|   auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n); | ||||
|   fmt::format_to(std::back_inserter(buf), format_str, | ||||
|                  std::forward<Args>(args)...); | ||||
|   fmt::format_to(std::back_inserter(buf), fmt, std::forward<Args>(args)...); | ||||
|   return {buf.out(), buf.count()}; | ||||
| } | ||||
|  | ||||
| template <typename S, typename... Args, | ||||
|           FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> | ||||
| FMT_CONSTEXPR20 auto formatted_size(const S& format_str, const Args&... args) | ||||
|           FMT_ENABLE_IF(is_compiled_string<S>::value)> | ||||
| FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args) | ||||
|     -> size_t { | ||||
|   return fmt::format_to(detail::counting_iterator(), format_str, args...) | ||||
|       .count(); | ||||
|   auto buf = detail::counting_buffer<>(); | ||||
|   fmt::format_to(appender(buf), fmt, args...); | ||||
|   return buf.count(); | ||||
| } | ||||
|  | ||||
| template <typename S, typename... Args, | ||||
|           FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> | ||||
| void print(std::FILE* f, const S& format_str, const Args&... args) { | ||||
|   memory_buffer buffer; | ||||
|   fmt::format_to(std::back_inserter(buffer), format_str, args...); | ||||
|   detail::print(f, {buffer.data(), buffer.size()}); | ||||
|           FMT_ENABLE_IF(is_compiled_string<S>::value)> | ||||
| void print(std::FILE* f, const S& fmt, const Args&... args) { | ||||
|   auto buf = memory_buffer(); | ||||
|   fmt::format_to(appender(buf), fmt, args...); | ||||
|   detail::print(f, {buf.data(), buf.size()}); | ||||
| } | ||||
|  | ||||
| template <typename S, typename... Args, | ||||
|           FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> | ||||
| void print(const S& format_str, const Args&... args) { | ||||
|   print(stdout, format_str, args...); | ||||
|           FMT_ENABLE_IF(is_compiled_string<S>::value)> | ||||
| void print(const S& fmt, const Args&... args) { | ||||
|   print(stdout, fmt, args...); | ||||
| } | ||||
|  | ||||
| #if FMT_USE_NONTYPE_TEMPLATE_ARGS | ||||
| inline namespace literals { | ||||
| template <detail_exported::fixed_string Str> constexpr auto operator""_cf() { | ||||
|   using char_t = remove_cvref_t<decltype(Str.data[0])>; | ||||
|   return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t), | ||||
|                                      Str>(); | ||||
| template <detail::fixed_string Str> constexpr auto operator""_cf() { | ||||
|   return FMT_COMPILE(Str.data); | ||||
| } | ||||
| }  // namespace literals | ||||
| #endif | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -8,36 +8,36 @@ | ||||
| #ifndef FMT_FORMAT_INL_H_ | ||||
| #define FMT_FORMAT_INL_H_ | ||||
|  | ||||
| #include <algorithm> | ||||
| #include <cerrno>  // errno | ||||
| #include <climits> | ||||
| #include <cmath> | ||||
| #include <exception> | ||||
|  | ||||
| #ifndef FMT_STATIC_THOUSANDS_SEPARATOR | ||||
| #  include <locale> | ||||
| #ifndef FMT_MODULE | ||||
| #  include <algorithm> | ||||
| #  include <cerrno>  // errno | ||||
| #  include <climits> | ||||
| #  include <cmath> | ||||
| #  include <exception> | ||||
| #endif | ||||
|  | ||||
| #if defined(_WIN32) && !defined(FMT_WINDOWS_NO_WCHAR) | ||||
| #if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE) | ||||
| #  include <io.h>  // _isatty | ||||
| #endif | ||||
|  | ||||
| #include "format.h" | ||||
|  | ||||
| #if FMT_USE_LOCALE | ||||
| #  include <locale> | ||||
| #endif | ||||
|  | ||||
| #ifndef FMT_FUNC | ||||
| #  define FMT_FUNC | ||||
| #endif | ||||
|  | ||||
| FMT_BEGIN_NAMESPACE | ||||
| namespace detail { | ||||
|  | ||||
| FMT_FUNC void assert_fail(const char* file, int line, const char* message) { | ||||
|   // Use unchecked std::fprintf to avoid triggering another assertion when | ||||
|   // writing to stderr fails | ||||
|   std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message); | ||||
|   // Chosen instead of std::abort to satisfy Clang in CUDA mode during device | ||||
|   // code pass. | ||||
|   std::terminate(); | ||||
| } | ||||
|  | ||||
| FMT_FUNC void throw_format_error(const char* message) { | ||||
|   FMT_THROW(format_error(message)); | ||||
|   // writing to stderr fails. | ||||
|   fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message); | ||||
|   abort(); | ||||
| } | ||||
|  | ||||
| FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code, | ||||
| @@ -56,89 +56,105 @@ FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code, | ||||
|     ++error_code_size; | ||||
|   } | ||||
|   error_code_size += detail::to_unsigned(detail::count_digits(abs_value)); | ||||
|   auto it = buffer_appender<char>(out); | ||||
|   auto it = appender(out); | ||||
|   if (message.size() <= inline_buffer_size - error_code_size) | ||||
|     fmt::format_to(it, FMT_STRING("{}{}"), message, SEP); | ||||
|   fmt::format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code); | ||||
|   FMT_ASSERT(out.size() <= inline_buffer_size, ""); | ||||
| } | ||||
|  | ||||
| FMT_FUNC void report_error(format_func func, int error_code, | ||||
|                            const char* message) noexcept { | ||||
| FMT_FUNC void do_report_error(format_func func, int error_code, | ||||
|                               const char* message) noexcept { | ||||
|   memory_buffer full_message; | ||||
|   func(full_message, error_code, message); | ||||
|   // Don't use fwrite_fully because the latter may throw. | ||||
|   // Don't use fwrite_all because the latter may throw. | ||||
|   if (std::fwrite(full_message.data(), full_message.size(), 1, stderr) > 0) | ||||
|     std::fputc('\n', stderr); | ||||
| } | ||||
|  | ||||
| // A wrapper around fwrite that throws on error. | ||||
| inline void fwrite_fully(const void* ptr, size_t count, FILE* stream) { | ||||
| inline void fwrite_all(const void* ptr, size_t count, FILE* stream) { | ||||
|   size_t written = std::fwrite(ptr, 1, count, stream); | ||||
|   if (written < count) | ||||
|     FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); | ||||
| } | ||||
|  | ||||
| #ifndef FMT_STATIC_THOUSANDS_SEPARATOR | ||||
| #if FMT_USE_LOCALE | ||||
| using std::locale; | ||||
| using std::numpunct; | ||||
| using std::use_facet; | ||||
|  | ||||
| template <typename Locale> | ||||
| locale_ref::locale_ref(const Locale& loc) : locale_(&loc) { | ||||
|   static_assert(std::is_same<Locale, std::locale>::value, ""); | ||||
|   static_assert(std::is_same<Locale, locale>::value, ""); | ||||
| } | ||||
| #else | ||||
| struct locale {}; | ||||
| template <typename Char> struct numpunct { | ||||
|   auto grouping() const -> std::string { return "\03"; } | ||||
|   auto thousands_sep() const -> Char { return ','; } | ||||
|   auto decimal_point() const -> Char { return '.'; } | ||||
| }; | ||||
| template <typename Facet> Facet use_facet(locale) { return {}; } | ||||
| #endif  // FMT_USE_LOCALE | ||||
|  | ||||
| template <typename Locale> auto locale_ref::get() const -> Locale { | ||||
|   static_assert(std::is_same<Locale, std::locale>::value, ""); | ||||
|   return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale(); | ||||
|   static_assert(std::is_same<Locale, locale>::value, ""); | ||||
| #if FMT_USE_LOCALE | ||||
|   if (locale_) return *static_cast<const locale*>(locale_); | ||||
| #endif | ||||
|   return locale(); | ||||
| } | ||||
|  | ||||
| template <typename Char> | ||||
| FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> { | ||||
|   auto& facet = std::use_facet<std::numpunct<Char>>(loc.get<std::locale>()); | ||||
|   auto&& facet = use_facet<numpunct<Char>>(loc.get<locale>()); | ||||
|   auto grouping = facet.grouping(); | ||||
|   auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep(); | ||||
|   return {std::move(grouping), thousands_sep}; | ||||
| } | ||||
| template <typename Char> | ||||
| FMT_FUNC auto decimal_point_impl(locale_ref loc) -> Char { | ||||
|   return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>()) | ||||
|       .decimal_point(); | ||||
|   return use_facet<numpunct<Char>>(loc.get<locale>()).decimal_point(); | ||||
| } | ||||
| #else | ||||
| template <typename Char> | ||||
| FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result<Char> { | ||||
|   return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR}; | ||||
| } | ||||
| template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref) { | ||||
|   return '.'; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #if FMT_USE_LOCALE | ||||
| FMT_FUNC auto write_loc(appender out, loc_value value, | ||||
|                         const format_specs<>& specs, locale_ref loc) -> bool { | ||||
| #ifndef FMT_STATIC_THOUSANDS_SEPARATOR | ||||
|                         const format_specs& specs, locale_ref loc) -> bool { | ||||
|   auto locale = loc.get<std::locale>(); | ||||
|   // We cannot use the num_put<char> facet because it may produce output in | ||||
|   // a wrong encoding. | ||||
|   using facet = format_facet<std::locale>; | ||||
|   if (std::has_facet<facet>(locale)) | ||||
|     return std::use_facet<facet>(locale).put(out, value, specs); | ||||
|     return use_facet<facet>(locale).put(out, value, specs); | ||||
|   return facet(locale).put(out, value, specs); | ||||
| #endif | ||||
|   return false; | ||||
| } | ||||
| #endif | ||||
| }  // namespace detail | ||||
|  | ||||
| FMT_FUNC void report_error(const char* message) { | ||||
| #if FMT_USE_EXCEPTIONS | ||||
|   // Use FMT_THROW instead of throw to avoid bogus unreachable code warnings | ||||
|   // from MSVC. | ||||
|   FMT_THROW(format_error(message)); | ||||
| #else | ||||
|   fputs(message, stderr); | ||||
|   abort(); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| template <typename Locale> typename Locale::id format_facet<Locale>::id; | ||||
|  | ||||
| #ifndef FMT_STATIC_THOUSANDS_SEPARATOR | ||||
| template <typename Locale> format_facet<Locale>::format_facet(Locale& loc) { | ||||
|   auto& numpunct = std::use_facet<std::numpunct<char>>(loc); | ||||
|   grouping_ = numpunct.grouping(); | ||||
|   if (!grouping_.empty()) separator_ = std::string(1, numpunct.thousands_sep()); | ||||
|   auto& np = detail::use_facet<detail::numpunct<char>>(loc); | ||||
|   grouping_ = np.grouping(); | ||||
|   if (!grouping_.empty()) separator_ = std::string(1, np.thousands_sep()); | ||||
| } | ||||
|  | ||||
| #if FMT_USE_LOCALE | ||||
| template <> | ||||
| FMT_API FMT_FUNC auto format_facet<std::locale>::do_put( | ||||
|     appender out, loc_value val, const format_specs<>& specs) const -> bool { | ||||
|     appender out, loc_value val, const format_specs& specs) const -> bool { | ||||
|   return val.visit( | ||||
|       detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_}); | ||||
| } | ||||
| @@ -1411,7 +1427,7 @@ FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code, | ||||
|                                   const char* message) noexcept { | ||||
|   FMT_TRY { | ||||
|     auto ec = std::error_code(error_code, std::generic_category()); | ||||
|     write(std::back_inserter(out), std::system_error(ec, message).what()); | ||||
|     detail::write(appender(out), std::system_error(ec, message).what()); | ||||
|     return; | ||||
|   } | ||||
|   FMT_CATCH(...) {} | ||||
| @@ -1420,7 +1436,7 @@ FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code, | ||||
|  | ||||
| FMT_FUNC void report_system_error(int error_code, | ||||
|                                   const char* message) noexcept { | ||||
|   report_error(format_system_error, error_code, message); | ||||
|   do_report_error(format_system_error, error_code, message); | ||||
| } | ||||
|  | ||||
| FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string { | ||||
| @@ -1432,9 +1448,252 @@ FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string { | ||||
| } | ||||
|  | ||||
| namespace detail { | ||||
| #if !defined(_WIN32) || defined(FMT_WINDOWS_NO_WCHAR) | ||||
|  | ||||
| FMT_FUNC void vformat_to(buffer<char>& buf, string_view fmt, format_args args, | ||||
|                          locale_ref loc) { | ||||
|   auto out = appender(buf); | ||||
|   if (fmt.size() == 2 && equal2(fmt.data(), "{}")) | ||||
|     return args.get(0).visit(default_arg_formatter<char>{out}); | ||||
|   parse_format_string( | ||||
|       fmt, format_handler<char>{parse_context<char>(fmt), {out, args, loc}}); | ||||
| } | ||||
|  | ||||
| template <typename T> struct span { | ||||
|   T* data; | ||||
|   size_t size; | ||||
| }; | ||||
|  | ||||
| template <typename F> auto flockfile(F* f) -> decltype(_lock_file(f)) { | ||||
|   _lock_file(f); | ||||
| } | ||||
| template <typename F> auto funlockfile(F* f) -> decltype(_unlock_file(f)) { | ||||
|   _unlock_file(f); | ||||
| } | ||||
|  | ||||
| #ifndef getc_unlocked | ||||
| template <typename F> auto getc_unlocked(F* f) -> decltype(_fgetc_nolock(f)) { | ||||
|   return _fgetc_nolock(f); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| template <typename F = FILE, typename Enable = void> | ||||
| struct has_flockfile : std::false_type {}; | ||||
|  | ||||
| template <typename F> | ||||
| struct has_flockfile<F, void_t<decltype(flockfile(&std::declval<F&>()))>> | ||||
|     : std::true_type {}; | ||||
|  | ||||
| // A FILE wrapper. F is FILE defined as a template parameter to make system API | ||||
| // detection work. | ||||
| template <typename F> class file_base { | ||||
|  public: | ||||
|   F* file_; | ||||
|  | ||||
|  public: | ||||
|   file_base(F* file) : file_(file) {} | ||||
|   operator F*() const { return file_; } | ||||
|  | ||||
|   // Reads a code unit from the stream. | ||||
|   auto get() -> int { | ||||
|     int result = getc_unlocked(file_); | ||||
|     if (result == EOF && ferror(file_) != 0) | ||||
|       FMT_THROW(system_error(errno, FMT_STRING("getc failed"))); | ||||
|     return result; | ||||
|   } | ||||
|  | ||||
|   // Puts the code unit back into the stream buffer. | ||||
|   void unget(char c) { | ||||
|     if (ungetc(c, file_) == EOF) | ||||
|       FMT_THROW(system_error(errno, FMT_STRING("ungetc failed"))); | ||||
|   } | ||||
|  | ||||
|   void flush() { fflush(this->file_); } | ||||
| }; | ||||
|  | ||||
| // A FILE wrapper for glibc. | ||||
| template <typename F> class glibc_file : public file_base<F> { | ||||
|  private: | ||||
|   enum { | ||||
|     line_buffered = 0x200,  // _IO_LINE_BUF | ||||
|     unbuffered = 2          // _IO_UNBUFFERED | ||||
|   }; | ||||
|  | ||||
|  public: | ||||
|   using file_base<F>::file_base; | ||||
|  | ||||
|   auto is_buffered() const -> bool { | ||||
|     return (this->file_->_flags & unbuffered) == 0; | ||||
|   } | ||||
|  | ||||
|   void init_buffer() { | ||||
|     if (this->file_->_IO_write_ptr) return; | ||||
|     // Force buffer initialization by placing and removing a char in a buffer. | ||||
|     assume(this->file_->_IO_write_ptr >= this->file_->_IO_write_end); | ||||
|     putc_unlocked(0, this->file_); | ||||
|     --this->file_->_IO_write_ptr; | ||||
|   } | ||||
|  | ||||
|   // Returns the file's read buffer. | ||||
|   auto get_read_buffer() const -> span<const char> { | ||||
|     auto ptr = this->file_->_IO_read_ptr; | ||||
|     return {ptr, to_unsigned(this->file_->_IO_read_end - ptr)}; | ||||
|   } | ||||
|  | ||||
|   // Returns the file's write buffer. | ||||
|   auto get_write_buffer() const -> span<char> { | ||||
|     auto ptr = this->file_->_IO_write_ptr; | ||||
|     return {ptr, to_unsigned(this->file_->_IO_buf_end - ptr)}; | ||||
|   } | ||||
|  | ||||
|   void advance_write_buffer(size_t size) { this->file_->_IO_write_ptr += size; } | ||||
|  | ||||
|   bool needs_flush() const { | ||||
|     if ((this->file_->_flags & line_buffered) == 0) return false; | ||||
|     char* end = this->file_->_IO_write_end; | ||||
|     return memchr(end, '\n', to_unsigned(this->file_->_IO_write_ptr - end)); | ||||
|   } | ||||
|  | ||||
|   void flush() { fflush_unlocked(this->file_); } | ||||
| }; | ||||
|  | ||||
| // A FILE wrapper for Apple's libc. | ||||
| template <typename F> class apple_file : public file_base<F> { | ||||
|  private: | ||||
|   enum { | ||||
|     line_buffered = 1,  // __SNBF | ||||
|     unbuffered = 2      // __SLBF | ||||
|   }; | ||||
|  | ||||
|  public: | ||||
|   using file_base<F>::file_base; | ||||
|  | ||||
|   auto is_buffered() const -> bool { | ||||
|     return (this->file_->_flags & unbuffered) == 0; | ||||
|   } | ||||
|  | ||||
|   void init_buffer() { | ||||
|     if (this->file_->_p) return; | ||||
|     // Force buffer initialization by placing and removing a char in a buffer. | ||||
|     putc_unlocked(0, this->file_); | ||||
|     --this->file_->_p; | ||||
|     ++this->file_->_w; | ||||
|   } | ||||
|  | ||||
|   auto get_read_buffer() const -> span<const char> { | ||||
|     return {reinterpret_cast<char*>(this->file_->_p), | ||||
|             to_unsigned(this->file_->_r)}; | ||||
|   } | ||||
|  | ||||
|   auto get_write_buffer() const -> span<char> { | ||||
|     return {reinterpret_cast<char*>(this->file_->_p), | ||||
|             to_unsigned(this->file_->_bf._base + this->file_->_bf._size - | ||||
|                         this->file_->_p)}; | ||||
|   } | ||||
|  | ||||
|   void advance_write_buffer(size_t size) { | ||||
|     this->file_->_p += size; | ||||
|     this->file_->_w -= size; | ||||
|   } | ||||
|  | ||||
|   bool needs_flush() const { | ||||
|     if ((this->file_->_flags & line_buffered) == 0) return false; | ||||
|     return memchr(this->file_->_p + this->file_->_w, '\n', | ||||
|                   to_unsigned(-this->file_->_w)); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| // A fallback FILE wrapper. | ||||
| template <typename F> class fallback_file : public file_base<F> { | ||||
|  private: | ||||
|   char next_;  // The next unconsumed character in the buffer. | ||||
|   bool has_next_ = false; | ||||
|  | ||||
|  public: | ||||
|   using file_base<F>::file_base; | ||||
|  | ||||
|   auto is_buffered() const -> bool { return false; } | ||||
|   auto needs_flush() const -> bool { return false; } | ||||
|   void init_buffer() {} | ||||
|  | ||||
|   auto get_read_buffer() const -> span<const char> { | ||||
|     return {&next_, has_next_ ? 1u : 0u}; | ||||
|   } | ||||
|  | ||||
|   auto get_write_buffer() const -> span<char> { return {nullptr, 0}; } | ||||
|  | ||||
|   void advance_write_buffer(size_t) {} | ||||
|  | ||||
|   auto get() -> int { | ||||
|     has_next_ = false; | ||||
|     return file_base<F>::get(); | ||||
|   } | ||||
|  | ||||
|   void unget(char c) { | ||||
|     file_base<F>::unget(c); | ||||
|     next_ = c; | ||||
|     has_next_ = true; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| #ifndef FMT_USE_FALLBACK_FILE | ||||
| #  define FMT_USE_FALLBACK_FILE 0 | ||||
| #endif | ||||
|  | ||||
| template <typename F, | ||||
|           FMT_ENABLE_IF(sizeof(F::_p) != 0 && !FMT_USE_FALLBACK_FILE)> | ||||
| auto get_file(F* f, int) -> apple_file<F> { | ||||
|   return f; | ||||
| } | ||||
| template <typename F, | ||||
|           FMT_ENABLE_IF(sizeof(F::_IO_read_ptr) != 0 && !FMT_USE_FALLBACK_FILE)> | ||||
| inline auto get_file(F* f, int) -> glibc_file<F> { | ||||
|   return f; | ||||
| } | ||||
|  | ||||
| inline auto get_file(FILE* f, ...) -> fallback_file<FILE> { return f; } | ||||
|  | ||||
| using file_ref = decltype(get_file(static_cast<FILE*>(nullptr), 0)); | ||||
|  | ||||
| template <typename F = FILE, typename Enable = void> | ||||
| class file_print_buffer : public buffer<char> { | ||||
|  public: | ||||
|   explicit file_print_buffer(F*) : buffer(nullptr, size_t()) {} | ||||
| }; | ||||
|  | ||||
| template <typename F> | ||||
| class file_print_buffer<F, enable_if_t<has_flockfile<F>::value>> | ||||
|     : public buffer<char> { | ||||
|  private: | ||||
|   file_ref file_; | ||||
|  | ||||
|   static void grow(buffer<char>& base, size_t) { | ||||
|     auto& self = static_cast<file_print_buffer&>(base); | ||||
|     self.file_.advance_write_buffer(self.size()); | ||||
|     if (self.file_.get_write_buffer().size == 0) self.file_.flush(); | ||||
|     auto buf = self.file_.get_write_buffer(); | ||||
|     FMT_ASSERT(buf.size > 0, ""); | ||||
|     self.set(buf.data, buf.size); | ||||
|     self.clear(); | ||||
|   } | ||||
|  | ||||
|  public: | ||||
|   explicit file_print_buffer(F* f) : buffer(grow, size_t()), file_(f) { | ||||
|     flockfile(f); | ||||
|     file_.init_buffer(); | ||||
|     auto buf = file_.get_write_buffer(); | ||||
|     set(buf.data, buf.size); | ||||
|   } | ||||
|   ~file_print_buffer() { | ||||
|     file_.advance_write_buffer(size()); | ||||
|     bool flush = file_.needs_flush(); | ||||
|     F* f = file_;    // Make funlockfile depend on the template parameter F | ||||
|     funlockfile(f);  // for the system API detection to work. | ||||
|     if (flush) fflush(file_); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| #if !defined(_WIN32) || defined(FMT_USE_WRITE_CONSOLE) | ||||
| FMT_FUNC auto write_console(int, string_view) -> bool { return false; } | ||||
| FMT_FUNC auto write_console(std::FILE*, string_view) -> bool { return false; } | ||||
| #else | ||||
| using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>; | ||||
| extern "C" __declspec(dllimport) int __stdcall WriteConsoleW(  // | ||||
| @@ -1445,39 +1704,51 @@ FMT_FUNC bool write_console(int fd, string_view text) { | ||||
|   return WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), u16.c_str(), | ||||
|                        static_cast<dword>(u16.size()), nullptr, nullptr) != 0; | ||||
| } | ||||
|  | ||||
| FMT_FUNC auto write_console(std::FILE* f, string_view text) -> bool { | ||||
|   return write_console(_fileno(f), text); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| // Print assuming legacy (non-Unicode) encoding. | ||||
| FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) { | ||||
| FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args, | ||||
|                               bool newline) { | ||||
|   auto buffer = memory_buffer(); | ||||
|   detail::vformat_to(buffer, fmt, args); | ||||
|   fwrite_fully(buffer.data(), buffer.size(), f); | ||||
|   if (newline) buffer.push_back('\n'); | ||||
|   fwrite_all(buffer.data(), buffer.size(), f); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| FMT_FUNC void print(std::FILE* f, string_view text) { | ||||
| #ifdef _WIN32 | ||||
| #if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE) | ||||
|   int fd = _fileno(f); | ||||
|   if (_isatty(fd)) { | ||||
|     std::fflush(f); | ||||
|     if (write_console(fd, text)) return; | ||||
|   } | ||||
| #endif | ||||
|   fwrite_fully(text.data(), text.size(), f); | ||||
|   fwrite_all(text.data(), text.size(), f); | ||||
| } | ||||
| }  // namespace detail | ||||
|  | ||||
| FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) { | ||||
| FMT_FUNC void vprint_buffered(std::FILE* f, string_view fmt, format_args args) { | ||||
|   auto buffer = memory_buffer(); | ||||
|   detail::vformat_to(buffer, fmt, args); | ||||
|   detail::print(f, {buffer.data(), buffer.size()}); | ||||
| } | ||||
|  | ||||
| FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) { | ||||
|   if (!detail::file_ref(f).is_buffered() || !detail::has_flockfile<>()) | ||||
|     return vprint_buffered(f, fmt, args); | ||||
|   auto&& buffer = detail::file_print_buffer<>(f); | ||||
|   return detail::vformat_to(buffer, fmt, args); | ||||
| } | ||||
|  | ||||
| FMT_FUNC void vprintln(std::FILE* f, string_view fmt, format_args args) { | ||||
|   auto buffer = memory_buffer(); | ||||
|   detail::vformat_to(buffer, fmt, args); | ||||
|   buffer.push_back('\n'); | ||||
|   detail::print(f, {buffer.data(), buffer.size()}); | ||||
| } | ||||
|  | ||||
| FMT_FUNC void vprint(string_view fmt, format_args args) { | ||||
|   vprint(stdout, fmt, args); | ||||
| } | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -8,18 +8,18 @@ | ||||
| #ifndef FMT_OS_H_ | ||||
| #define FMT_OS_H_ | ||||
|  | ||||
| #include <cerrno> | ||||
| #include <cstddef> | ||||
| #include <cstdio> | ||||
| #include <system_error>  // std::system_error | ||||
|  | ||||
| #include "format.h" | ||||
|  | ||||
| #if defined __APPLE__ || defined(__FreeBSD__) | ||||
| #ifndef FMT_MODULE | ||||
| #  include <cerrno> | ||||
| #  include <cstddef> | ||||
| #  include <cstdio> | ||||
| #  include <system_error>  // std::system_error | ||||
|  | ||||
| #  if FMT_HAS_INCLUDE(<xlocale.h>) | ||||
| #    include <xlocale.h>  // for LC_NUMERIC_MASK on OS X | ||||
| #    include <xlocale.h>  // LC_NUMERIC_MASK on macOS | ||||
| #  endif | ||||
| #endif | ||||
| #endif  // FMT_MODULE | ||||
|  | ||||
| #ifndef FMT_USE_FCNTL | ||||
| // UWP doesn't provide _pipe. | ||||
| @@ -77,46 +77,33 @@ FMT_BEGIN_NAMESPACE | ||||
| FMT_BEGIN_EXPORT | ||||
|  | ||||
| /** | ||||
|   \rst | ||||
|   A reference to a null-terminated string. It can be constructed from a C | ||||
|   string or ``std::string``. | ||||
|  | ||||
|   You can use one of the following type aliases for common character types: | ||||
|  | ||||
|   +---------------+-----------------------------+ | ||||
|   | Type          | Definition                  | | ||||
|   +===============+=============================+ | ||||
|   | cstring_view  | basic_cstring_view<char>    | | ||||
|   +---------------+-----------------------------+ | ||||
|   | wcstring_view | basic_cstring_view<wchar_t> | | ||||
|   +---------------+-----------------------------+ | ||||
|  | ||||
|   This class is most useful as a parameter type to allow passing | ||||
|   different types of strings to a function, for example:: | ||||
|  | ||||
|     template <typename... Args> | ||||
|     std::string format(cstring_view format_str, const Args & ... args); | ||||
|  | ||||
|     format("{}", 42); | ||||
|     format(std::string("{}"), 42); | ||||
|   \endrst | ||||
|  * A reference to a null-terminated string. It can be constructed from a C | ||||
|  * string or `std::string`. | ||||
|  * | ||||
|  * You can use one of the following type aliases for common character types: | ||||
|  * | ||||
|  * +---------------+-----------------------------+ | ||||
|  * | Type          | Definition                  | | ||||
|  * +===============+=============================+ | ||||
|  * | cstring_view  | basic_cstring_view<char>    | | ||||
|  * +---------------+-----------------------------+ | ||||
|  * | wcstring_view | basic_cstring_view<wchar_t> | | ||||
|  * +---------------+-----------------------------+ | ||||
|  * | ||||
|  * This class is most useful as a parameter type for functions that wrap C APIs. | ||||
|  */ | ||||
| template <typename Char> class basic_cstring_view { | ||||
|  private: | ||||
|   const Char* data_; | ||||
|  | ||||
|  public: | ||||
|   /** Constructs a string reference object from a C string. */ | ||||
|   /// Constructs a string reference object from a C string. | ||||
|   basic_cstring_view(const Char* s) : data_(s) {} | ||||
|  | ||||
|   /** | ||||
|     \rst | ||||
|     Constructs a string reference from an ``std::string`` object. | ||||
|     \endrst | ||||
|    */ | ||||
|   /// Constructs a string reference from an `std::string` object. | ||||
|   basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {} | ||||
|  | ||||
|   /** Returns the pointer to a C string. */ | ||||
|   /// Returns the pointer to a C string. | ||||
|   auto c_str() const -> const Char* { return data_; } | ||||
| }; | ||||
|  | ||||
| @@ -131,41 +118,38 @@ FMT_API void format_windows_error(buffer<char>& out, int error_code, | ||||
|                                   const char* message) noexcept; | ||||
| } | ||||
|  | ||||
| FMT_API std::system_error vwindows_error(int error_code, string_view format_str, | ||||
| FMT_API std::system_error vwindows_error(int error_code, string_view fmt, | ||||
|                                          format_args args); | ||||
|  | ||||
| /** | ||||
|  \rst | ||||
|  Constructs a :class:`std::system_error` object with the description | ||||
|  of the form | ||||
|  | ||||
|  .. parsed-literal:: | ||||
|    *<message>*: *<system-message>* | ||||
|  | ||||
|  where *<message>* is the formatted message and *<system-message>* is the | ||||
|  system message corresponding to the error code. | ||||
|  *error_code* is a Windows error code as given by ``GetLastError``. | ||||
|  If *error_code* is not a valid error code such as -1, the system message | ||||
|  will look like "error -1". | ||||
|  | ||||
|  **Example**:: | ||||
|  | ||||
|    // This throws a system_error with the description | ||||
|    //   cannot open file 'madeup': The system cannot find the file specified. | ||||
|    // or similar (system message may vary). | ||||
|    const char *filename = "madeup"; | ||||
|    LPOFSTRUCT of = LPOFSTRUCT(); | ||||
|    HFILE file = OpenFile(filename, &of, OF_READ); | ||||
|    if (file == HFILE_ERROR) { | ||||
|      throw fmt::windows_error(GetLastError(), | ||||
|                               "cannot open file '{}'", filename); | ||||
|    } | ||||
|  \endrst | ||||
| */ | ||||
| template <typename... Args> | ||||
| std::system_error windows_error(int error_code, string_view message, | ||||
|                                 const Args&... args) { | ||||
|   return vwindows_error(error_code, message, fmt::make_format_args(args...)); | ||||
|  * Constructs a `std::system_error` object with the description of the form | ||||
|  * | ||||
|  *     <message>: <system-message> | ||||
|  * | ||||
|  * where `<message>` is the formatted message and `<system-message>` is the | ||||
|  * system message corresponding to the error code. | ||||
|  * `error_code` is a Windows error code as given by `GetLastError`. | ||||
|  * If `error_code` is not a valid error code such as -1, the system message | ||||
|  * will look like "error -1". | ||||
|  * | ||||
|  * **Example**: | ||||
|  * | ||||
|  *     // This throws a system_error with the description | ||||
|  *     //   cannot open file 'madeup': The system cannot find the file | ||||
|  * specified. | ||||
|  *     // or similar (system message may vary). | ||||
|  *     const char *filename = "madeup"; | ||||
|  *     LPOFSTRUCT of = LPOFSTRUCT(); | ||||
|  *     HFILE file = OpenFile(filename, &of, OF_READ); | ||||
|  *     if (file == HFILE_ERROR) { | ||||
|  *       throw fmt::windows_error(GetLastError(), | ||||
|  *                                "cannot open file '{}'", filename); | ||||
|  *     } | ||||
|  */ | ||||
| template <typename... T> | ||||
| auto windows_error(int error_code, string_view message, const T&... args) | ||||
|     -> std::system_error { | ||||
|   return vwindows_error(error_code, message, vargs<T...>{{args...}}); | ||||
| } | ||||
|  | ||||
| // Reports a Windows error without throwing an exception. | ||||
| @@ -180,8 +164,8 @@ inline auto system_category() noexcept -> const std::error_category& { | ||||
| // std::system is not available on some platforms such as iOS (#2248). | ||||
| #ifdef __OSX__ | ||||
| template <typename S, typename... Args, typename Char = char_t<S>> | ||||
| void say(const S& format_str, Args&&... args) { | ||||
|   std::system(format("say \"{}\"", format(format_str, args...)).c_str()); | ||||
| void say(const S& fmt, Args&&... args) { | ||||
|   std::system(format("say \"{}\"", format(fmt, args...)).c_str()); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| @@ -192,24 +176,24 @@ class buffered_file { | ||||
|  | ||||
|   friend class file; | ||||
|  | ||||
|   explicit buffered_file(FILE* f) : file_(f) {} | ||||
|   inline explicit buffered_file(FILE* f) : file_(f) {} | ||||
|  | ||||
|  public: | ||||
|   buffered_file(const buffered_file&) = delete; | ||||
|   void operator=(const buffered_file&) = delete; | ||||
|  | ||||
|   // Constructs a buffered_file object which doesn't represent any file. | ||||
|   buffered_file() noexcept : file_(nullptr) {} | ||||
|   inline buffered_file() noexcept : file_(nullptr) {} | ||||
|  | ||||
|   // Destroys the object closing the file it represents if any. | ||||
|   FMT_API ~buffered_file() noexcept; | ||||
|  | ||||
|  public: | ||||
|   buffered_file(buffered_file&& other) noexcept : file_(other.file_) { | ||||
|   inline buffered_file(buffered_file&& other) noexcept : file_(other.file_) { | ||||
|     other.file_ = nullptr; | ||||
|   } | ||||
|  | ||||
|   auto operator=(buffered_file&& other) -> buffered_file& { | ||||
|   inline auto operator=(buffered_file&& other) -> buffered_file& { | ||||
|     close(); | ||||
|     file_ = other.file_; | ||||
|     other.file_ = nullptr; | ||||
| @@ -223,21 +207,20 @@ class buffered_file { | ||||
|   FMT_API void close(); | ||||
|  | ||||
|   // Returns the pointer to a FILE object representing this file. | ||||
|   auto get() const noexcept -> FILE* { return file_; } | ||||
|   inline auto get() const noexcept -> FILE* { return file_; } | ||||
|  | ||||
|   FMT_API auto descriptor() const -> int; | ||||
|  | ||||
|   void vprint(string_view format_str, format_args args) { | ||||
|     fmt::vprint(file_, format_str, args); | ||||
|   } | ||||
|  | ||||
|   template <typename... Args> | ||||
|   inline void print(string_view format_str, const Args&... args) { | ||||
|     vprint(format_str, fmt::make_format_args(args...)); | ||||
|   template <typename... T> | ||||
|   inline void print(string_view fmt, const T&... args) { | ||||
|     fmt::vargs<T...> vargs = {{args...}}; | ||||
|     detail::is_locking<T...>() ? fmt::vprint_buffered(file_, fmt, vargs) | ||||
|                                : fmt::vprint(file_, fmt, vargs); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| #if FMT_USE_FCNTL | ||||
|  | ||||
| // A file. Closed file is represented by a file object with descriptor -1. | ||||
| // Methods that are not declared with noexcept may throw | ||||
| // fmt::system_error in case of failure. Note that some errors such as | ||||
| @@ -251,6 +234,8 @@ class FMT_API file { | ||||
|   // Constructs a file object with a given descriptor. | ||||
|   explicit file(int fd) : fd_(fd) {} | ||||
|  | ||||
|   friend struct pipe; | ||||
|  | ||||
|  public: | ||||
|   // Possible values for the oflag argument to the constructor. | ||||
|   enum { | ||||
| @@ -263,7 +248,7 @@ class FMT_API file { | ||||
|   }; | ||||
|  | ||||
|   // Constructs a file object which doesn't represent any file. | ||||
|   file() noexcept : fd_(-1) {} | ||||
|   inline file() noexcept : fd_(-1) {} | ||||
|  | ||||
|   // Opens a file and constructs a file object representing this file. | ||||
|   file(cstring_view path, int oflag); | ||||
| @@ -272,10 +257,10 @@ class FMT_API file { | ||||
|   file(const file&) = delete; | ||||
|   void operator=(const file&) = delete; | ||||
|  | ||||
|   file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; } | ||||
|   inline file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; } | ||||
|  | ||||
|   // Move assignment is not noexcept because close may throw. | ||||
|   auto operator=(file&& other) -> file& { | ||||
|   inline auto operator=(file&& other) -> file& { | ||||
|     close(); | ||||
|     fd_ = other.fd_; | ||||
|     other.fd_ = -1; | ||||
| @@ -286,7 +271,7 @@ class FMT_API file { | ||||
|   ~file() noexcept; | ||||
|  | ||||
|   // Returns the file descriptor. | ||||
|   auto descriptor() const noexcept -> int { return fd_; } | ||||
|   inline auto descriptor() const noexcept -> int { return fd_; } | ||||
|  | ||||
|   // Closes the file. | ||||
|   void close(); | ||||
| @@ -313,11 +298,6 @@ class FMT_API file { | ||||
|   // necessary. | ||||
|   void dup2(int fd, std::error_code& ec) noexcept; | ||||
|  | ||||
|   // Creates a pipe setting up read_end and write_end file objects for reading | ||||
|   // and writing respectively. | ||||
|   // DEPRECATED! Taking files as out parameters is deprecated. | ||||
|   static void pipe(file& read_end, file& write_end); | ||||
|  | ||||
|   // Creates a buffered_file object associated with this file and detaches | ||||
|   // this file object from the file. | ||||
|   auto fdopen(const char* mode) -> buffered_file; | ||||
| @@ -329,15 +309,24 @@ class FMT_API file { | ||||
| #  endif | ||||
| }; | ||||
|  | ||||
| struct FMT_API pipe { | ||||
|   file read_end; | ||||
|   file write_end; | ||||
|  | ||||
|   // Creates a pipe setting up read_end and write_end file objects for reading | ||||
|   // and writing respectively. | ||||
|   pipe(); | ||||
| }; | ||||
|  | ||||
| // Returns the memory page size. | ||||
| auto getpagesize() -> long; | ||||
|  | ||||
| namespace detail { | ||||
|  | ||||
| struct buffer_size { | ||||
|   buffer_size() = default; | ||||
|   constexpr buffer_size() = default; | ||||
|   size_t value = 0; | ||||
|   auto operator=(size_t val) const -> buffer_size { | ||||
|   FMT_CONSTEXPR auto operator=(size_t val) const -> buffer_size { | ||||
|     auto bs = buffer_size(); | ||||
|     bs.value = val; | ||||
|     return bs; | ||||
| @@ -348,7 +337,7 @@ struct ostream_params { | ||||
|   int oflag = file::WRONLY | file::CREATE | file::TRUNC; | ||||
|   size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768; | ||||
|  | ||||
|   ostream_params() {} | ||||
|   constexpr ostream_params() {} | ||||
|  | ||||
|   template <typename... T> | ||||
|   ostream_params(T... params, int new_oflag) : ostream_params(params...) { | ||||
| @@ -369,79 +358,62 @@ struct ostream_params { | ||||
| #  endif | ||||
| }; | ||||
|  | ||||
| class file_buffer final : public buffer<char> { | ||||
| }  // namespace detail | ||||
|  | ||||
| FMT_INLINE_VARIABLE constexpr auto buffer_size = detail::buffer_size(); | ||||
|  | ||||
| /// A fast buffered output stream for writing from a single thread. Writing from | ||||
| /// multiple threads without external synchronization may result in a data race. | ||||
| class FMT_API ostream : private detail::buffer<char> { | ||||
|  private: | ||||
|   file file_; | ||||
|  | ||||
|   FMT_API void grow(size_t) override; | ||||
|   ostream(cstring_view path, const detail::ostream_params& params); | ||||
|  | ||||
|   static void grow(buffer<char>& buf, size_t); | ||||
|  | ||||
|  public: | ||||
|   FMT_API file_buffer(cstring_view path, const ostream_params& params); | ||||
|   FMT_API file_buffer(file_buffer&& other); | ||||
|   FMT_API ~file_buffer(); | ||||
|   ostream(ostream&& other) noexcept; | ||||
|   ~ostream(); | ||||
|  | ||||
|   void flush() { | ||||
|   operator writer() { | ||||
|     detail::buffer<char>& buf = *this; | ||||
|     return buf; | ||||
|   } | ||||
|  | ||||
|   inline void flush() { | ||||
|     if (size() == 0) return; | ||||
|     file_.write(data(), size() * sizeof(data()[0])); | ||||
|     clear(); | ||||
|   } | ||||
|  | ||||
|   void close() { | ||||
|     flush(); | ||||
|     file_.close(); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| }  // namespace detail | ||||
|  | ||||
| // Added {} below to work around default constructor error known to | ||||
| // occur in Xcode versions 7.2.1 and 8.2.1. | ||||
| constexpr detail::buffer_size buffer_size{}; | ||||
|  | ||||
| /** A fast output stream which is not thread-safe. */ | ||||
| class FMT_API ostream { | ||||
|  private: | ||||
|   FMT_MSC_WARNING(suppress : 4251) | ||||
|   detail::file_buffer buffer_; | ||||
|  | ||||
|   ostream(cstring_view path, const detail::ostream_params& params) | ||||
|       : buffer_(path, params) {} | ||||
|  | ||||
|  public: | ||||
|   ostream(ostream&& other) : buffer_(std::move(other.buffer_)) {} | ||||
|  | ||||
|   ~ostream(); | ||||
|  | ||||
|   void flush() { buffer_.flush(); } | ||||
|  | ||||
|   template <typename... T> | ||||
|   friend auto output_file(cstring_view path, T... params) -> ostream; | ||||
|  | ||||
|   void close() { buffer_.close(); } | ||||
|   inline void close() { | ||||
|     flush(); | ||||
|     file_.close(); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|     Formats ``args`` according to specifications in ``fmt`` and writes the | ||||
|     output to the file. | ||||
|    */ | ||||
|   /// Formats `args` according to specifications in `fmt` and writes the | ||||
|   /// output to the file. | ||||
|   template <typename... T> void print(format_string<T...> fmt, T&&... args) { | ||||
|     vformat_to(std::back_inserter(buffer_), fmt, | ||||
|                fmt::make_format_args(args...)); | ||||
|     vformat_to(appender(*this), fmt.str, vargs<T...>{{args...}}); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| /** | ||||
|   \rst | ||||
|   Opens a file for writing. Supported parameters passed in *params*: | ||||
|  | ||||
|   * ``<integer>``: Flags passed to `open | ||||
|     <https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_ | ||||
|     (``file::WRONLY | file::CREATE | file::TRUNC`` by default) | ||||
|   * ``buffer_size=<integer>``: Output buffer size | ||||
|  | ||||
|   **Example**:: | ||||
|  | ||||
|     auto out = fmt::output_file("guide.txt"); | ||||
|     out.print("Don't {}", "Panic"); | ||||
|   \endrst | ||||
|  * Opens a file for writing. Supported parameters passed in `params`: | ||||
|  * | ||||
|  * - `<integer>`: Flags passed to [open]( | ||||
|  *   https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html) | ||||
|  *   (`file::WRONLY | file::CREATE | file::TRUNC` by default) | ||||
|  * - `buffer_size=<integer>`: Output buffer size | ||||
|  * | ||||
|  * **Example**: | ||||
|  * | ||||
|  *     auto out = fmt::output_file("guide.txt"); | ||||
|  *     out.print("Don't {}", "Panic"); | ||||
|  */ | ||||
| template <typename... T> | ||||
| inline auto output_file(cstring_view path, T... params) -> ostream { | ||||
|   | ||||
| @@ -8,7 +8,9 @@ | ||||
| #ifndef FMT_OSTREAM_H_ | ||||
| #define FMT_OSTREAM_H_ | ||||
|  | ||||
| #include <fstream>  // std::filebuf | ||||
| #ifndef FMT_MODULE | ||||
| #  include <fstream>  // std::filebuf | ||||
| #endif | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| #  ifdef __GLIBCXX__ | ||||
| @@ -18,42 +20,19 @@ | ||||
| #  include <io.h> | ||||
| #endif | ||||
|  | ||||
| #include "format.h" | ||||
| #include "chrono.h"  // formatbuf | ||||
|  | ||||
| #ifdef _MSVC_STL_UPDATE | ||||
| #  define FMT_MSVC_STL_UPDATE _MSVC_STL_UPDATE | ||||
| #elif defined(_MSC_VER) && _MSC_VER < 1912  // VS 15.5 | ||||
| #  define FMT_MSVC_STL_UPDATE _MSVC_LANG | ||||
| #else | ||||
| #  define FMT_MSVC_STL_UPDATE 0 | ||||
| #endif | ||||
|  | ||||
| FMT_BEGIN_NAMESPACE | ||||
| namespace detail { | ||||
|  | ||||
| template <typename Streambuf> class formatbuf : public Streambuf { | ||||
|  private: | ||||
|   using char_type = typename Streambuf::char_type; | ||||
|   using streamsize = decltype(std::declval<Streambuf>().sputn(nullptr, 0)); | ||||
|   using int_type = typename Streambuf::int_type; | ||||
|   using traits_type = typename Streambuf::traits_type; | ||||
|  | ||||
|   buffer<char_type>& buffer_; | ||||
|  | ||||
|  public: | ||||
|   explicit formatbuf(buffer<char_type>& buf) : buffer_(buf) {} | ||||
|  | ||||
|  protected: | ||||
|   // The put area is always empty. This makes the implementation simpler and has | ||||
|   // the advantage that the streambuf and the buffer are always in sync and | ||||
|   // sputc never writes into uninitialized memory. A disadvantage is that each | ||||
|   // call to sputc always results in a (virtual) call to overflow. There is no | ||||
|   // disadvantage here for sputn since this always results in a call to xsputn. | ||||
|  | ||||
|   auto overflow(int_type ch) -> int_type override { | ||||
|     if (!traits_type::eq_int_type(ch, traits_type::eof())) | ||||
|       buffer_.push_back(static_cast<char_type>(ch)); | ||||
|     return ch; | ||||
|   } | ||||
|  | ||||
|   auto xsputn(const char_type* s, streamsize count) -> streamsize override { | ||||
|     buffer_.append(s, s + count); | ||||
|     return count; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| // Generate a unique explicit instantion in every translation unit using a tag | ||||
| // type in an anonymous namespace. | ||||
| namespace { | ||||
| @@ -64,53 +43,18 @@ class file_access { | ||||
|   friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; } | ||||
| }; | ||||
|  | ||||
| #if FMT_MSC_VERSION | ||||
| #if FMT_MSVC_STL_UPDATE | ||||
| template class file_access<file_access_tag, std::filebuf, | ||||
|                            &std::filebuf::_Myfile>; | ||||
| auto get_file(std::filebuf&) -> FILE*; | ||||
| #endif | ||||
|  | ||||
| inline auto write_ostream_unicode(std::ostream& os, fmt::string_view data) | ||||
|     -> bool { | ||||
|   FILE* f = nullptr; | ||||
| #if FMT_MSC_VERSION | ||||
|   if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf())) | ||||
|     f = get_file(*buf); | ||||
|   else | ||||
|     return false; | ||||
| #elif defined(_WIN32) && defined(__GLIBCXX__) | ||||
|   auto* rdbuf = os.rdbuf(); | ||||
|   if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf)) | ||||
|     f = sfbuf->file(); | ||||
|   else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf)) | ||||
|     f = fbuf->file(); | ||||
|   else | ||||
|     return false; | ||||
| #else | ||||
|   ignore_unused(os, data, f); | ||||
| #endif | ||||
| #ifdef _WIN32 | ||||
|   if (f) { | ||||
|     int fd = _fileno(f); | ||||
|     if (_isatty(fd)) { | ||||
|       os.flush(); | ||||
|       return write_console(fd, data); | ||||
|     } | ||||
|   } | ||||
| #endif | ||||
|   return false; | ||||
| } | ||||
| inline auto write_ostream_unicode(std::wostream&, | ||||
|                                   fmt::basic_string_view<wchar_t>) -> bool { | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| // Write the content of buf to os. | ||||
| // It is a separate function rather than a part of vprint to simplify testing. | ||||
| template <typename Char> | ||||
| void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) { | ||||
|   const Char* buf_data = buf.data(); | ||||
|   using unsigned_streamsize = std::make_unsigned<std::streamsize>::type; | ||||
|   using unsigned_streamsize = make_unsigned_t<std::streamsize>; | ||||
|   unsigned_streamsize size = buf.size(); | ||||
|   unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>()); | ||||
|   do { | ||||
| @@ -121,21 +65,9 @@ void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) { | ||||
|   } while (size != 0); | ||||
| } | ||||
|  | ||||
| template <typename Char, typename T> | ||||
| void format_value(buffer<Char>& buf, const T& value) { | ||||
|   auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf); | ||||
|   auto&& output = std::basic_ostream<Char>(&format_buf); | ||||
| #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) | ||||
|   output.imbue(std::locale::classic());  // The default is always unlocalized. | ||||
| #endif | ||||
|   output << value; | ||||
|   output.exceptions(std::ios_base::failbit | std::ios_base::badbit); | ||||
| } | ||||
|  | ||||
| template <typename T> struct streamed_view { | ||||
|   const T& value; | ||||
| }; | ||||
|  | ||||
| }  // namespace detail | ||||
|  | ||||
| // Formats an object of type T that has an overloaded ostream operator<<. | ||||
| @@ -143,11 +75,14 @@ template <typename Char> | ||||
| struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> { | ||||
|   void set_debug_format() = delete; | ||||
|  | ||||
|   template <typename T, typename OutputIt> | ||||
|   auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const | ||||
|       -> OutputIt { | ||||
|   template <typename T, typename Context> | ||||
|   auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) { | ||||
|     auto buffer = basic_memory_buffer<Char>(); | ||||
|     detail::format_value(buffer, value); | ||||
|     auto&& formatbuf = detail::formatbuf<std::basic_streambuf<Char>>(buffer); | ||||
|     auto&& output = std::basic_ostream<Char>(&formatbuf); | ||||
|     output.imbue(std::locale::classic());  // The default is always unlocalized. | ||||
|     output << value; | ||||
|     output.exceptions(std::ios_base::failbit | std::ios_base::badbit); | ||||
|     return formatter<basic_string_view<Char>, Char>::format( | ||||
|         {buffer.data(), buffer.size()}, ctx); | ||||
|   } | ||||
| @@ -158,73 +93,67 @@ using ostream_formatter = basic_ostream_formatter<char>; | ||||
| template <typename T, typename Char> | ||||
| struct formatter<detail::streamed_view<T>, Char> | ||||
|     : basic_ostream_formatter<Char> { | ||||
|   template <typename OutputIt> | ||||
|   auto format(detail::streamed_view<T> view, | ||||
|               basic_format_context<OutputIt, Char>& ctx) const -> OutputIt { | ||||
|   template <typename Context> | ||||
|   auto format(detail::streamed_view<T> view, Context& ctx) const | ||||
|       -> decltype(ctx.out()) { | ||||
|     return basic_ostream_formatter<Char>::format(view.value, ctx); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| /** | ||||
|   \rst | ||||
|   Returns a view that formats `value` via an ostream ``operator<<``. | ||||
|  | ||||
|   **Example**:: | ||||
|  | ||||
|     fmt::print("Current thread id: {}\n", | ||||
|                fmt::streamed(std::this_thread::get_id())); | ||||
|   \endrst | ||||
|  * Returns a view that formats `value` via an ostream `operator<<`. | ||||
|  * | ||||
|  * **Example**: | ||||
|  * | ||||
|  *     fmt::print("Current thread id: {}\n", | ||||
|  *                fmt::streamed(std::this_thread::get_id())); | ||||
|  */ | ||||
| template <typename T> | ||||
| constexpr auto streamed(const T& value) -> detail::streamed_view<T> { | ||||
|   return {value}; | ||||
| } | ||||
|  | ||||
| namespace detail { | ||||
|  | ||||
| inline void vprint_directly(std::ostream& os, string_view format_str, | ||||
|                             format_args args) { | ||||
| inline void vprint(std::ostream& os, string_view fmt, format_args args) { | ||||
|   auto buffer = memory_buffer(); | ||||
|   detail::vformat_to(buffer, format_str, args); | ||||
|   detail::write_buffer(os, buffer); | ||||
| } | ||||
|  | ||||
| }  // namespace detail | ||||
|  | ||||
| FMT_EXPORT template <typename Char> | ||||
| void vprint(std::basic_ostream<Char>& os, | ||||
|             basic_string_view<type_identity_t<Char>> format_str, | ||||
|             basic_format_args<buffer_context<type_identity_t<Char>>> args) { | ||||
|   auto buffer = basic_memory_buffer<Char>(); | ||||
|   detail::vformat_to(buffer, format_str, args); | ||||
|   if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return; | ||||
|   detail::vformat_to(buffer, fmt, args); | ||||
|   FILE* f = nullptr; | ||||
| #if FMT_MSVC_STL_UPDATE && FMT_USE_RTTI | ||||
|   if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf())) | ||||
|     f = detail::get_file(*buf); | ||||
| #elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI | ||||
|   auto* rdbuf = os.rdbuf(); | ||||
|   if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf)) | ||||
|     f = sfbuf->file(); | ||||
|   else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf)) | ||||
|     f = fbuf->file(); | ||||
| #endif | ||||
| #ifdef _WIN32 | ||||
|   if (f) { | ||||
|     int fd = _fileno(f); | ||||
|     if (_isatty(fd)) { | ||||
|       os.flush(); | ||||
|       if (detail::write_console(fd, {buffer.data(), buffer.size()})) return; | ||||
|     } | ||||
|   } | ||||
| #endif | ||||
|   detail::ignore_unused(f); | ||||
|   detail::write_buffer(os, buffer); | ||||
| } | ||||
|  | ||||
| /** | ||||
|   \rst | ||||
|   Prints formatted data to the stream *os*. | ||||
|  | ||||
|   **Example**:: | ||||
|  | ||||
|     fmt::print(cerr, "Don't {}!", "panic"); | ||||
|   \endrst | ||||
|  * Prints formatted data to the stream `os`. | ||||
|  * | ||||
|  * **Example**: | ||||
|  * | ||||
|  *     fmt::print(cerr, "Don't {}!", "panic"); | ||||
|  */ | ||||
| FMT_EXPORT template <typename... T> | ||||
| void print(std::ostream& os, format_string<T...> fmt, T&&... args) { | ||||
|   const auto& vargs = fmt::make_format_args(args...); | ||||
|   if (detail::is_utf8()) | ||||
|     vprint(os, fmt, vargs); | ||||
|   else | ||||
|     detail::vprint_directly(os, fmt, vargs); | ||||
| } | ||||
|  | ||||
| FMT_EXPORT | ||||
| template <typename... Args> | ||||
| void print(std::wostream& os, | ||||
|            basic_format_string<wchar_t, type_identity_t<Args>...> fmt, | ||||
|            Args&&... args) { | ||||
|   vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...)); | ||||
|   fmt::vargs<T...> vargs = {{args...}}; | ||||
|   if (detail::const_check(detail::use_utf8)) return vprint(os, fmt.str, vargs); | ||||
|   auto buffer = memory_buffer(); | ||||
|   detail::vformat_to(buffer, fmt.str, vargs); | ||||
|   detail::write_buffer(os, buffer); | ||||
| } | ||||
|  | ||||
| FMT_EXPORT template <typename... T> | ||||
| @@ -232,14 +161,6 @@ void println(std::ostream& os, format_string<T...> fmt, T&&... args) { | ||||
|   fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...)); | ||||
| } | ||||
|  | ||||
| FMT_EXPORT | ||||
| template <typename... Args> | ||||
| void println(std::wostream& os, | ||||
|              basic_format_string<wchar_t, type_identity_t<Args>...> fmt, | ||||
|              Args&&... args) { | ||||
|   print(os, L"{}\n", fmt::format(fmt, std::forward<Args>(args)...)); | ||||
| } | ||||
|  | ||||
| FMT_END_NAMESPACE | ||||
|  | ||||
| #endif  // FMT_OSTREAM_H_ | ||||
|   | ||||
| @@ -8,8 +8,10 @@ | ||||
| #ifndef FMT_PRINTF_H_ | ||||
| #define FMT_PRINTF_H_ | ||||
|  | ||||
| #include <algorithm>  // std::max | ||||
| #include <limits>     // std::numeric_limits | ||||
| #ifndef FMT_MODULE | ||||
| #  include <algorithm>  // std::max | ||||
| #  include <limits>     // std::numeric_limits | ||||
| #endif | ||||
|  | ||||
| #include "format.h" | ||||
|  | ||||
| @@ -22,7 +24,7 @@ template <typename T> struct printf_formatter { | ||||
|  | ||||
| template <typename Char> class basic_printf_context { | ||||
|  private: | ||||
|   detail::buffer_appender<Char> out_; | ||||
|   basic_appender<Char> out_; | ||||
|   basic_format_args<basic_printf_context> args_; | ||||
|  | ||||
|   static_assert(std::is_same<Char, char>::value || | ||||
| @@ -31,43 +33,53 @@ template <typename Char> class basic_printf_context { | ||||
|  | ||||
|  public: | ||||
|   using char_type = Char; | ||||
|   using parse_context_type = basic_format_parse_context<Char>; | ||||
|   using parse_context_type = parse_context<Char>; | ||||
|   template <typename T> using formatter_type = printf_formatter<T>; | ||||
|   enum { builtin_types = 1 }; | ||||
|  | ||||
|   /** | ||||
|     \rst | ||||
|     Constructs a ``printf_context`` object. References to the arguments are | ||||
|     stored in the context object so make sure they have appropriate lifetimes. | ||||
|     \endrst | ||||
|    */ | ||||
|   basic_printf_context(detail::buffer_appender<Char> out, | ||||
|   /// Constructs a `printf_context` object. References to the arguments are | ||||
|   /// stored in the context object so make sure they have appropriate lifetimes. | ||||
|   basic_printf_context(basic_appender<Char> out, | ||||
|                        basic_format_args<basic_printf_context> args) | ||||
|       : out_(out), args_(args) {} | ||||
|  | ||||
|   auto out() -> detail::buffer_appender<Char> { return out_; } | ||||
|   void advance_to(detail::buffer_appender<Char>) {} | ||||
|   auto out() -> basic_appender<Char> { return out_; } | ||||
|   void advance_to(basic_appender<Char>) {} | ||||
|  | ||||
|   auto locale() -> detail::locale_ref { return {}; } | ||||
|  | ||||
|   auto arg(int id) const -> basic_format_arg<basic_printf_context> { | ||||
|     return args_.get(id); | ||||
|   } | ||||
|  | ||||
|   FMT_CONSTEXPR void on_error(const char* message) { | ||||
|     detail::error_handler().on_error(message); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| namespace detail { | ||||
|  | ||||
| // Return the result via the out param to workaround gcc bug 77539. | ||||
| template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*> | ||||
| FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool { | ||||
|   for (out = first; out != last; ++out) { | ||||
|     if (*out == value) return true; | ||||
|   } | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| template <> | ||||
| inline auto find<false, char>(const char* first, const char* last, char value, | ||||
|                               const char*& out) -> bool { | ||||
|   out = | ||||
|       static_cast<const char*>(memchr(first, value, to_unsigned(last - first))); | ||||
|   return out != nullptr; | ||||
| } | ||||
|  | ||||
| // Checks if a value fits in int - used to avoid warnings about comparing | ||||
| // signed and unsigned integers. | ||||
| template <bool IsSigned> struct int_checker { | ||||
|   template <typename T> static auto fits_in_int(T value) -> bool { | ||||
|     unsigned max = max_value<int>(); | ||||
|     unsigned max = to_unsigned(max_value<int>()); | ||||
|     return value <= max; | ||||
|   } | ||||
|   static auto fits_in_int(bool) -> bool { return true; } | ||||
|   inline static auto fits_in_int(bool) -> bool { return true; } | ||||
| }; | ||||
|  | ||||
| template <> struct int_checker<true> { | ||||
| @@ -75,20 +87,20 @@ template <> struct int_checker<true> { | ||||
|     return value >= (std::numeric_limits<int>::min)() && | ||||
|            value <= max_value<int>(); | ||||
|   } | ||||
|   static auto fits_in_int(int) -> bool { return true; } | ||||
|   inline static auto fits_in_int(int) -> bool { return true; } | ||||
| }; | ||||
|  | ||||
| struct printf_precision_handler { | ||||
|   template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> | ||||
|   auto operator()(T value) -> int { | ||||
|     if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value)) | ||||
|       throw_format_error("number is too big"); | ||||
|       report_error("number is too big"); | ||||
|     return (std::max)(static_cast<int>(value), 0); | ||||
|   } | ||||
|  | ||||
|   template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> | ||||
|   auto operator()(T) -> int { | ||||
|     throw_format_error("precision is not integer"); | ||||
|     report_error("precision is not integer"); | ||||
|     return 0; | ||||
|   } | ||||
| }; | ||||
| @@ -133,25 +145,19 @@ template <typename T, typename Context> class arg_converter { | ||||
|     using target_type = conditional_t<std::is_same<T, void>::value, U, T>; | ||||
|     if (const_check(sizeof(target_type) <= sizeof(int))) { | ||||
|       // Extra casts are used to silence warnings. | ||||
|       if (is_signed) { | ||||
|         auto n = static_cast<int>(static_cast<target_type>(value)); | ||||
|         arg_ = detail::make_arg<Context>(n); | ||||
|       } else { | ||||
|         using unsigned_type = typename make_unsigned_or_bool<target_type>::type; | ||||
|         auto n = static_cast<unsigned>(static_cast<unsigned_type>(value)); | ||||
|         arg_ = detail::make_arg<Context>(n); | ||||
|       } | ||||
|       using unsigned_type = typename make_unsigned_or_bool<target_type>::type; | ||||
|       if (is_signed) | ||||
|         arg_ = static_cast<int>(static_cast<target_type>(value)); | ||||
|       else | ||||
|         arg_ = static_cast<unsigned>(static_cast<unsigned_type>(value)); | ||||
|     } else { | ||||
|       if (is_signed) { | ||||
|         // glibc's printf doesn't sign extend arguments of smaller types: | ||||
|         //   std::printf("%lld", -42);  // prints "4294967254" | ||||
|         // but we don't have to do the same because it's a UB. | ||||
|         auto n = static_cast<long long>(value); | ||||
|         arg_ = detail::make_arg<Context>(n); | ||||
|       } else { | ||||
|         auto n = static_cast<typename make_unsigned_or_bool<U>::type>(value); | ||||
|         arg_ = detail::make_arg<Context>(n); | ||||
|       } | ||||
|       // glibc's printf doesn't sign extend arguments of smaller types: | ||||
|       //   std::printf("%lld", -42);  // prints "4294967254" | ||||
|       // but we don't have to do the same because it's a UB. | ||||
|       if (is_signed) | ||||
|         arg_ = static_cast<long long>(value); | ||||
|       else | ||||
|         arg_ = static_cast<typename make_unsigned_or_bool<U>::type>(value); | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -165,7 +171,7 @@ template <typename T, typename Context> class arg_converter { | ||||
| // unsigned). | ||||
| template <typename T, typename Context, typename Char> | ||||
| void convert_arg(basic_format_arg<Context>& arg, Char type) { | ||||
|   visit_format_arg(arg_converter<T, Context>(arg, type), arg); | ||||
|   arg.visit(arg_converter<T, Context>(arg, type)); | ||||
| } | ||||
|  | ||||
| // Converts an integer argument to char for printf. | ||||
| @@ -178,8 +184,7 @@ template <typename Context> class char_converter { | ||||
|  | ||||
|   template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> | ||||
|   void operator()(T value) { | ||||
|     auto c = static_cast<typename Context::char_type>(value); | ||||
|     arg_ = detail::make_arg<Context>(c); | ||||
|     arg_ = static_cast<typename Context::char_type>(value); | ||||
|   } | ||||
|  | ||||
|   template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> | ||||
| @@ -195,28 +200,28 @@ template <typename Char> struct get_cstring { | ||||
|  | ||||
| // Checks if an argument is a valid printf width specifier and sets | ||||
| // left alignment if it is negative. | ||||
| template <typename Char> class printf_width_handler { | ||||
| class printf_width_handler { | ||||
|  private: | ||||
|   format_specs<Char>& specs_; | ||||
|   format_specs& specs_; | ||||
|  | ||||
|  public: | ||||
|   explicit printf_width_handler(format_specs<Char>& specs) : specs_(specs) {} | ||||
|   inline explicit printf_width_handler(format_specs& specs) : specs_(specs) {} | ||||
|  | ||||
|   template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> | ||||
|   auto operator()(T value) -> unsigned { | ||||
|     auto width = static_cast<uint32_or_64_or_128_t<T>>(value); | ||||
|     if (detail::is_negative(value)) { | ||||
|       specs_.align = align::left; | ||||
|       specs_.set_align(align::left); | ||||
|       width = 0 - width; | ||||
|     } | ||||
|     unsigned int_max = max_value<int>(); | ||||
|     if (width > int_max) throw_format_error("number is too big"); | ||||
|     unsigned int_max = to_unsigned(max_value<int>()); | ||||
|     if (width > int_max) report_error("number is too big"); | ||||
|     return static_cast<unsigned>(width); | ||||
|   } | ||||
|  | ||||
|   template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> | ||||
|   auto operator()(T) -> unsigned { | ||||
|     throw_format_error("width is not integer"); | ||||
|     report_error("width is not integer"); | ||||
|     return 0; | ||||
|   } | ||||
| }; | ||||
| @@ -224,12 +229,12 @@ template <typename Char> class printf_width_handler { | ||||
| // Workaround for a bug with the XL compiler when initializing | ||||
| // printf_arg_formatter's base class. | ||||
| template <typename Char> | ||||
| auto make_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s) | ||||
| auto make_arg_formatter(basic_appender<Char> iter, format_specs& s) | ||||
|     -> arg_formatter<Char> { | ||||
|   return {iter, s, locale_ref()}; | ||||
| } | ||||
|  | ||||
| // The ``printf`` argument formatter. | ||||
| // The `printf` argument formatter. | ||||
| template <typename Char> | ||||
| class printf_arg_formatter : public arg_formatter<Char> { | ||||
|  private: | ||||
| @@ -240,105 +245,96 @@ class printf_arg_formatter : public arg_formatter<Char> { | ||||
|  | ||||
|   void write_null_pointer(bool is_string = false) { | ||||
|     auto s = this->specs; | ||||
|     s.type = presentation_type::none; | ||||
|     write_bytes(this->out, is_string ? "(null)" : "(nil)", s); | ||||
|     s.set_type(presentation_type::none); | ||||
|     write_bytes<Char>(this->out, is_string ? "(null)" : "(nil)", s); | ||||
|   } | ||||
|  | ||||
|   template <typename T> void write(T value) { | ||||
|     detail::write<Char>(this->out, value, this->specs, this->locale); | ||||
|   } | ||||
|  | ||||
|  public: | ||||
|   printf_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s, | ||||
|   printf_arg_formatter(basic_appender<Char> iter, format_specs& s, | ||||
|                        context_type& ctx) | ||||
|       : base(make_arg_formatter(iter, s)), context_(ctx) {} | ||||
|  | ||||
|   void operator()(monostate value) { base::operator()(value); } | ||||
|   void operator()(monostate value) { write(value); } | ||||
|  | ||||
|   template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)> | ||||
|   void operator()(T value) { | ||||
|     // MSVC2013 fails to compile separate overloads for bool and Char so use | ||||
|     // std::is_same instead. | ||||
|     if (!std::is_same<T, Char>::value) { | ||||
|       base::operator()(value); | ||||
|       write(value); | ||||
|       return; | ||||
|     } | ||||
|     format_specs<Char> fmt_specs = this->specs; | ||||
|     if (fmt_specs.type != presentation_type::none && | ||||
|         fmt_specs.type != presentation_type::chr) { | ||||
|     format_specs s = this->specs; | ||||
|     if (s.type() != presentation_type::none && | ||||
|         s.type() != presentation_type::chr) { | ||||
|       return (*this)(static_cast<int>(value)); | ||||
|     } | ||||
|     fmt_specs.sign = sign::none; | ||||
|     fmt_specs.alt = false; | ||||
|     fmt_specs.fill[0] = ' ';  // Ignore '0' flag for char types. | ||||
|     s.set_sign(sign::none); | ||||
|     s.clear_alt(); | ||||
|     s.set_fill(' ');  // Ignore '0' flag for char types. | ||||
|     // align::numeric needs to be overwritten here since the '0' flag is | ||||
|     // ignored for non-numeric types | ||||
|     if (fmt_specs.align == align::none || fmt_specs.align == align::numeric) | ||||
|       fmt_specs.align = align::right; | ||||
|     write<Char>(this->out, static_cast<Char>(value), fmt_specs); | ||||
|     if (s.align() == align::none || s.align() == align::numeric) | ||||
|       s.set_align(align::right); | ||||
|     detail::write<Char>(this->out, static_cast<Char>(value), s); | ||||
|   } | ||||
|  | ||||
|   template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)> | ||||
|   void operator()(T value) { | ||||
|     base::operator()(value); | ||||
|     write(value); | ||||
|   } | ||||
|  | ||||
|   /** Formats a null-terminated C string. */ | ||||
|   void operator()(const char* value) { | ||||
|     if (value) | ||||
|       base::operator()(value); | ||||
|       write(value); | ||||
|     else | ||||
|       write_null_pointer(this->specs.type != presentation_type::pointer); | ||||
|       write_null_pointer(this->specs.type() != presentation_type::pointer); | ||||
|   } | ||||
|  | ||||
|   /** Formats a null-terminated wide C string. */ | ||||
|   void operator()(const wchar_t* value) { | ||||
|     if (value) | ||||
|       base::operator()(value); | ||||
|       write(value); | ||||
|     else | ||||
|       write_null_pointer(this->specs.type != presentation_type::pointer); | ||||
|       write_null_pointer(this->specs.type() != presentation_type::pointer); | ||||
|   } | ||||
|  | ||||
|   void operator()(basic_string_view<Char> value) { base::operator()(value); } | ||||
|   void operator()(basic_string_view<Char> value) { write(value); } | ||||
|  | ||||
|   /** Formats a pointer. */ | ||||
|   void operator()(const void* value) { | ||||
|     if (value) | ||||
|       base::operator()(value); | ||||
|       write(value); | ||||
|     else | ||||
|       write_null_pointer(); | ||||
|   } | ||||
|  | ||||
|   /** Formats an argument of a custom (user-defined) type. */ | ||||
|   void operator()(typename basic_format_arg<context_type>::handle handle) { | ||||
|     auto parse_ctx = basic_format_parse_context<Char>({}); | ||||
|     auto parse_ctx = parse_context<Char>({}); | ||||
|     handle.format(parse_ctx, context_); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| template <typename Char> | ||||
| void parse_flags(format_specs<Char>& specs, const Char*& it, const Char* end) { | ||||
| void parse_flags(format_specs& specs, const Char*& it, const Char* end) { | ||||
|   for (; it != end; ++it) { | ||||
|     switch (*it) { | ||||
|     case '-': | ||||
|       specs.align = align::left; | ||||
|       break; | ||||
|     case '+': | ||||
|       specs.sign = sign::plus; | ||||
|       break; | ||||
|     case '0': | ||||
|       specs.fill[0] = '0'; | ||||
|       break; | ||||
|     case '-': specs.set_align(align::left); break; | ||||
|     case '+': specs.set_sign(sign::plus); break; | ||||
|     case '0': specs.set_fill('0'); break; | ||||
|     case ' ': | ||||
|       if (specs.sign != sign::plus) specs.sign = sign::space; | ||||
|       if (specs.sign() != sign::plus) specs.set_sign(sign::space); | ||||
|       break; | ||||
|     case '#': | ||||
|       specs.alt = true; | ||||
|       break; | ||||
|     default: | ||||
|       return; | ||||
|     case '#': specs.set_alt(); break; | ||||
|     default:  return; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| template <typename Char, typename GetArg> | ||||
| auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs, | ||||
| auto parse_header(const Char*& it, const Char* end, format_specs& specs, | ||||
|                   GetArg get_arg) -> int { | ||||
|   int arg_index = -1; | ||||
|   Char c = *it; | ||||
| @@ -350,11 +346,11 @@ auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs, | ||||
|       ++it; | ||||
|       arg_index = value != -1 ? value : max_value<int>(); | ||||
|     } else { | ||||
|       if (c == '0') specs.fill[0] = '0'; | ||||
|       if (c == '0') specs.set_fill('0'); | ||||
|       if (value != 0) { | ||||
|         // Nonzero value means that we parsed width and don't need to | ||||
|         // parse it or flags again, so return now. | ||||
|         if (value == -1) throw_format_error("number is too big"); | ||||
|         if (value == -1) report_error("number is too big"); | ||||
|         specs.width = value; | ||||
|         return arg_index; | ||||
|       } | ||||
| @@ -365,63 +361,47 @@ auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs, | ||||
|   if (it != end) { | ||||
|     if (*it >= '0' && *it <= '9') { | ||||
|       specs.width = parse_nonnegative_int(it, end, -1); | ||||
|       if (specs.width == -1) throw_format_error("number is too big"); | ||||
|       if (specs.width == -1) report_error("number is too big"); | ||||
|     } else if (*it == '*') { | ||||
|       ++it; | ||||
|       specs.width = static_cast<int>(visit_format_arg( | ||||
|           detail::printf_width_handler<Char>(specs), get_arg(-1))); | ||||
|       specs.width = static_cast<int>( | ||||
|           get_arg(-1).visit(detail::printf_width_handler(specs))); | ||||
|     } | ||||
|   } | ||||
|   return arg_index; | ||||
| } | ||||
|  | ||||
| inline auto parse_printf_presentation_type(char c, type t) | ||||
| inline auto parse_printf_presentation_type(char c, type t, bool& upper) | ||||
|     -> presentation_type { | ||||
|   using pt = presentation_type; | ||||
|   constexpr auto integral_set = sint_set | uint_set | bool_set | char_set; | ||||
|   switch (c) { | ||||
|   case 'd': | ||||
|     return in(t, integral_set) ? pt::dec : pt::none; | ||||
|   case 'o': | ||||
|     return in(t, integral_set) ? pt::oct : pt::none; | ||||
|   case 'x': | ||||
|     return in(t, integral_set) ? pt::hex_lower : pt::none; | ||||
|   case 'X': | ||||
|     return in(t, integral_set) ? pt::hex_upper : pt::none; | ||||
|   case 'a': | ||||
|     return in(t, float_set) ? pt::hexfloat_lower : pt::none; | ||||
|   case 'A': | ||||
|     return in(t, float_set) ? pt::hexfloat_upper : pt::none; | ||||
|   case 'e': | ||||
|     return in(t, float_set) ? pt::exp_lower : pt::none; | ||||
|   case 'E': | ||||
|     return in(t, float_set) ? pt::exp_upper : pt::none; | ||||
|   case 'f': | ||||
|     return in(t, float_set) ? pt::fixed_lower : pt::none; | ||||
|   case 'F': | ||||
|     return in(t, float_set) ? pt::fixed_upper : pt::none; | ||||
|   case 'g': | ||||
|     return in(t, float_set) ? pt::general_lower : pt::none; | ||||
|   case 'G': | ||||
|     return in(t, float_set) ? pt::general_upper : pt::none; | ||||
|   case 'c': | ||||
|     return in(t, integral_set) ? pt::chr : pt::none; | ||||
|   case 's': | ||||
|     return in(t, string_set | cstring_set) ? pt::string : pt::none; | ||||
|   case 'p': | ||||
|     return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none; | ||||
|   default: | ||||
|     return pt::none; | ||||
|   case 'd': return in(t, integral_set) ? pt::dec : pt::none; | ||||
|   case 'o': return in(t, integral_set) ? pt::oct : pt::none; | ||||
|   case 'X': upper = true; FMT_FALLTHROUGH; | ||||
|   case 'x': return in(t, integral_set) ? pt::hex : pt::none; | ||||
|   case 'E': upper = true; FMT_FALLTHROUGH; | ||||
|   case 'e': return in(t, float_set) ? pt::exp : pt::none; | ||||
|   case 'F': upper = true; FMT_FALLTHROUGH; | ||||
|   case 'f': return in(t, float_set) ? pt::fixed : pt::none; | ||||
|   case 'G': upper = true; FMT_FALLTHROUGH; | ||||
|   case 'g': return in(t, float_set) ? pt::general : pt::none; | ||||
|   case 'A': upper = true; FMT_FALLTHROUGH; | ||||
|   case 'a': return in(t, float_set) ? pt::hexfloat : pt::none; | ||||
|   case 'c': return in(t, integral_set) ? pt::chr : pt::none; | ||||
|   case 's': return in(t, string_set | cstring_set) ? pt::string : pt::none; | ||||
|   case 'p': return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none; | ||||
|   default:  return pt::none; | ||||
|   } | ||||
| } | ||||
|  | ||||
| template <typename Char, typename Context> | ||||
| void vprintf(buffer<Char>& buf, basic_string_view<Char> format, | ||||
|              basic_format_args<Context> args) { | ||||
|   using iterator = buffer_appender<Char>; | ||||
|   using iterator = basic_appender<Char>; | ||||
|   auto out = iterator(buf); | ||||
|   auto context = basic_printf_context<Char>(out, args); | ||||
|   auto parse_ctx = basic_format_parse_context<Char>(format); | ||||
|   auto parse_ctx = parse_context<Char>(format); | ||||
|  | ||||
|   // Returns the argument with specified index or, if arg_index is -1, the next | ||||
|   // argument. | ||||
| @@ -449,12 +429,12 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format, | ||||
|     } | ||||
|     write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start))); | ||||
|  | ||||
|     auto specs = format_specs<Char>(); | ||||
|     specs.align = align::right; | ||||
|     auto specs = format_specs(); | ||||
|     specs.set_align(align::right); | ||||
|  | ||||
|     // Parse argument index, flags and width. | ||||
|     int arg_index = parse_header(it, end, specs, get_arg); | ||||
|     if (arg_index == 0) throw_format_error("argument not found"); | ||||
|     if (arg_index == 0) report_error("argument not found"); | ||||
|  | ||||
|     // Parse precision. | ||||
|     if (it != end && *it == '.') { | ||||
| @@ -464,8 +444,8 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format, | ||||
|         specs.precision = parse_nonnegative_int(it, end, 0); | ||||
|       } else if (c == '*') { | ||||
|         ++it; | ||||
|         specs.precision = static_cast<int>( | ||||
|             visit_format_arg(printf_precision_handler(), get_arg(-1))); | ||||
|         specs.precision = | ||||
|             static_cast<int>(get_arg(-1).visit(printf_precision_handler())); | ||||
|       } else { | ||||
|         specs.precision = 0; | ||||
|       } | ||||
| @@ -474,25 +454,26 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format, | ||||
|     auto arg = get_arg(arg_index); | ||||
|     // For d, i, o, u, x, and X conversion specifiers, if a precision is | ||||
|     // specified, the '0' flag is ignored | ||||
|     if (specs.precision >= 0 && arg.is_integral()) { | ||||
|     if (specs.precision >= 0 && is_integral_type(arg.type())) { | ||||
|       // Ignore '0' for non-numeric types or if '-' present. | ||||
|       specs.fill[0] = ' '; | ||||
|       specs.set_fill(' '); | ||||
|     } | ||||
|     if (specs.precision >= 0 && arg.type() == type::cstring_type) { | ||||
|       auto str = visit_format_arg(get_cstring<Char>(), arg); | ||||
|       auto str = arg.visit(get_cstring<Char>()); | ||||
|       auto str_end = str + specs.precision; | ||||
|       auto nul = std::find(str, str_end, Char()); | ||||
|       auto sv = basic_string_view<Char>( | ||||
|           str, to_unsigned(nul != str_end ? nul - str : specs.precision)); | ||||
|       arg = make_arg<basic_printf_context<Char>>(sv); | ||||
|       arg = sv; | ||||
|     } | ||||
|     if (specs.alt && visit_format_arg(is_zero_int(), arg)) specs.alt = false; | ||||
|     if (specs.fill[0] == '0') { | ||||
|       if (arg.is_arithmetic() && specs.align != align::left) | ||||
|         specs.align = align::numeric; | ||||
|       else | ||||
|         specs.fill[0] = ' ';  // Ignore '0' flag for non-numeric types or if '-' | ||||
|                               // flag is also present. | ||||
|     if (specs.alt() && arg.visit(is_zero_int())) specs.clear_alt(); | ||||
|     if (specs.fill_unit<Char>() == '0') { | ||||
|       if (is_arithmetic_type(arg.type()) && specs.align() != align::left) { | ||||
|         specs.set_align(align::numeric); | ||||
|       } else { | ||||
|         // Ignore '0' flag for non-numeric types or if '-' flag is also present. | ||||
|         specs.set_fill(' '); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // Parse length and convert the argument to the required type. | ||||
| @@ -517,47 +498,39 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format, | ||||
|         convert_arg<long>(arg, t); | ||||
|       } | ||||
|       break; | ||||
|     case 'j': | ||||
|       convert_arg<intmax_t>(arg, t); | ||||
|       break; | ||||
|     case 'z': | ||||
|       convert_arg<size_t>(arg, t); | ||||
|       break; | ||||
|     case 't': | ||||
|       convert_arg<std::ptrdiff_t>(arg, t); | ||||
|       break; | ||||
|     case 'j': convert_arg<intmax_t>(arg, t); break; | ||||
|     case 'z': convert_arg<size_t>(arg, t); break; | ||||
|     case 't': convert_arg<std::ptrdiff_t>(arg, t); break; | ||||
|     case 'L': | ||||
|       // printf produces garbage when 'L' is omitted for long double, no | ||||
|       // need to do the same. | ||||
|       break; | ||||
|     default: | ||||
|       --it; | ||||
|       convert_arg<void>(arg, c); | ||||
|     default: --it; convert_arg<void>(arg, c); | ||||
|     } | ||||
|  | ||||
|     // Parse type. | ||||
|     if (it == end) throw_format_error("invalid format string"); | ||||
|     if (it == end) report_error("invalid format string"); | ||||
|     char type = static_cast<char>(*it++); | ||||
|     if (arg.is_integral()) { | ||||
|     if (is_integral_type(arg.type())) { | ||||
|       // Normalize type. | ||||
|       switch (type) { | ||||
|       case 'i': | ||||
|       case 'u': | ||||
|         type = 'd'; | ||||
|         break; | ||||
|       case 'u': type = 'd'; break; | ||||
|       case 'c': | ||||
|         visit_format_arg(char_converter<basic_printf_context<Char>>(arg), arg); | ||||
|         arg.visit(char_converter<basic_printf_context<Char>>(arg)); | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|     specs.type = parse_printf_presentation_type(type, arg.type()); | ||||
|     if (specs.type == presentation_type::none) | ||||
|       throw_format_error("invalid format specifier"); | ||||
|     bool upper = false; | ||||
|     specs.set_type(parse_printf_presentation_type(type, arg.type(), upper)); | ||||
|     if (specs.type() == presentation_type::none) | ||||
|       report_error("invalid format specifier"); | ||||
|     if (upper) specs.set_upper(); | ||||
|  | ||||
|     start = it; | ||||
|  | ||||
|     // Format argument. | ||||
|     visit_format_arg(printf_arg_formatter<Char>(out, specs, context), arg); | ||||
|     arg.visit(printf_arg_formatter<Char>(out, specs, context)); | ||||
|   } | ||||
|   write(out, basic_string_view<Char>(start, to_unsigned(it - start))); | ||||
| } | ||||
| @@ -569,56 +542,44 @@ using wprintf_context = basic_printf_context<wchar_t>; | ||||
| using printf_args = basic_format_args<printf_context>; | ||||
| using wprintf_args = basic_format_args<wprintf_context>; | ||||
|  | ||||
| /** | ||||
|   \rst | ||||
|   Constructs an `~fmt::format_arg_store` object that contains references to | ||||
|   arguments and can be implicitly converted to `~fmt::printf_args`. | ||||
|   \endrst | ||||
|  */ | ||||
| template <typename... T> | ||||
| inline auto make_printf_args(const T&... args) | ||||
|     -> format_arg_store<printf_context, T...> { | ||||
|   return {args...}; | ||||
| /// Constructs an `format_arg_store` object that contains references to | ||||
| /// arguments and can be implicitly converted to `printf_args`. | ||||
| template <typename Char = char, typename... T> | ||||
| inline auto make_printf_args(T&... args) | ||||
|     -> decltype(fmt::make_format_args<basic_printf_context<Char>>(args...)) { | ||||
|   return fmt::make_format_args<basic_printf_context<Char>>(args...); | ||||
| } | ||||
|  | ||||
| // DEPRECATED! | ||||
| template <typename... T> | ||||
| inline auto make_wprintf_args(const T&... args) | ||||
|     -> format_arg_store<wprintf_context, T...> { | ||||
|   return {args...}; | ||||
| } | ||||
| template <typename Char> struct vprintf_args { | ||||
|   using type = basic_format_args<basic_printf_context<Char>>; | ||||
| }; | ||||
|  | ||||
| template <typename Char> | ||||
| inline auto vsprintf( | ||||
|     basic_string_view<Char> fmt, | ||||
|     basic_format_args<basic_printf_context<type_identity_t<Char>>> args) | ||||
| inline auto vsprintf(basic_string_view<Char> fmt, | ||||
|                      typename vprintf_args<Char>::type args) | ||||
|     -> std::basic_string<Char> { | ||||
|   auto buf = basic_memory_buffer<Char>(); | ||||
|   detail::vprintf(buf, fmt, args); | ||||
|   return to_string(buf); | ||||
|   return {buf.data(), buf.size()}; | ||||
| } | ||||
|  | ||||
| /** | ||||
|   \rst | ||||
|   Formats arguments and returns the result as a string. | ||||
|  | ||||
|   **Example**:: | ||||
|  | ||||
|     std::string message = fmt::sprintf("The answer is %d", 42); | ||||
|   \endrst | ||||
| */ | ||||
| template <typename S, typename... T, | ||||
|           typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> | ||||
|  * Formats `args` according to specifications in `fmt` and returns the result | ||||
|  * as as string. | ||||
|  * | ||||
|  * **Example**: | ||||
|  * | ||||
|  *     std::string message = fmt::sprintf("The answer is %d", 42); | ||||
|  */ | ||||
| template <typename S, typename... T, typename Char = detail::char_t<S>> | ||||
| inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> { | ||||
|   return vsprintf(detail::to_string_view(fmt), | ||||
|                   fmt::make_format_args<basic_printf_context<Char>>(args...)); | ||||
| } | ||||
|  | ||||
| template <typename Char> | ||||
| inline auto vfprintf( | ||||
|     std::FILE* f, basic_string_view<Char> fmt, | ||||
|     basic_format_args<basic_printf_context<type_identity_t<Char>>> args) | ||||
|     -> int { | ||||
| inline auto vfprintf(std::FILE* f, basic_string_view<Char> fmt, | ||||
|                      typename vprintf_args<Char>::type args) -> int { | ||||
|   auto buf = basic_memory_buffer<Char>(); | ||||
|   detail::vprintf(buf, fmt, args); | ||||
|   size_t size = buf.size(); | ||||
| @@ -628,36 +589,33 @@ inline auto vfprintf( | ||||
| } | ||||
|  | ||||
| /** | ||||
|   \rst | ||||
|   Prints formatted data to the file *f*. | ||||
|  | ||||
|   **Example**:: | ||||
|  | ||||
|     fmt::fprintf(stderr, "Don't %s!", "panic"); | ||||
|   \endrst | ||||
|  * Formats `args` according to specifications in `fmt` and writes the output | ||||
|  * to `f`. | ||||
|  * | ||||
|  * **Example**: | ||||
|  * | ||||
|  *     fmt::fprintf(stderr, "Don't %s!", "panic"); | ||||
|  */ | ||||
| template <typename S, typename... T, typename Char = char_t<S>> | ||||
| template <typename S, typename... T, typename Char = detail::char_t<S>> | ||||
| inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int { | ||||
|   return vfprintf(f, detail::to_string_view(fmt), | ||||
|                   fmt::make_format_args<basic_printf_context<Char>>(args...)); | ||||
|                   make_printf_args<Char>(args...)); | ||||
| } | ||||
|  | ||||
| template <typename Char> | ||||
| FMT_DEPRECATED inline auto vprintf( | ||||
|     basic_string_view<Char> fmt, | ||||
|     basic_format_args<basic_printf_context<type_identity_t<Char>>> args) | ||||
| FMT_DEPRECATED inline auto vprintf(basic_string_view<Char> fmt, | ||||
|                                    typename vprintf_args<Char>::type args) | ||||
|     -> int { | ||||
|   return vfprintf(stdout, fmt, args); | ||||
| } | ||||
|  | ||||
| /** | ||||
|   \rst | ||||
|   Prints formatted data to ``stdout``. | ||||
|  | ||||
|   **Example**:: | ||||
|  | ||||
|     fmt::printf("Elapsed time: %.2f seconds", 1.23); | ||||
|   \endrst | ||||
|  * Formats `args` according to specifications in `fmt` and writes the output | ||||
|  * to `stdout`. | ||||
|  * | ||||
|  * **Example**: | ||||
|  * | ||||
|  *   fmt::printf("Elapsed time: %.2f seconds", 1.23); | ||||
|  */ | ||||
| template <typename... T> | ||||
| inline auto printf(string_view fmt, const T&... args) -> int { | ||||
| @@ -666,7 +624,7 @@ inline auto printf(string_view fmt, const T&... args) -> int { | ||||
| template <typename... T> | ||||
| FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt, | ||||
|                                   const T&... args) -> int { | ||||
|   return vfprintf(stdout, fmt, make_wprintf_args(args...)); | ||||
|   return vfprintf(stdout, fmt, make_printf_args<wchar_t>(args...)); | ||||
| } | ||||
|  | ||||
| FMT_END_EXPORT | ||||
|   | ||||
| @@ -8,67 +8,31 @@ | ||||
| #ifndef FMT_RANGES_H_ | ||||
| #define FMT_RANGES_H_ | ||||
|  | ||||
| #include <initializer_list> | ||||
| #include <tuple> | ||||
| #include <type_traits> | ||||
| #ifndef FMT_MODULE | ||||
| #  include <initializer_list> | ||||
| #  include <iterator> | ||||
| #  include <string> | ||||
| #  include <tuple> | ||||
| #  include <type_traits> | ||||
| #  include <utility> | ||||
| #endif | ||||
|  | ||||
| #include "format.h" | ||||
|  | ||||
| FMT_BEGIN_NAMESPACE | ||||
|  | ||||
| FMT_EXPORT | ||||
| enum class range_format { disabled, map, set, sequence, string, debug_string }; | ||||
|  | ||||
| namespace detail { | ||||
|  | ||||
| template <typename Range, typename OutputIt> | ||||
| auto copy(const Range& range, OutputIt out) -> OutputIt { | ||||
|   for (auto it = range.begin(), end = range.end(); it != end; ++it) | ||||
|     *out++ = *it; | ||||
|   return out; | ||||
| } | ||||
|  | ||||
| template <typename OutputIt> | ||||
| auto copy(const char* str, OutputIt out) -> OutputIt { | ||||
|   while (*str) *out++ = *str++; | ||||
|   return out; | ||||
| } | ||||
|  | ||||
| template <typename OutputIt> auto copy(char ch, OutputIt out) -> OutputIt { | ||||
|   *out++ = ch; | ||||
|   return out; | ||||
| } | ||||
|  | ||||
| template <typename OutputIt> auto copy(wchar_t ch, OutputIt out) -> OutputIt { | ||||
|   *out++ = ch; | ||||
|   return out; | ||||
| } | ||||
|  | ||||
| // Returns true if T has a std::string-like interface, like std::string_view. | ||||
| template <typename T> class is_std_string_like { | ||||
|   template <typename U> | ||||
|   static auto check(U* p) | ||||
|       -> decltype((void)p->find('a'), p->length(), (void)p->data(), int()); | ||||
|   template <typename> static void check(...); | ||||
|  | ||||
|  public: | ||||
|   static constexpr const bool value = | ||||
|       is_string<T>::value || | ||||
|       std::is_convertible<T, std_string_view<char>>::value || | ||||
|       !std::is_void<decltype(check<T>(nullptr))>::value; | ||||
| }; | ||||
|  | ||||
| template <typename Char> | ||||
| struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {}; | ||||
|  | ||||
| template <typename T> class is_map { | ||||
|   template <typename U> static auto check(U*) -> typename U::mapped_type; | ||||
|   template <typename> static void check(...); | ||||
|  | ||||
|  public: | ||||
| #ifdef FMT_FORMAT_MAP_AS_LIST  // DEPRECATED! | ||||
|   static constexpr const bool value = false; | ||||
| #else | ||||
|   static constexpr const bool value = | ||||
|       !std::is_void<decltype(check<T>(nullptr))>::value; | ||||
| #endif | ||||
| }; | ||||
|  | ||||
| template <typename T> class is_set { | ||||
| @@ -76,26 +40,10 @@ template <typename T> class is_set { | ||||
|   template <typename> static void check(...); | ||||
|  | ||||
|  public: | ||||
| #ifdef FMT_FORMAT_SET_AS_LIST  // DEPRECATED! | ||||
|   static constexpr const bool value = false; | ||||
| #else | ||||
|   static constexpr const bool value = | ||||
|       !std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value; | ||||
| #endif | ||||
| }; | ||||
|  | ||||
| template <typename... Ts> struct conditional_helper {}; | ||||
|  | ||||
| template <typename T, typename _ = void> struct is_range_ : std::false_type {}; | ||||
|  | ||||
| #if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800 | ||||
|  | ||||
| #  define FMT_DECLTYPE_RETURN(val)  \ | ||||
|     ->decltype(val) { return val; } \ | ||||
|     static_assert(                  \ | ||||
|         true, "")  // This makes it so that a semicolon is required after the | ||||
|                    // macro, which helps clang-format handle the formatting. | ||||
|  | ||||
| // C array overload | ||||
| template <typename T, std::size_t N> | ||||
| auto range_begin(const T (&arr)[N]) -> const T* { | ||||
| @@ -110,17 +58,21 @@ template <typename T, typename Enable = void> | ||||
| struct has_member_fn_begin_end_t : std::false_type {}; | ||||
|  | ||||
| template <typename T> | ||||
| struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()), | ||||
| struct has_member_fn_begin_end_t<T, void_t<decltype(*std::declval<T>().begin()), | ||||
|                                            decltype(std::declval<T>().end())>> | ||||
|     : std::true_type {}; | ||||
|  | ||||
| // Member function overload | ||||
| // Member function overloads. | ||||
| template <typename T> | ||||
| auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin()); | ||||
| auto range_begin(T&& rng) -> decltype(static_cast<T&&>(rng).begin()) { | ||||
|   return static_cast<T&&>(rng).begin(); | ||||
| } | ||||
| template <typename T> | ||||
| auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end()); | ||||
| auto range_end(T&& rng) -> decltype(static_cast<T&&>(rng).end()) { | ||||
|   return static_cast<T&&>(rng).end(); | ||||
| } | ||||
|  | ||||
| // ADL overload. Only participates in overload resolution if member functions | ||||
| // ADL overloads. Only participate in overload resolution if member functions | ||||
| // are not found. | ||||
| template <typename T> | ||||
| auto range_begin(T&& rng) | ||||
| @@ -141,31 +93,30 @@ struct has_mutable_begin_end : std::false_type {}; | ||||
|  | ||||
| template <typename T> | ||||
| struct has_const_begin_end< | ||||
|     T, | ||||
|     void_t< | ||||
|         decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())), | ||||
|         decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>> | ||||
|     T, void_t<decltype(*detail::range_begin( | ||||
|                   std::declval<const remove_cvref_t<T>&>())), | ||||
|               decltype(detail::range_end( | ||||
|                   std::declval<const remove_cvref_t<T>&>()))>> | ||||
|     : std::true_type {}; | ||||
|  | ||||
| template <typename T> | ||||
| struct has_mutable_begin_end< | ||||
|     T, void_t<decltype(detail::range_begin(std::declval<T>())), | ||||
|               decltype(detail::range_end(std::declval<T>())), | ||||
|     T, void_t<decltype(*detail::range_begin(std::declval<T&>())), | ||||
|               decltype(detail::range_end(std::declval<T&>())), | ||||
|               // the extra int here is because older versions of MSVC don't | ||||
|               // SFINAE properly unless there are distinct types | ||||
|               int>> : std::true_type {}; | ||||
|  | ||||
| template <typename T, typename _ = void> struct is_range_ : std::false_type {}; | ||||
| template <typename T> | ||||
| struct is_range_<T, void> | ||||
|     : std::integral_constant<bool, (has_const_begin_end<T>::value || | ||||
|                                     has_mutable_begin_end<T>::value)> {}; | ||||
| #  undef FMT_DECLTYPE_RETURN | ||||
| #endif | ||||
|  | ||||
| // tuple_size and tuple_element check. | ||||
| template <typename T> class is_tuple_like_ { | ||||
|   template <typename U> | ||||
|   static auto check(U* p) -> decltype(std::tuple_size<U>::value, int()); | ||||
|   template <typename U, typename V = typename std::remove_cv<U>::type> | ||||
|   static auto check(U* p) -> decltype(std::tuple_size<V>::value, 0); | ||||
|   template <typename> static void check(...); | ||||
|  | ||||
|  public: | ||||
| @@ -206,12 +157,13 @@ class is_tuple_formattable_ { | ||||
|   static constexpr const bool value = false; | ||||
| }; | ||||
| template <typename T, typename C> class is_tuple_formattable_<T, C, true> { | ||||
|   template <std::size_t... Is> | ||||
|   static auto check2(index_sequence<Is...>, | ||||
|                      integer_sequence<bool, (Is == Is)...>) -> std::true_type; | ||||
|   static auto check2(...) -> std::false_type; | ||||
|   template <std::size_t... Is> | ||||
|   static auto check(index_sequence<Is...>) -> decltype(check2( | ||||
|   template <size_t... Is> | ||||
|   static auto all_true(index_sequence<Is...>, | ||||
|                        integer_sequence<bool, (Is >= 0)...>) -> std::true_type; | ||||
|   static auto all_true(...) -> std::false_type; | ||||
|  | ||||
|   template <size_t... Is> | ||||
|   static auto check(index_sequence<Is...>) -> decltype(all_true( | ||||
|       index_sequence<Is...>{}, | ||||
|       integer_sequence<bool, | ||||
|                        (is_formattable<typename std::tuple_element<Is, T>::type, | ||||
| @@ -292,21 +244,32 @@ FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set) | ||||
| template <typename Formatter> | ||||
| FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {} | ||||
|  | ||||
| template <typename T> | ||||
| struct range_format_kind_ | ||||
|     : std::integral_constant<range_format, | ||||
|                              std::is_same<uncvref_type<T>, T>::value | ||||
|                                  ? range_format::disabled | ||||
|                              : is_map<T>::value ? range_format::map | ||||
|                              : is_set<T>::value ? range_format::set | ||||
|                                                 : range_format::sequence> {}; | ||||
|  | ||||
| template <range_format K> | ||||
| using range_format_constant = std::integral_constant<range_format, K>; | ||||
|  | ||||
| // These are not generic lambdas for compatibility with C++11. | ||||
| template <typename ParseContext> struct parse_empty_specs { | ||||
| template <typename Char> struct parse_empty_specs { | ||||
|   template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) { | ||||
|     f.parse(ctx); | ||||
|     detail::maybe_set_debug_format(f, true); | ||||
|   } | ||||
|   ParseContext& ctx; | ||||
|   parse_context<Char>& ctx; | ||||
| }; | ||||
| template <typename FormatContext> struct format_tuple_element { | ||||
|   using char_type = typename FormatContext::char_type; | ||||
|  | ||||
|   template <typename T> | ||||
|   void operator()(const formatter<T, char_type>& f, const T& v) { | ||||
|     if (i > 0) | ||||
|       ctx.advance_to(detail::copy_str<char_type>(separator, ctx.out())); | ||||
|     if (i > 0) ctx.advance_to(detail::copy<char_type>(separator, ctx.out())); | ||||
|     ctx.advance_to(f.format(v, ctx)); | ||||
|     ++i; | ||||
|   } | ||||
| @@ -355,66 +318,48 @@ struct formatter<Tuple, Char, | ||||
|     closing_bracket_ = close; | ||||
|   } | ||||
|  | ||||
|   template <typename ParseContext> | ||||
|   FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | ||||
|   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { | ||||
|     auto it = ctx.begin(); | ||||
|     if (it != ctx.end() && *it != '}') | ||||
|       FMT_THROW(format_error("invalid format specifier")); | ||||
|     detail::for_each(formatters_, detail::parse_empty_specs<ParseContext>{ctx}); | ||||
|     auto end = ctx.end(); | ||||
|     if (it != end && detail::to_ascii(*it) == 'n') { | ||||
|       ++it; | ||||
|       set_brackets({}, {}); | ||||
|       set_separator({}); | ||||
|     } | ||||
|     if (it != end && *it != '}') report_error("invalid format specifier"); | ||||
|     ctx.advance_to(it); | ||||
|     detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx}); | ||||
|     return it; | ||||
|   } | ||||
|  | ||||
|   template <typename FormatContext> | ||||
|   auto format(const Tuple& value, FormatContext& ctx) const | ||||
|       -> decltype(ctx.out()) { | ||||
|     ctx.advance_to(detail::copy_str<Char>(opening_bracket_, ctx.out())); | ||||
|     ctx.advance_to(detail::copy<Char>(opening_bracket_, ctx.out())); | ||||
|     detail::for_each2( | ||||
|         formatters_, value, | ||||
|         detail::format_tuple_element<FormatContext>{0, ctx, separator_}); | ||||
|     return detail::copy_str<Char>(closing_bracket_, ctx.out()); | ||||
|     return detail::copy<Char>(closing_bracket_, ctx.out()); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| template <typename T, typename Char> struct is_range { | ||||
|   static constexpr const bool value = | ||||
|       detail::is_range_<T>::value && !detail::is_std_string_like<T>::value && | ||||
|       !std::is_convertible<T, std::basic_string<Char>>::value && | ||||
|       !std::is_convertible<T, detail::std_string_view<Char>>::value; | ||||
|       detail::is_range_<T>::value && !detail::has_to_string_view<T>::value; | ||||
| }; | ||||
|  | ||||
| namespace detail { | ||||
| template <typename Context> struct range_mapper { | ||||
|   using mapper = arg_mapper<Context>; | ||||
|  | ||||
|   template <typename T, | ||||
|             FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)> | ||||
|   static auto map(T&& value) -> T&& { | ||||
|     return static_cast<T&&>(value); | ||||
|   } | ||||
|   template <typename T, | ||||
|             FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)> | ||||
|   static auto map(T&& value) | ||||
|       -> decltype(mapper().map(static_cast<T&&>(value))) { | ||||
|     return mapper().map(static_cast<T&&>(value)); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| template <typename Char, typename Element> | ||||
| using range_formatter_type = | ||||
|     formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map( | ||||
|                   std::declval<Element>()))>, | ||||
|               Char>; | ||||
| using range_formatter_type = formatter<remove_cvref_t<Element>, Char>; | ||||
|  | ||||
| template <typename R> | ||||
| using maybe_const_range = | ||||
|     conditional_t<has_const_begin_end<R>::value, const R, R>; | ||||
|  | ||||
| // Workaround a bug in MSVC 2015 and earlier. | ||||
| #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 | ||||
| template <typename R, typename Char> | ||||
| struct is_formattable_delayed | ||||
|     : is_formattable<uncvref_type<maybe_const_range<R>>, Char> {}; | ||||
| #endif | ||||
| }  // namespace detail | ||||
|  | ||||
| template <typename...> struct conjunction : std::true_type {}; | ||||
| @@ -438,6 +383,24 @@ struct range_formatter< | ||||
|       detail::string_literal<Char, '['>{}; | ||||
|   basic_string_view<Char> closing_bracket_ = | ||||
|       detail::string_literal<Char, ']'>{}; | ||||
|   bool is_debug = false; | ||||
|  | ||||
|   template <typename Output, typename It, typename Sentinel, typename U = T, | ||||
|             FMT_ENABLE_IF(std::is_same<U, Char>::value)> | ||||
|   auto write_debug_string(Output& out, It it, Sentinel end) const -> Output { | ||||
|     auto buf = basic_memory_buffer<Char>(); | ||||
|     for (; it != end; ++it) buf.push_back(*it); | ||||
|     auto specs = format_specs(); | ||||
|     specs.set_type(presentation_type::debug); | ||||
|     return detail::write<Char>( | ||||
|         out, basic_string_view<Char>(buf.data(), buf.size()), specs); | ||||
|   } | ||||
|  | ||||
|   template <typename Output, typename It, typename Sentinel, typename U = T, | ||||
|             FMT_ENABLE_IF(!std::is_same<U, Char>::value)> | ||||
|   auto write_debug_string(Output& out, It, Sentinel) const -> Output { | ||||
|     return out; | ||||
|   } | ||||
|  | ||||
|  public: | ||||
|   FMT_CONSTEXPR range_formatter() {} | ||||
| @@ -456,21 +419,40 @@ struct range_formatter< | ||||
|     closing_bracket_ = close; | ||||
|   } | ||||
|  | ||||
|   template <typename ParseContext> | ||||
|   FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | ||||
|   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { | ||||
|     auto it = ctx.begin(); | ||||
|     auto end = ctx.end(); | ||||
|     detail::maybe_set_debug_format(underlying_, true); | ||||
|     if (it == end) return underlying_.parse(ctx); | ||||
|  | ||||
|     if (it != end && *it == 'n') { | ||||
|     switch (detail::to_ascii(*it)) { | ||||
|     case 'n': | ||||
|       set_brackets({}, {}); | ||||
|       ++it; | ||||
|       break; | ||||
|     case '?': | ||||
|       is_debug = true; | ||||
|       set_brackets({}, {}); | ||||
|       ++it; | ||||
|       if (it == end || *it != 's') report_error("invalid format specifier"); | ||||
|       FMT_FALLTHROUGH; | ||||
|     case 's': | ||||
|       if (!std::is_same<T, Char>::value) | ||||
|         report_error("invalid format specifier"); | ||||
|       if (!is_debug) { | ||||
|         set_brackets(detail::string_literal<Char, '"'>{}, | ||||
|                      detail::string_literal<Char, '"'>{}); | ||||
|         set_separator({}); | ||||
|         detail::maybe_set_debug_format(underlying_, false); | ||||
|       } | ||||
|       ++it; | ||||
|       return it; | ||||
|     } | ||||
|  | ||||
|     if (it != end && *it != '}') { | ||||
|       if (*it != ':') FMT_THROW(format_error("invalid format specifier")); | ||||
|       if (*it != ':') report_error("invalid format specifier"); | ||||
|       detail::maybe_set_debug_format(underlying_, false); | ||||
|       ++it; | ||||
|     } else { | ||||
|       detail::maybe_set_debug_format(underlying_, true); | ||||
|     } | ||||
|  | ||||
|     ctx.advance_to(it); | ||||
| @@ -479,80 +461,26 @@ struct range_formatter< | ||||
|  | ||||
|   template <typename R, typename FormatContext> | ||||
|   auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { | ||||
|     detail::range_mapper<buffer_context<Char>> mapper; | ||||
|     auto out = ctx.out(); | ||||
|     out = detail::copy_str<Char>(opening_bracket_, out); | ||||
|     int i = 0; | ||||
|     auto it = detail::range_begin(range); | ||||
|     auto end = detail::range_end(range); | ||||
|     if (is_debug) return write_debug_string(out, std::move(it), end); | ||||
|  | ||||
|     out = detail::copy<Char>(opening_bracket_, out); | ||||
|     int i = 0; | ||||
|     for (; it != end; ++it) { | ||||
|       if (i > 0) out = detail::copy_str<Char>(separator_, out); | ||||
|       if (i > 0) out = detail::copy<Char>(separator_, out); | ||||
|       ctx.advance_to(out); | ||||
|       auto&& item = *it; | ||||
|       out = underlying_.format(mapper.map(item), ctx); | ||||
|       auto&& item = *it;  // Need an lvalue | ||||
|       out = underlying_.format(item, ctx); | ||||
|       ++i; | ||||
|     } | ||||
|     out = detail::copy_str<Char>(closing_bracket_, out); | ||||
|     out = detail::copy<Char>(closing_bracket_, out); | ||||
|     return out; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| enum class range_format { disabled, map, set, sequence, string, debug_string }; | ||||
|  | ||||
| namespace detail { | ||||
| template <typename T> | ||||
| struct range_format_kind_ | ||||
|     : std::integral_constant<range_format, | ||||
|                              std::is_same<uncvref_type<T>, T>::value | ||||
|                                  ? range_format::disabled | ||||
|                              : is_map<T>::value ? range_format::map | ||||
|                              : is_set<T>::value ? range_format::set | ||||
|                                                 : range_format::sequence> {}; | ||||
|  | ||||
| template <range_format K, typename R, typename Char, typename Enable = void> | ||||
| struct range_default_formatter; | ||||
|  | ||||
| template <range_format K> | ||||
| using range_format_constant = std::integral_constant<range_format, K>; | ||||
|  | ||||
| template <range_format K, typename R, typename Char> | ||||
| struct range_default_formatter< | ||||
|     K, R, Char, | ||||
|     enable_if_t<(K == range_format::sequence || K == range_format::map || | ||||
|                  K == range_format::set)>> { | ||||
|   using range_type = detail::maybe_const_range<R>; | ||||
|   range_formatter<detail::uncvref_type<range_type>, Char> underlying_; | ||||
|  | ||||
|   FMT_CONSTEXPR range_default_formatter() { init(range_format_constant<K>()); } | ||||
|  | ||||
|   FMT_CONSTEXPR void init(range_format_constant<range_format::set>) { | ||||
|     underlying_.set_brackets(detail::string_literal<Char, '{'>{}, | ||||
|                              detail::string_literal<Char, '}'>{}); | ||||
|   } | ||||
|  | ||||
|   FMT_CONSTEXPR void init(range_format_constant<range_format::map>) { | ||||
|     underlying_.set_brackets(detail::string_literal<Char, '{'>{}, | ||||
|                              detail::string_literal<Char, '}'>{}); | ||||
|     underlying_.underlying().set_brackets({}, {}); | ||||
|     underlying_.underlying().set_separator( | ||||
|         detail::string_literal<Char, ':', ' '>{}); | ||||
|   } | ||||
|  | ||||
|   FMT_CONSTEXPR void init(range_format_constant<range_format::sequence>) {} | ||||
|  | ||||
|   template <typename ParseContext> | ||||
|   FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | ||||
|     return underlying_.parse(ctx); | ||||
|   } | ||||
|  | ||||
|   template <typename FormatContext> | ||||
|   auto format(range_type& range, FormatContext& ctx) const | ||||
|       -> decltype(ctx.out()) { | ||||
|     return underlying_.format(range, ctx); | ||||
|   } | ||||
| }; | ||||
| }  // namespace detail | ||||
|  | ||||
| FMT_EXPORT | ||||
| template <typename T, typename Char, typename Enable = void> | ||||
| struct range_format_kind | ||||
|     : conditional_t< | ||||
| @@ -562,23 +490,191 @@ struct range_format_kind | ||||
| template <typename R, typename Char> | ||||
| struct formatter< | ||||
|     R, Char, | ||||
|     enable_if_t<conjunction<bool_constant<range_format_kind<R, Char>::value != | ||||
|                                           range_format::disabled> | ||||
| // Workaround a bug in MSVC 2015 and earlier. | ||||
| #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 | ||||
|                             , | ||||
|                             detail::is_formattable_delayed<R, Char> | ||||
| #endif | ||||
|                             >::value>> | ||||
|     : detail::range_default_formatter<range_format_kind<R, Char>::value, R, | ||||
|                                       Char> { | ||||
|     enable_if_t<conjunction< | ||||
|         bool_constant< | ||||
|             range_format_kind<R, Char>::value != range_format::disabled && | ||||
|             range_format_kind<R, Char>::value != range_format::map && | ||||
|             range_format_kind<R, Char>::value != range_format::string && | ||||
|             range_format_kind<R, Char>::value != range_format::debug_string>, | ||||
|         detail::is_formattable_delayed<R, Char>>::value>> { | ||||
|  private: | ||||
|   using range_type = detail::maybe_const_range<R>; | ||||
|   range_formatter<detail::uncvref_type<range_type>, Char> range_formatter_; | ||||
|  | ||||
|  public: | ||||
|   using nonlocking = void; | ||||
|  | ||||
|   FMT_CONSTEXPR formatter() { | ||||
|     if (detail::const_check(range_format_kind<R, Char>::value != | ||||
|                             range_format::set)) | ||||
|       return; | ||||
|     range_formatter_.set_brackets(detail::string_literal<Char, '{'>{}, | ||||
|                                   detail::string_literal<Char, '}'>{}); | ||||
|   } | ||||
|  | ||||
|   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { | ||||
|     return range_formatter_.parse(ctx); | ||||
|   } | ||||
|  | ||||
|   template <typename FormatContext> | ||||
|   auto format(range_type& range, FormatContext& ctx) const | ||||
|       -> decltype(ctx.out()) { | ||||
|     return range_formatter_.format(range, ctx); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| template <typename Char, typename... T> struct tuple_join_view : detail::view { | ||||
|   const std::tuple<T...>& tuple; | ||||
| // A map formatter. | ||||
| template <typename R, typename Char> | ||||
| struct formatter< | ||||
|     R, Char, | ||||
|     enable_if_t<conjunction< | ||||
|         bool_constant<range_format_kind<R, Char>::value == range_format::map>, | ||||
|         detail::is_formattable_delayed<R, Char>>::value>> { | ||||
|  private: | ||||
|   using map_type = detail::maybe_const_range<R>; | ||||
|   using element_type = detail::uncvref_type<map_type>; | ||||
|  | ||||
|   decltype(detail::tuple::get_formatters<element_type, Char>( | ||||
|       detail::tuple_index_sequence<element_type>())) formatters_; | ||||
|   bool no_delimiters_ = false; | ||||
|  | ||||
|  public: | ||||
|   FMT_CONSTEXPR formatter() {} | ||||
|  | ||||
|   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { | ||||
|     auto it = ctx.begin(); | ||||
|     auto end = ctx.end(); | ||||
|     if (it != end) { | ||||
|       if (detail::to_ascii(*it) == 'n') { | ||||
|         no_delimiters_ = true; | ||||
|         ++it; | ||||
|       } | ||||
|       if (it != end && *it != '}') { | ||||
|         if (*it != ':') report_error("invalid format specifier"); | ||||
|         ++it; | ||||
|       } | ||||
|       ctx.advance_to(it); | ||||
|     } | ||||
|     detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx}); | ||||
|     return it; | ||||
|   } | ||||
|  | ||||
|   template <typename FormatContext> | ||||
|   auto format(map_type& map, FormatContext& ctx) const -> decltype(ctx.out()) { | ||||
|     auto out = ctx.out(); | ||||
|     basic_string_view<Char> open = detail::string_literal<Char, '{'>{}; | ||||
|     if (!no_delimiters_) out = detail::copy<Char>(open, out); | ||||
|     int i = 0; | ||||
|     basic_string_view<Char> sep = detail::string_literal<Char, ',', ' '>{}; | ||||
|     for (auto&& value : map) { | ||||
|       if (i > 0) out = detail::copy<Char>(sep, out); | ||||
|       ctx.advance_to(out); | ||||
|       detail::for_each2(formatters_, value, | ||||
|                         detail::format_tuple_element<FormatContext>{ | ||||
|                             0, ctx, detail::string_literal<Char, ':', ' '>{}}); | ||||
|       ++i; | ||||
|     } | ||||
|     basic_string_view<Char> close = detail::string_literal<Char, '}'>{}; | ||||
|     if (!no_delimiters_) out = detail::copy<Char>(close, out); | ||||
|     return out; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| // A (debug_)string formatter. | ||||
| template <typename R, typename Char> | ||||
| struct formatter< | ||||
|     R, Char, | ||||
|     enable_if_t<range_format_kind<R, Char>::value == range_format::string || | ||||
|                 range_format_kind<R, Char>::value == | ||||
|                     range_format::debug_string>> { | ||||
|  private: | ||||
|   using range_type = detail::maybe_const_range<R>; | ||||
|   using string_type = | ||||
|       conditional_t<std::is_constructible< | ||||
|                         detail::std_string_view<Char>, | ||||
|                         decltype(detail::range_begin(std::declval<R>())), | ||||
|                         decltype(detail::range_end(std::declval<R>()))>::value, | ||||
|                     detail::std_string_view<Char>, std::basic_string<Char>>; | ||||
|  | ||||
|   formatter<string_type, Char> underlying_; | ||||
|  | ||||
|  public: | ||||
|   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { | ||||
|     return underlying_.parse(ctx); | ||||
|   } | ||||
|  | ||||
|   template <typename FormatContext> | ||||
|   auto format(range_type& range, FormatContext& ctx) const | ||||
|       -> decltype(ctx.out()) { | ||||
|     auto out = ctx.out(); | ||||
|     if (detail::const_check(range_format_kind<R, Char>::value == | ||||
|                             range_format::debug_string)) | ||||
|       *out++ = '"'; | ||||
|     out = underlying_.format( | ||||
|         string_type{detail::range_begin(range), detail::range_end(range)}, ctx); | ||||
|     if (detail::const_check(range_format_kind<R, Char>::value == | ||||
|                             range_format::debug_string)) | ||||
|       *out++ = '"'; | ||||
|     return out; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| template <typename It, typename Sentinel, typename Char = char> | ||||
| struct join_view : detail::view { | ||||
|   It begin; | ||||
|   Sentinel end; | ||||
|   basic_string_view<Char> sep; | ||||
|  | ||||
|   tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s) | ||||
|   join_view(It b, Sentinel e, basic_string_view<Char> s) | ||||
|       : begin(std::move(b)), end(e), sep(s) {} | ||||
| }; | ||||
|  | ||||
| template <typename It, typename Sentinel, typename Char> | ||||
| struct formatter<join_view<It, Sentinel, Char>, Char> { | ||||
|  private: | ||||
|   using value_type = | ||||
| #ifdef __cpp_lib_ranges | ||||
|       std::iter_value_t<It>; | ||||
| #else | ||||
|       typename std::iterator_traits<It>::value_type; | ||||
| #endif | ||||
|   formatter<remove_cvref_t<value_type>, Char> value_formatter_; | ||||
|  | ||||
|   using view = conditional_t<std::is_copy_constructible<It>::value, | ||||
|                              const join_view<It, Sentinel, Char>, | ||||
|                              join_view<It, Sentinel, Char>>; | ||||
|  | ||||
|  public: | ||||
|   using nonlocking = void; | ||||
|  | ||||
|   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { | ||||
|     return value_formatter_.parse(ctx); | ||||
|   } | ||||
|  | ||||
|   template <typename FormatContext> | ||||
|   auto format(view& value, FormatContext& ctx) const -> decltype(ctx.out()) { | ||||
|     using iter = | ||||
|         conditional_t<std::is_copy_constructible<view>::value, It, It&>; | ||||
|     iter it = value.begin; | ||||
|     auto out = ctx.out(); | ||||
|     if (it == value.end) return out; | ||||
|     out = value_formatter_.format(*it, ctx); | ||||
|     ++it; | ||||
|     while (it != value.end) { | ||||
|       out = detail::copy<Char>(value.sep.begin(), value.sep.end(), out); | ||||
|       ctx.advance_to(out); | ||||
|       out = value_formatter_.format(*it, ctx); | ||||
|       ++it; | ||||
|     } | ||||
|     return out; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| template <typename Char, typename Tuple> struct tuple_join_view : detail::view { | ||||
|   const Tuple& tuple; | ||||
|   basic_string_view<Char> sep; | ||||
|  | ||||
|   tuple_join_view(const Tuple& t, basic_string_view<Char> s) | ||||
|       : tuple(t), sep{s} {} | ||||
| }; | ||||
|  | ||||
| @@ -589,65 +685,64 @@ template <typename Char, typename... T> struct tuple_join_view : detail::view { | ||||
| #  define FMT_TUPLE_JOIN_SPECIFIERS 0 | ||||
| #endif | ||||
|  | ||||
| template <typename Char, typename... T> | ||||
| struct formatter<tuple_join_view<Char, T...>, Char> { | ||||
|   template <typename ParseContext> | ||||
|   FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | ||||
|     return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>()); | ||||
| template <typename Char, typename Tuple> | ||||
| struct formatter<tuple_join_view<Char, Tuple>, Char, | ||||
|                  enable_if_t<is_tuple_like<Tuple>::value>> { | ||||
|   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { | ||||
|     return do_parse(ctx, std::tuple_size<Tuple>()); | ||||
|   } | ||||
|  | ||||
|   template <typename FormatContext> | ||||
|   auto format(const tuple_join_view<Char, T...>& value, | ||||
|   auto format(const tuple_join_view<Char, Tuple>& value, | ||||
|               FormatContext& ctx) const -> typename FormatContext::iterator { | ||||
|     return do_format(value, ctx, | ||||
|                      std::integral_constant<size_t, sizeof...(T)>()); | ||||
|     return do_format(value, ctx, std::tuple_size<Tuple>()); | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_; | ||||
|   decltype(detail::tuple::get_formatters<Tuple, Char>( | ||||
|       detail::tuple_index_sequence<Tuple>())) formatters_; | ||||
|  | ||||
|   template <typename ParseContext> | ||||
|   FMT_CONSTEXPR auto do_parse(ParseContext& ctx, | ||||
|   FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx, | ||||
|                               std::integral_constant<size_t, 0>) | ||||
|       -> decltype(ctx.begin()) { | ||||
|       -> const Char* { | ||||
|     return ctx.begin(); | ||||
|   } | ||||
|  | ||||
|   template <typename ParseContext, size_t N> | ||||
|   FMT_CONSTEXPR auto do_parse(ParseContext& ctx, | ||||
|   template <size_t N> | ||||
|   FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx, | ||||
|                               std::integral_constant<size_t, N>) | ||||
|       -> decltype(ctx.begin()) { | ||||
|       -> const Char* { | ||||
|     auto end = ctx.begin(); | ||||
| #if FMT_TUPLE_JOIN_SPECIFIERS | ||||
|     end = std::get<sizeof...(T) - N>(formatters_).parse(ctx); | ||||
|     end = std::get<std::tuple_size<Tuple>::value - N>(formatters_).parse(ctx); | ||||
|     if (N > 1) { | ||||
|       auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>()); | ||||
|       if (end != end1) | ||||
|         FMT_THROW(format_error("incompatible format specs for tuple elements")); | ||||
|         report_error("incompatible format specs for tuple elements"); | ||||
|     } | ||||
| #endif | ||||
|     return end; | ||||
|   } | ||||
|  | ||||
|   template <typename FormatContext> | ||||
|   auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx, | ||||
|   auto do_format(const tuple_join_view<Char, Tuple>&, FormatContext& ctx, | ||||
|                  std::integral_constant<size_t, 0>) const -> | ||||
|       typename FormatContext::iterator { | ||||
|     return ctx.out(); | ||||
|   } | ||||
|  | ||||
|   template <typename FormatContext, size_t N> | ||||
|   auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx, | ||||
|   auto do_format(const tuple_join_view<Char, Tuple>& value, FormatContext& ctx, | ||||
|                  std::integral_constant<size_t, N>) const -> | ||||
|       typename FormatContext::iterator { | ||||
|     auto out = std::get<sizeof...(T) - N>(formatters_) | ||||
|                    .format(std::get<sizeof...(T) - N>(value.tuple), ctx); | ||||
|     if (N > 1) { | ||||
|       out = std::copy(value.sep.begin(), value.sep.end(), out); | ||||
|       ctx.advance_to(out); | ||||
|       return do_format(value, ctx, std::integral_constant<size_t, N - 1>()); | ||||
|     } | ||||
|     return out; | ||||
|     using std::get; | ||||
|     auto out = | ||||
|         std::get<std::tuple_size<Tuple>::value - N>(formatters_) | ||||
|             .format(get<std::tuple_size<Tuple>::value - N>(value.tuple), ctx); | ||||
|     if (N <= 1) return out; | ||||
|     out = detail::copy<Char>(value.sep, out); | ||||
|     ctx.advance_to(out); | ||||
|     return do_format(value, ctx, std::integral_constant<size_t, N - 1>()); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| @@ -691,40 +786,57 @@ struct formatter< | ||||
|  | ||||
| FMT_BEGIN_EXPORT | ||||
|  | ||||
| /// Returns a view that formats the iterator range `[begin, end)` with elements | ||||
| /// separated by `sep`. | ||||
| template <typename It, typename Sentinel> | ||||
| auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> { | ||||
|   return {std::move(begin), end, sep}; | ||||
| } | ||||
|  | ||||
| /** | ||||
|   \rst | ||||
|   Returns an object that formats `tuple` with elements separated by `sep`. | ||||
|  | ||||
|   **Example**:: | ||||
|  | ||||
|     std::tuple<int, char> t = {1, 'a'}; | ||||
|     fmt::print("{}", fmt::join(t, ", ")); | ||||
|     // Output: "1, a" | ||||
|   \endrst | ||||
|  * Returns a view that formats `range` with elements separated by `sep`. | ||||
|  * | ||||
|  * **Example**: | ||||
|  * | ||||
|  *     auto v = std::vector<int>{1, 2, 3}; | ||||
|  *     fmt::print("{}", fmt::join(v, ", ")); | ||||
|  *     // Output: 1, 2, 3 | ||||
|  * | ||||
|  * `fmt::join` applies passed format specifiers to the range elements: | ||||
|  * | ||||
|  *     fmt::print("{:02}", fmt::join(v, ", ")); | ||||
|  *     // Output: 01, 02, 03 | ||||
|  */ | ||||
| template <typename... T> | ||||
| FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep) | ||||
|     -> tuple_join_view<char, T...> { | ||||
|   return {tuple, sep}; | ||||
| template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)> | ||||
| auto join(Range&& r, string_view sep) | ||||
|     -> join_view<decltype(detail::range_begin(r)), | ||||
|                  decltype(detail::range_end(r))> { | ||||
|   return {detail::range_begin(r), detail::range_end(r), sep}; | ||||
| } | ||||
|  | ||||
| template <typename... T> | ||||
| FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, | ||||
|                         basic_string_view<wchar_t> sep) | ||||
|     -> tuple_join_view<wchar_t, T...> { | ||||
| /** | ||||
|  * Returns an object that formats `std::tuple` with elements separated by `sep`. | ||||
|  * | ||||
|  * **Example**: | ||||
|  * | ||||
|  *     auto t = std::tuple<int, char>{1, 'a'}; | ||||
|  *     fmt::print("{}", fmt::join(t, ", ")); | ||||
|  *     // Output: 1, a | ||||
|  */ | ||||
| template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)> | ||||
| FMT_CONSTEXPR auto join(const Tuple& tuple, string_view sep) | ||||
|     -> tuple_join_view<char, Tuple> { | ||||
|   return {tuple, sep}; | ||||
| } | ||||
|  | ||||
| /** | ||||
|   \rst | ||||
|   Returns an object that formats `initializer_list` with elements separated by | ||||
|   `sep`. | ||||
|  | ||||
|   **Example**:: | ||||
|  | ||||
|     fmt::print("{}", fmt::join({1, 2, 3}, ", ")); | ||||
|     // Output: "1, 2, 3" | ||||
|   \endrst | ||||
|  * Returns an object that formats `std::initializer_list` with elements | ||||
|  * separated by `sep`. | ||||
|  * | ||||
|  * **Example**: | ||||
|  * | ||||
|  *     fmt::print("{}", fmt::join({1, 2, 3}, ", ")); | ||||
|  *     // Output: "1, 2, 3" | ||||
|  */ | ||||
| template <typename T> | ||||
| auto join(std::initializer_list<T> list, string_view sep) | ||||
|   | ||||
| @@ -8,39 +8,49 @@ | ||||
| #ifndef FMT_STD_H_ | ||||
| #define FMT_STD_H_ | ||||
|  | ||||
| #include <atomic> | ||||
| #include <bitset> | ||||
| #include <cstdlib> | ||||
| #include <exception> | ||||
| #include <memory> | ||||
| #include <thread> | ||||
| #include <type_traits> | ||||
| #include <typeinfo> | ||||
| #include <utility> | ||||
| #include <vector> | ||||
|  | ||||
| #include "format.h" | ||||
| #include "ostream.h" | ||||
|  | ||||
| #ifndef FMT_MODULE | ||||
| #  include <atomic> | ||||
| #  include <bitset> | ||||
| #  include <complex> | ||||
| #  include <cstdlib> | ||||
| #  include <exception> | ||||
| #  include <functional> | ||||
| #  include <memory> | ||||
| #  include <thread> | ||||
| #  include <type_traits> | ||||
| #  include <typeinfo> | ||||
| #  include <utility> | ||||
| #  include <vector> | ||||
|  | ||||
| // Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC. | ||||
| #  if FMT_CPLUSPLUS >= 201703L | ||||
| #    if FMT_HAS_INCLUDE(<filesystem>) && \ | ||||
|         (!defined(FMT_CPP_LIB_FILESYSTEM) || FMT_CPP_LIB_FILESYSTEM != 0) | ||||
| #      include <filesystem> | ||||
| #    endif | ||||
| #    if FMT_HAS_INCLUDE(<variant>) | ||||
| #      include <variant> | ||||
| #    endif | ||||
| #    if FMT_HAS_INCLUDE(<optional>) | ||||
| #      include <optional> | ||||
| #    endif | ||||
| #  endif | ||||
| // Use > instead of >= in the version check because <source_location> may be | ||||
| // available after C++17 but before C++20 is marked as implemented. | ||||
| #  if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<source_location>) | ||||
| #    include <source_location> | ||||
| #  endif | ||||
| #  if FMT_CPLUSPLUS > 202002L && FMT_HAS_INCLUDE(<expected>) | ||||
| #    include <expected> | ||||
| #  endif | ||||
| #endif  // FMT_MODULE | ||||
|  | ||||
| #if FMT_HAS_INCLUDE(<version>) | ||||
| #  include <version> | ||||
| #endif | ||||
| // Checking FMT_CPLUSPLUS for warning suppression in MSVC. | ||||
| #if FMT_CPLUSPLUS >= 201703L | ||||
| #  if FMT_HAS_INCLUDE(<filesystem>) | ||||
| #    include <filesystem> | ||||
| #  endif | ||||
| #  if FMT_HAS_INCLUDE(<variant>) | ||||
| #    include <variant> | ||||
| #  endif | ||||
| #  if FMT_HAS_INCLUDE(<optional>) | ||||
| #    include <optional> | ||||
| #  endif | ||||
| #endif | ||||
|  | ||||
| #if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<source_location>) | ||||
| #  include <source_location> | ||||
| #endif | ||||
|  | ||||
| // GCC 4 does not support FMT_HAS_INCLUDE. | ||||
| #if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__) | ||||
| @@ -52,17 +62,6 @@ | ||||
| #  endif | ||||
| #endif | ||||
|  | ||||
| // Check if typeid is available. | ||||
| #ifndef FMT_USE_TYPEID | ||||
| // __RTTI is for EDG compilers. In MSVC typeid is available without RTTI. | ||||
| #  if defined(__GXX_RTTI) || FMT_HAS_FEATURE(cxx_rtti) || FMT_MSC_VERSION || \ | ||||
|       defined(__INTEL_RTTI__) || defined(__RTTI) | ||||
| #    define FMT_USE_TYPEID 1 | ||||
| #  else | ||||
| #    define FMT_USE_TYPEID 0 | ||||
| #  endif | ||||
| #endif | ||||
|  | ||||
| // For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined. | ||||
| #ifndef FMT_CPP_LIB_FILESYSTEM | ||||
| #  ifdef __cpp_lib_filesystem | ||||
| @@ -117,7 +116,7 @@ void write_escaped_path(basic_memory_buffer<Char>& quoted, | ||||
| FMT_EXPORT | ||||
| template <typename Char> struct formatter<std::filesystem::path, Char> { | ||||
|  private: | ||||
|   format_specs<Char> specs_; | ||||
|   format_specs specs_; | ||||
|   detail::arg_ref<Char> width_ref_; | ||||
|   bool debug_ = false; | ||||
|   char path_type_ = 0; | ||||
| @@ -125,33 +124,33 @@ template <typename Char> struct formatter<std::filesystem::path, Char> { | ||||
|  public: | ||||
|   FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; } | ||||
|  | ||||
|   template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) { | ||||
|   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) { | ||||
|     auto it = ctx.begin(), end = ctx.end(); | ||||
|     if (it == end) return it; | ||||
|  | ||||
|     it = detail::parse_align(it, end, specs_); | ||||
|     if (it == end) return it; | ||||
|  | ||||
|     it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx); | ||||
|     Char c = *it; | ||||
|     if ((c >= '0' && c <= '9') || c == '{') | ||||
|       it = detail::parse_width(it, end, specs_, width_ref_, ctx); | ||||
|     if (it != end && *it == '?') { | ||||
|       debug_ = true; | ||||
|       ++it; | ||||
|     } | ||||
|     if (it != end && (*it == 'g')) path_type_ = *it++; | ||||
|     if (it != end && (*it == 'g')) path_type_ = detail::to_ascii(*it++); | ||||
|     return it; | ||||
|   } | ||||
|  | ||||
|   template <typename FormatContext> | ||||
|   auto format(const std::filesystem::path& p, FormatContext& ctx) const { | ||||
|     auto specs = specs_; | ||||
| #  ifdef _WIN32 | ||||
|     auto path_string = !path_type_ ? p.native() : p.generic_wstring(); | ||||
| #  else | ||||
|     auto path_string = !path_type_ ? p.native() : p.generic_string(); | ||||
| #  endif | ||||
|     auto path_string = | ||||
|         !path_type_ ? p.native() | ||||
|                     : p.generic_string<std::filesystem::path::value_type>(); | ||||
|  | ||||
|     detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_, | ||||
|                                                        ctx); | ||||
|     detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, | ||||
|                                 ctx); | ||||
|     if (!debug_) { | ||||
|       auto s = detail::get_path_string<Char>(p, path_string); | ||||
|       return detail::write(ctx.out(), basic_string_view<Char>(s), specs); | ||||
| @@ -163,13 +162,30 @@ template <typename Char> struct formatter<std::filesystem::path, Char> { | ||||
|                          specs); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| class path : public std::filesystem::path { | ||||
|  public: | ||||
|   auto display_string() const -> std::string { | ||||
|     const std::filesystem::path& base = *this; | ||||
|     return fmt::format(FMT_STRING("{}"), base); | ||||
|   } | ||||
|   auto system_string() const -> std::string { return string(); } | ||||
|  | ||||
|   auto generic_display_string() const -> std::string { | ||||
|     const std::filesystem::path& base = *this; | ||||
|     return fmt::format(FMT_STRING("{:g}"), base); | ||||
|   } | ||||
|   auto generic_system_string() const -> std::string { return generic_string(); } | ||||
| }; | ||||
|  | ||||
| FMT_END_NAMESPACE | ||||
| #endif  // FMT_CPP_LIB_FILESYSTEM | ||||
|  | ||||
| FMT_BEGIN_NAMESPACE | ||||
| FMT_EXPORT | ||||
| template <std::size_t N, typename Char> | ||||
| struct formatter<std::bitset<N>, Char> : nested_formatter<string_view> { | ||||
| struct formatter<std::bitset<N>, Char> | ||||
|     : nested_formatter<basic_string_view<Char>, Char> { | ||||
|  private: | ||||
|   // Functor because C++11 doesn't support generic lambdas. | ||||
|   struct writer { | ||||
| @@ -189,7 +205,7 @@ struct formatter<std::bitset<N>, Char> : nested_formatter<string_view> { | ||||
|   template <typename FormatContext> | ||||
|   auto format(const std::bitset<N>& bs, FormatContext& ctx) const | ||||
|       -> decltype(ctx.out()) { | ||||
|     return write_padded(ctx, writer{bs}); | ||||
|     return this->write_padded(ctx, writer{bs}); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| @@ -222,7 +238,7 @@ struct formatter<std::optional<T>, Char, | ||||
|   FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {} | ||||
|  | ||||
|  public: | ||||
|   template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) { | ||||
|   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) { | ||||
|     maybe_set_debug_format(underlying_, true); | ||||
|     return underlying_.parse(ctx); | ||||
|   } | ||||
| @@ -242,13 +258,62 @@ struct formatter<std::optional<T>, Char, | ||||
| FMT_END_NAMESPACE | ||||
| #endif  // __cpp_lib_optional | ||||
|  | ||||
| #if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT | ||||
|  | ||||
| FMT_BEGIN_NAMESPACE | ||||
| namespace detail { | ||||
|  | ||||
| template <typename Char, typename OutputIt, typename T> | ||||
| auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt { | ||||
|   if constexpr (has_to_string_view<T>::value) | ||||
|     return write_escaped_string<Char>(out, detail::to_string_view(v)); | ||||
|   if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v); | ||||
|   return write<Char>(out, v); | ||||
| } | ||||
|  | ||||
| }  // namespace detail | ||||
|  | ||||
| FMT_END_NAMESPACE | ||||
| #endif | ||||
|  | ||||
| #ifdef __cpp_lib_expected | ||||
| FMT_BEGIN_NAMESPACE | ||||
|  | ||||
| FMT_EXPORT | ||||
| template <typename T, typename E, typename Char> | ||||
| struct formatter<std::expected<T, E>, Char, | ||||
|                  std::enable_if_t<(std::is_void<T>::value || | ||||
|                                    is_formattable<T, Char>::value) && | ||||
|                                   is_formattable<E, Char>::value>> { | ||||
|   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { | ||||
|     return ctx.begin(); | ||||
|   } | ||||
|  | ||||
|   template <typename FormatContext> | ||||
|   auto format(const std::expected<T, E>& value, FormatContext& ctx) const | ||||
|       -> decltype(ctx.out()) { | ||||
|     auto out = ctx.out(); | ||||
|  | ||||
|     if (value.has_value()) { | ||||
|       out = detail::write<Char>(out, "expected("); | ||||
|       if constexpr (!std::is_void<T>::value) | ||||
|         out = detail::write_escaped_alternative<Char>(out, *value); | ||||
|     } else { | ||||
|       out = detail::write<Char>(out, "unexpected("); | ||||
|       out = detail::write_escaped_alternative<Char>(out, value.error()); | ||||
|     } | ||||
|     *out++ = ')'; | ||||
|     return out; | ||||
|   } | ||||
| }; | ||||
| FMT_END_NAMESPACE | ||||
| #endif  // __cpp_lib_expected | ||||
|  | ||||
| #ifdef __cpp_lib_source_location | ||||
| FMT_BEGIN_NAMESPACE | ||||
| FMT_EXPORT | ||||
| template <> struct formatter<std::source_location> { | ||||
|   template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) { | ||||
|     return ctx.begin(); | ||||
|   } | ||||
|   FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); } | ||||
|  | ||||
|   template <typename FormatContext> | ||||
|   auto format(const std::source_location& loc, FormatContext& ctx) const | ||||
| @@ -291,16 +356,6 @@ template <typename T, typename C> class is_variant_formattable_ { | ||||
|       decltype(check(variant_index_sequence<T>{}))::value; | ||||
| }; | ||||
|  | ||||
| template <typename Char, typename OutputIt, typename T> | ||||
| auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt { | ||||
|   if constexpr (is_string<T>::value) | ||||
|     return write_escaped_string<Char>(out, detail::to_string_view(v)); | ||||
|   else if constexpr (std::is_same_v<T, Char>) | ||||
|     return write_escaped_char(out, v); | ||||
|   else | ||||
|     return write<Char>(out, v); | ||||
| } | ||||
|  | ||||
| }  // namespace detail | ||||
|  | ||||
| template <typename T> struct is_variant_like { | ||||
| @@ -314,8 +369,7 @@ template <typename T, typename C> struct is_variant_formattable { | ||||
|  | ||||
| FMT_EXPORT | ||||
| template <typename Char> struct formatter<std::monostate, Char> { | ||||
|   template <typename ParseContext> | ||||
|   FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | ||||
|   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { | ||||
|     return ctx.begin(); | ||||
|   } | ||||
|  | ||||
| @@ -332,8 +386,7 @@ struct formatter< | ||||
|     Variant, Char, | ||||
|     std::enable_if_t<std::conjunction_v< | ||||
|         is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> { | ||||
|   template <typename ParseContext> | ||||
|   FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | ||||
|   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { | ||||
|     return ctx.begin(); | ||||
|   } | ||||
|  | ||||
| @@ -346,7 +399,7 @@ struct formatter< | ||||
|     FMT_TRY { | ||||
|       std::visit( | ||||
|           [&](const auto& v) { | ||||
|             out = detail::write_variant_alternative<Char>(out, v); | ||||
|             out = detail::write_escaped_alternative<Char>(out, v); | ||||
|           }, | ||||
|           value); | ||||
|     } | ||||
| @@ -362,23 +415,128 @@ FMT_END_NAMESPACE | ||||
|  | ||||
| FMT_BEGIN_NAMESPACE | ||||
| FMT_EXPORT | ||||
| template <typename Char> struct formatter<std::error_code, Char> { | ||||
|   template <typename ParseContext> | ||||
|   FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { | ||||
|     return ctx.begin(); | ||||
| template <> struct formatter<std::error_code> { | ||||
|  private: | ||||
|   format_specs specs_; | ||||
|   detail::arg_ref<char> width_ref_; | ||||
|  | ||||
|  public: | ||||
|   FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* { | ||||
|     auto it = ctx.begin(), end = ctx.end(); | ||||
|     if (it == end) return it; | ||||
|  | ||||
|     it = detail::parse_align(it, end, specs_); | ||||
|     if (it == end) return it; | ||||
|  | ||||
|     char c = *it; | ||||
|     if ((c >= '0' && c <= '9') || c == '{') | ||||
|       it = detail::parse_width(it, end, specs_, width_ref_, ctx); | ||||
|     return it; | ||||
|   } | ||||
|  | ||||
|   template <typename FormatContext> | ||||
|   FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const | ||||
|       -> decltype(ctx.out()) { | ||||
|     auto out = ctx.out(); | ||||
|     out = detail::write_bytes(out, ec.category().name(), format_specs<Char>()); | ||||
|     out = detail::write<Char>(out, Char(':')); | ||||
|     out = detail::write<Char>(out, ec.value()); | ||||
|     return out; | ||||
|   FMT_CONSTEXPR20 auto format(const std::error_code& ec, | ||||
|                               FormatContext& ctx) const -> decltype(ctx.out()) { | ||||
|     auto specs = specs_; | ||||
|     detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, | ||||
|                                 ctx); | ||||
|     memory_buffer buf; | ||||
|     buf.append(string_view(ec.category().name())); | ||||
|     buf.push_back(':'); | ||||
|     detail::write<char>(appender(buf), ec.value()); | ||||
|     return detail::write<char>(ctx.out(), string_view(buf.data(), buf.size()), | ||||
|                                specs); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| #if FMT_USE_RTTI | ||||
| namespace detail { | ||||
|  | ||||
| template <typename Char, typename OutputIt> | ||||
| auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt { | ||||
| #  ifdef FMT_HAS_ABI_CXA_DEMANGLE | ||||
|   int status = 0; | ||||
|   std::size_t size = 0; | ||||
|   std::unique_ptr<char, void (*)(void*)> demangled_name_ptr( | ||||
|       abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free); | ||||
|  | ||||
|   string_view demangled_name_view; | ||||
|   if (demangled_name_ptr) { | ||||
|     demangled_name_view = demangled_name_ptr.get(); | ||||
|  | ||||
|     // Normalization of stdlib inline namespace names. | ||||
|     // libc++ inline namespaces. | ||||
|     //  std::__1::*       -> std::* | ||||
|     //  std::__1::__fs::* -> std::* | ||||
|     // libstdc++ inline namespaces. | ||||
|     //  std::__cxx11::*             -> std::* | ||||
|     //  std::filesystem::__cxx11::* -> std::filesystem::* | ||||
|     if (demangled_name_view.starts_with("std::")) { | ||||
|       char* begin = demangled_name_ptr.get(); | ||||
|       char* to = begin + 5;  // std:: | ||||
|       for (char *from = to, *end = begin + demangled_name_view.size(); | ||||
|            from < end;) { | ||||
|         // This is safe, because demangled_name is NUL-terminated. | ||||
|         if (from[0] == '_' && from[1] == '_') { | ||||
|           char* next = from + 1; | ||||
|           while (next < end && *next != ':') next++; | ||||
|           if (next[0] == ':' && next[1] == ':') { | ||||
|             from = next + 2; | ||||
|             continue; | ||||
|           } | ||||
|         } | ||||
|         *to++ = *from++; | ||||
|       } | ||||
|       demangled_name_view = {begin, detail::to_unsigned(to - begin)}; | ||||
|     } | ||||
|   } else { | ||||
|     demangled_name_view = string_view(ti.name()); | ||||
|   } | ||||
|   return detail::write_bytes<Char>(out, demangled_name_view); | ||||
| #  elif FMT_MSC_VERSION | ||||
|   const string_view demangled_name(ti.name()); | ||||
|   for (std::size_t i = 0; i < demangled_name.size(); ++i) { | ||||
|     auto sub = demangled_name; | ||||
|     sub.remove_prefix(i); | ||||
|     if (sub.starts_with("enum ")) { | ||||
|       i += 4; | ||||
|       continue; | ||||
|     } | ||||
|     if (sub.starts_with("class ") || sub.starts_with("union ")) { | ||||
|       i += 5; | ||||
|       continue; | ||||
|     } | ||||
|     if (sub.starts_with("struct ")) { | ||||
|       i += 6; | ||||
|       continue; | ||||
|     } | ||||
|     if (*sub.begin() != ' ') *out++ = *sub.begin(); | ||||
|   } | ||||
|   return out; | ||||
| #  else | ||||
|   return detail::write_bytes<Char>(out, string_view(ti.name())); | ||||
| #  endif | ||||
| } | ||||
|  | ||||
| }  // namespace detail | ||||
|  | ||||
| FMT_EXPORT | ||||
| template <typename Char> | ||||
| struct formatter<std::type_info, Char  // DEPRECATED! Mixing code unit types. | ||||
|                  > { | ||||
|  public: | ||||
|   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { | ||||
|     return ctx.begin(); | ||||
|   } | ||||
|  | ||||
|   template <typename Context> | ||||
|   auto format(const std::type_info& ti, Context& ctx) const | ||||
|       -> decltype(ctx.out()) { | ||||
|     return detail::write_demangled_name<Char>(ctx.out(), ti); | ||||
|   } | ||||
| }; | ||||
| #endif | ||||
|  | ||||
| FMT_EXPORT | ||||
| template <typename T, typename Char> | ||||
| struct formatter< | ||||
| @@ -388,81 +546,29 @@ struct formatter< | ||||
|   bool with_typename_ = false; | ||||
|  | ||||
|  public: | ||||
|   FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx) | ||||
|       -> decltype(ctx.begin()) { | ||||
|   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { | ||||
|     auto it = ctx.begin(); | ||||
|     auto end = ctx.end(); | ||||
|     if (it == end || *it == '}') return it; | ||||
|     if (*it == 't') { | ||||
|       ++it; | ||||
|       with_typename_ = FMT_USE_TYPEID != 0; | ||||
|       with_typename_ = FMT_USE_RTTI != 0; | ||||
|     } | ||||
|     return it; | ||||
|   } | ||||
|  | ||||
|   template <typename OutputIt> | ||||
|   auto format(const std::exception& ex, | ||||
|               basic_format_context<OutputIt, Char>& ctx) const -> OutputIt { | ||||
|     format_specs<Char> spec; | ||||
|   template <typename Context> | ||||
|   auto format(const std::exception& ex, Context& ctx) const | ||||
|       -> decltype(ctx.out()) { | ||||
|     auto out = ctx.out(); | ||||
|     if (!with_typename_) | ||||
|       return detail::write_bytes(out, string_view(ex.what()), spec); | ||||
|  | ||||
| #if FMT_USE_TYPEID | ||||
|     const std::type_info& ti = typeid(ex); | ||||
| #  ifdef FMT_HAS_ABI_CXA_DEMANGLE | ||||
|     int status = 0; | ||||
|     std::size_t size = 0; | ||||
|     std::unique_ptr<char, void (*)(void*)> demangled_name_ptr( | ||||
|         abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free); | ||||
|  | ||||
|     string_view demangled_name_view; | ||||
|     if (demangled_name_ptr) { | ||||
|       demangled_name_view = demangled_name_ptr.get(); | ||||
|  | ||||
|       // Normalization of stdlib inline namespace names. | ||||
|       // libc++ inline namespaces. | ||||
|       //  std::__1::*       -> std::* | ||||
|       //  std::__1::__fs::* -> std::* | ||||
|       // libstdc++ inline namespaces. | ||||
|       //  std::__cxx11::*             -> std::* | ||||
|       //  std::filesystem::__cxx11::* -> std::filesystem::* | ||||
|       if (demangled_name_view.starts_with("std::")) { | ||||
|         char* begin = demangled_name_ptr.get(); | ||||
|         char* to = begin + 5;  // std:: | ||||
|         for (char *from = to, *end = begin + demangled_name_view.size(); | ||||
|              from < end;) { | ||||
|           // This is safe, because demangled_name is NUL-terminated. | ||||
|           if (from[0] == '_' && from[1] == '_') { | ||||
|             char* next = from + 1; | ||||
|             while (next < end && *next != ':') next++; | ||||
|             if (next[0] == ':' && next[1] == ':') { | ||||
|               from = next + 2; | ||||
|               continue; | ||||
|             } | ||||
|           } | ||||
|           *to++ = *from++; | ||||
|         } | ||||
|         demangled_name_view = {begin, detail::to_unsigned(to - begin)}; | ||||
|       } | ||||
|     } else { | ||||
|       demangled_name_view = string_view(ti.name()); | ||||
| #if FMT_USE_RTTI | ||||
|     if (with_typename_) { | ||||
|       out = detail::write_demangled_name<Char>(out, typeid(ex)); | ||||
|       *out++ = ':'; | ||||
|       *out++ = ' '; | ||||
|     } | ||||
|     out = detail::write_bytes(out, demangled_name_view, spec); | ||||
| #  elif FMT_MSC_VERSION | ||||
|     string_view demangled_name_view(ti.name()); | ||||
|     if (demangled_name_view.starts_with("class ")) | ||||
|       demangled_name_view.remove_prefix(6); | ||||
|     else if (demangled_name_view.starts_with("struct ")) | ||||
|       demangled_name_view.remove_prefix(7); | ||||
|     out = detail::write_bytes(out, demangled_name_view, spec); | ||||
| #  else | ||||
|     out = detail::write_bytes(out, string_view(ti.name()), spec); | ||||
| #  endif | ||||
|     *out++ = ':'; | ||||
|     *out++ = ' '; | ||||
|     return detail::write_bytes(out, string_view(ex.what()), spec); | ||||
| #endif | ||||
|     return detail::write_bytes<Char>(out, string_view(ex.what())); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| @@ -509,6 +615,14 @@ struct formatter<BitRef, Char, | ||||
|   } | ||||
| }; | ||||
|  | ||||
| template <typename T, typename Deleter> | ||||
| auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* { | ||||
|   return p.get(); | ||||
| } | ||||
| template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* { | ||||
|   return p.get(); | ||||
| } | ||||
|  | ||||
| FMT_EXPORT | ||||
| template <typename T, typename Char> | ||||
| struct formatter<std::atomic<T>, Char, | ||||
| @@ -533,5 +647,80 @@ struct formatter<std::atomic_flag, Char> : formatter<bool, Char> { | ||||
| }; | ||||
| #endif  // __cpp_lib_atomic_flag_test | ||||
|  | ||||
| FMT_EXPORT | ||||
| template <typename T, typename Char> struct formatter<std::complex<T>, Char> { | ||||
|  private: | ||||
|   detail::dynamic_format_specs<Char> specs_; | ||||
|  | ||||
|   template <typename FormatContext, typename OutputIt> | ||||
|   FMT_CONSTEXPR auto do_format(const std::complex<T>& c, | ||||
|                                detail::dynamic_format_specs<Char>& specs, | ||||
|                                FormatContext& ctx, OutputIt out) const | ||||
|       -> OutputIt { | ||||
|     if (c.real() != 0) { | ||||
|       *out++ = Char('('); | ||||
|       out = detail::write<Char>(out, c.real(), specs, ctx.locale()); | ||||
|       specs.set_sign(sign::plus); | ||||
|       out = detail::write<Char>(out, c.imag(), specs, ctx.locale()); | ||||
|       if (!detail::isfinite(c.imag())) *out++ = Char(' '); | ||||
|       *out++ = Char('i'); | ||||
|       *out++ = Char(')'); | ||||
|       return out; | ||||
|     } | ||||
|     out = detail::write<Char>(out, c.imag(), specs, ctx.locale()); | ||||
|     if (!detail::isfinite(c.imag())) *out++ = Char(' '); | ||||
|     *out++ = Char('i'); | ||||
|     return out; | ||||
|   } | ||||
|  | ||||
|  public: | ||||
|   FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* { | ||||
|     if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin(); | ||||
|     return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, | ||||
|                               detail::type_constant<T, Char>::value); | ||||
|   } | ||||
|  | ||||
|   template <typename FormatContext> | ||||
|   auto format(const std::complex<T>& c, FormatContext& ctx) const | ||||
|       -> decltype(ctx.out()) { | ||||
|     auto specs = specs_; | ||||
|     if (specs.dynamic()) { | ||||
|       detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, | ||||
|                                   specs.width_ref, ctx); | ||||
|       detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision, | ||||
|                                   specs.precision_ref, ctx); | ||||
|     } | ||||
|  | ||||
|     if (specs.width == 0) return do_format(c, specs, ctx, ctx.out()); | ||||
|     auto buf = basic_memory_buffer<Char>(); | ||||
|  | ||||
|     auto outer_specs = format_specs(); | ||||
|     outer_specs.width = specs.width; | ||||
|     outer_specs.copy_fill_from(specs); | ||||
|     outer_specs.set_align(specs.align()); | ||||
|  | ||||
|     specs.width = 0; | ||||
|     specs.set_fill({}); | ||||
|     specs.set_align(align::none); | ||||
|  | ||||
|     do_format(c, specs, ctx, basic_appender<Char>(buf)); | ||||
|     return detail::write<Char>(ctx.out(), | ||||
|                                basic_string_view<Char>(buf.data(), buf.size()), | ||||
|                                outer_specs); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| FMT_EXPORT | ||||
| template <typename T, typename Char> | ||||
| struct formatter<std::reference_wrapper<T>, Char, | ||||
|                  enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value>> | ||||
|     : formatter<remove_cvref_t<T>, Char> { | ||||
|   template <typename FormatContext> | ||||
|   auto format(std::reference_wrapper<T> ref, FormatContext& ctx) const | ||||
|       -> decltype(ctx.out()) { | ||||
|     return formatter<remove_cvref_t<T>, Char>::format(ref.get(), ctx); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| FMT_END_NAMESPACE | ||||
| #endif  // FMT_STD_H_ | ||||
|   | ||||
| @@ -8,12 +8,16 @@ | ||||
| #ifndef FMT_XCHAR_H_ | ||||
| #define FMT_XCHAR_H_ | ||||
|  | ||||
| #include <cwchar> | ||||
|  | ||||
| #include "color.h" | ||||
| #include "format.h" | ||||
| #include "ostream.h" | ||||
| #include "ranges.h" | ||||
|  | ||||
| #ifndef FMT_STATIC_THOUSANDS_SEPARATOR | ||||
| #  include <locale> | ||||
| #ifndef FMT_MODULE | ||||
| #  include <cwchar> | ||||
| #  if FMT_USE_LOCALE | ||||
| #    include <locale> | ||||
| #  endif | ||||
| #endif | ||||
|  | ||||
| FMT_BEGIN_NAMESPACE | ||||
| @@ -22,10 +26,26 @@ namespace detail { | ||||
| template <typename T> | ||||
| using is_exotic_char = bool_constant<!std::is_same<T, char>::value>; | ||||
|  | ||||
| inline auto write_loc(std::back_insert_iterator<detail::buffer<wchar_t>> out, | ||||
|                       loc_value value, const format_specs<wchar_t>& specs, | ||||
|                       locale_ref loc) -> bool { | ||||
| #ifndef FMT_STATIC_THOUSANDS_SEPARATOR | ||||
| template <typename S, typename = void> struct format_string_char {}; | ||||
|  | ||||
| template <typename S> | ||||
| struct format_string_char< | ||||
|     S, void_t<decltype(sizeof(detail::to_string_view(std::declval<S>())))>> { | ||||
|   using type = char_t<S>; | ||||
| }; | ||||
|  | ||||
| template <typename S> | ||||
| struct format_string_char< | ||||
|     S, enable_if_t<std::is_base_of<detail::compile_string, S>::value>> { | ||||
|   using type = typename S::char_type; | ||||
| }; | ||||
|  | ||||
| template <typename S> | ||||
| using format_string_char_t = typename format_string_char<S>::type; | ||||
|  | ||||
| inline auto write_loc(basic_appender<wchar_t> out, loc_value value, | ||||
|                       const format_specs& specs, locale_ref loc) -> bool { | ||||
| #if FMT_USE_LOCALE | ||||
|   auto& numpunct = | ||||
|       std::use_facet<std::numpunct<wchar_t>>(loc.get<std::locale>()); | ||||
|   auto separator = std::wstring(); | ||||
| @@ -40,42 +60,79 @@ inline auto write_loc(std::back_insert_iterator<detail::buffer<wchar_t>> out, | ||||
| FMT_BEGIN_EXPORT | ||||
|  | ||||
| using wstring_view = basic_string_view<wchar_t>; | ||||
| using wformat_parse_context = basic_format_parse_context<wchar_t>; | ||||
| using wformat_context = buffer_context<wchar_t>; | ||||
| using wformat_parse_context = parse_context<wchar_t>; | ||||
| using wformat_context = buffered_context<wchar_t>; | ||||
| using wformat_args = basic_format_args<wformat_context>; | ||||
| using wmemory_buffer = basic_memory_buffer<wchar_t>; | ||||
|  | ||||
| #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 | ||||
| // Workaround broken conversion on older gcc. | ||||
| template <typename... Args> using wformat_string = wstring_view; | ||||
| inline auto runtime(wstring_view s) -> wstring_view { return s; } | ||||
| #else | ||||
| template <typename... Args> | ||||
| using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>; | ||||
| template <typename Char, typename... T> struct basic_fstring { | ||||
|  private: | ||||
|   basic_string_view<Char> str_; | ||||
|  | ||||
|   static constexpr int num_static_named_args = | ||||
|       detail::count_static_named_args<T...>(); | ||||
|  | ||||
|   using checker = detail::format_string_checker< | ||||
|       Char, static_cast<int>(sizeof...(T)), num_static_named_args, | ||||
|       num_static_named_args != detail::count_named_args<T...>()>; | ||||
|  | ||||
|   using arg_pack = detail::arg_pack<T...>; | ||||
|  | ||||
|  public: | ||||
|   using t = basic_fstring; | ||||
|  | ||||
|   template <typename S, | ||||
|             FMT_ENABLE_IF( | ||||
|                 std::is_convertible<const S&, basic_string_view<Char>>::value)> | ||||
|   FMT_CONSTEVAL FMT_ALWAYS_INLINE basic_fstring(const S& s) : str_(s) { | ||||
|     if (FMT_USE_CONSTEVAL) | ||||
|       detail::parse_format_string<Char>(s, checker(s, arg_pack())); | ||||
|   } | ||||
|   template <typename S, | ||||
|             FMT_ENABLE_IF(std::is_base_of<detail::compile_string, S>::value&& | ||||
|                               std::is_same<typename S::char_type, Char>::value)> | ||||
|   FMT_ALWAYS_INLINE basic_fstring(const S&) : str_(S()) { | ||||
|     FMT_CONSTEXPR auto sv = basic_string_view<Char>(S()); | ||||
|     FMT_CONSTEXPR int ignore = | ||||
|         (parse_format_string(sv, checker(sv, arg_pack())), 0); | ||||
|     detail::ignore_unused(ignore); | ||||
|   } | ||||
|   basic_fstring(runtime_format_string<Char> fmt) : str_(fmt.str) {} | ||||
|  | ||||
|   operator basic_string_view<Char>() const { return str_; } | ||||
|   auto get() const -> basic_string_view<Char> { return str_; } | ||||
| }; | ||||
|  | ||||
| template <typename Char, typename... T> | ||||
| using basic_format_string = basic_fstring<Char, T...>; | ||||
|  | ||||
| template <typename... T> | ||||
| using wformat_string = typename basic_format_string<wchar_t, T...>::t; | ||||
| inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> { | ||||
|   return {{s}}; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| template <> struct is_char<wchar_t> : std::true_type {}; | ||||
| template <> struct is_char<detail::char8_type> : std::true_type {}; | ||||
| template <> struct is_char<char16_t> : std::true_type {}; | ||||
| template <> struct is_char<char32_t> : std::true_type {}; | ||||
|  | ||||
| #ifdef __cpp_char8_t | ||||
| template <> struct is_char<char8_t> : bool_constant<detail::is_utf8_enabled> {}; | ||||
| #endif | ||||
|  | ||||
| template <typename... T> | ||||
| constexpr auto make_wformat_args(const T&... args) | ||||
|     -> format_arg_store<wformat_context, T...> { | ||||
|   return {args...}; | ||||
| constexpr auto make_wformat_args(T&... args) | ||||
|     -> decltype(fmt::make_format_args<wformat_context>(args...)) { | ||||
|   return fmt::make_format_args<wformat_context>(args...); | ||||
| } | ||||
|  | ||||
| #if !FMT_USE_NONTYPE_TEMPLATE_ARGS | ||||
| inline namespace literals { | ||||
| #if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS | ||||
| constexpr auto operator""_a(const wchar_t* s, size_t) | ||||
|     -> detail::udl_arg<wchar_t> { | ||||
| inline auto operator""_a(const wchar_t* s, size_t) -> detail::udl_arg<wchar_t> { | ||||
|   return {s}; | ||||
| } | ||||
| #endif | ||||
| }  // namespace literals | ||||
| #endif | ||||
|  | ||||
| template <typename It, typename Sentinel> | ||||
| auto join(It begin, Sentinel end, wstring_view sep) | ||||
| @@ -83,9 +140,9 @@ auto join(It begin, Sentinel end, wstring_view sep) | ||||
|   return {begin, end, sep}; | ||||
| } | ||||
|  | ||||
| template <typename Range> | ||||
| template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)> | ||||
| auto join(Range&& range, wstring_view sep) | ||||
|     -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>, | ||||
|     -> join_view<decltype(std::begin(range)), decltype(std::end(range)), | ||||
|                  wchar_t> { | ||||
|   return join(std::begin(range), std::end(range), sep); | ||||
| } | ||||
| @@ -96,13 +153,19 @@ auto join(std::initializer_list<T> list, wstring_view sep) | ||||
|   return join(std::begin(list), std::end(list), sep); | ||||
| } | ||||
|  | ||||
| template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)> | ||||
| auto join(const Tuple& tuple, basic_string_view<wchar_t> sep) | ||||
|     -> tuple_join_view<wchar_t, Tuple> { | ||||
|   return {tuple, sep}; | ||||
| } | ||||
|  | ||||
| template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)> | ||||
| auto vformat(basic_string_view<Char> format_str, | ||||
|              basic_format_args<buffer_context<type_identity_t<Char>>> args) | ||||
| auto vformat(basic_string_view<Char> fmt, | ||||
|              typename detail::vformat_args<Char>::type args) | ||||
|     -> std::basic_string<Char> { | ||||
|   auto buf = basic_memory_buffer<Char>(); | ||||
|   detail::vformat_to(buf, format_str, args); | ||||
|   return to_string(buf); | ||||
|   detail::vformat_to(buf, fmt, args); | ||||
|   return {buf.data(), buf.size()}; | ||||
| } | ||||
|  | ||||
| template <typename... T> | ||||
| @@ -110,110 +173,122 @@ auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring { | ||||
|   return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...)); | ||||
| } | ||||
|  | ||||
| template <typename OutputIt, typename... T> | ||||
| auto format_to(OutputIt out, wformat_string<T...> fmt, T&&... args) | ||||
|     -> OutputIt { | ||||
|   return vformat_to(out, fmt::wstring_view(fmt), | ||||
|                     fmt::make_wformat_args(args...)); | ||||
| } | ||||
|  | ||||
| // Pass char_t as a default template parameter instead of using | ||||
| // std::basic_string<char_t<S>> to reduce the symbol size. | ||||
| template <typename S, typename... T, typename Char = char_t<S>, | ||||
| template <typename S, typename... T, | ||||
|           typename Char = detail::format_string_char_t<S>, | ||||
|           FMT_ENABLE_IF(!std::is_same<Char, char>::value && | ||||
|                         !std::is_same<Char, wchar_t>::value)> | ||||
| auto format(const S& format_str, T&&... args) -> std::basic_string<Char> { | ||||
|   return vformat(detail::to_string_view(format_str), | ||||
|                  fmt::make_format_args<buffer_context<Char>>(args...)); | ||||
| auto format(const S& fmt, T&&... args) -> std::basic_string<Char> { | ||||
|   return vformat(detail::to_string_view(fmt), | ||||
|                  fmt::make_format_args<buffered_context<Char>>(args...)); | ||||
| } | ||||
|  | ||||
| template <typename Locale, typename S, typename Char = char_t<S>, | ||||
| template <typename Locale, typename S, | ||||
|           typename Char = detail::format_string_char_t<S>, | ||||
|           FMT_ENABLE_IF(detail::is_locale<Locale>::value&& | ||||
|                             detail::is_exotic_char<Char>::value)> | ||||
| inline auto vformat( | ||||
|     const Locale& loc, const S& format_str, | ||||
|     basic_format_args<buffer_context<type_identity_t<Char>>> args) | ||||
| inline auto vformat(const Locale& loc, const S& fmt, | ||||
|                     typename detail::vformat_args<Char>::type args) | ||||
|     -> std::basic_string<Char> { | ||||
|   return detail::vformat(loc, detail::to_string_view(format_str), args); | ||||
|   auto buf = basic_memory_buffer<Char>(); | ||||
|   detail::vformat_to(buf, detail::to_string_view(fmt), args, | ||||
|                      detail::locale_ref(loc)); | ||||
|   return {buf.data(), buf.size()}; | ||||
| } | ||||
|  | ||||
| template <typename Locale, typename S, typename... T, typename Char = char_t<S>, | ||||
| template <typename Locale, typename S, typename... T, | ||||
|           typename Char = detail::format_string_char_t<S>, | ||||
|           FMT_ENABLE_IF(detail::is_locale<Locale>::value&& | ||||
|                             detail::is_exotic_char<Char>::value)> | ||||
| inline auto format(const Locale& loc, const S& format_str, T&&... args) | ||||
| inline auto format(const Locale& loc, const S& fmt, T&&... args) | ||||
|     -> std::basic_string<Char> { | ||||
|   return detail::vformat(loc, detail::to_string_view(format_str), | ||||
|                          fmt::make_format_args<buffer_context<Char>>(args...)); | ||||
|   return vformat(loc, detail::to_string_view(fmt), | ||||
|                  fmt::make_format_args<buffered_context<Char>>(args...)); | ||||
| } | ||||
|  | ||||
| template <typename OutputIt, typename S, typename Char = char_t<S>, | ||||
| template <typename OutputIt, typename S, | ||||
|           typename Char = detail::format_string_char_t<S>, | ||||
|           FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& | ||||
|                             detail::is_exotic_char<Char>::value)> | ||||
| auto vformat_to(OutputIt out, const S& format_str, | ||||
|                 basic_format_args<buffer_context<type_identity_t<Char>>> args) | ||||
|     -> OutputIt { | ||||
| auto vformat_to(OutputIt out, const S& fmt, | ||||
|                 typename detail::vformat_args<Char>::type args) -> OutputIt { | ||||
|   auto&& buf = detail::get_buffer<Char>(out); | ||||
|   detail::vformat_to(buf, detail::to_string_view(format_str), args); | ||||
|   detail::vformat_to(buf, detail::to_string_view(fmt), args); | ||||
|   return detail::get_iterator(buf, out); | ||||
| } | ||||
|  | ||||
| template <typename OutputIt, typename S, typename... T, | ||||
|           typename Char = char_t<S>, | ||||
|           FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& | ||||
|                             detail::is_exotic_char<Char>::value)> | ||||
|           typename Char = detail::format_string_char_t<S>, | ||||
|           FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value && | ||||
|                         !std::is_same<Char, char>::value && | ||||
|                         !std::is_same<Char, wchar_t>::value)> | ||||
| inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt { | ||||
|   return vformat_to(out, detail::to_string_view(fmt), | ||||
|                     fmt::make_format_args<buffer_context<Char>>(args...)); | ||||
|                     fmt::make_format_args<buffered_context<Char>>(args...)); | ||||
| } | ||||
|  | ||||
| template <typename Locale, typename S, typename OutputIt, typename... Args, | ||||
|           typename Char = char_t<S>, | ||||
|           typename Char = detail::format_string_char_t<S>, | ||||
|           FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& | ||||
|                             detail::is_locale<Locale>::value&& | ||||
|                                 detail::is_exotic_char<Char>::value)> | ||||
| inline auto vformat_to( | ||||
|     OutputIt out, const Locale& loc, const S& format_str, | ||||
|     basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt { | ||||
| inline auto vformat_to(OutputIt out, const Locale& loc, const S& fmt, | ||||
|                        typename detail::vformat_args<Char>::type args) | ||||
|     -> OutputIt { | ||||
|   auto&& buf = detail::get_buffer<Char>(out); | ||||
|   vformat_to(buf, detail::to_string_view(format_str), args, | ||||
|              detail::locale_ref(loc)); | ||||
|   vformat_to(buf, detail::to_string_view(fmt), args, detail::locale_ref(loc)); | ||||
|   return detail::get_iterator(buf, out); | ||||
| } | ||||
|  | ||||
| template < | ||||
|     typename OutputIt, typename Locale, typename S, typename... T, | ||||
|     typename Char = char_t<S>, | ||||
|     bool enable = detail::is_output_iterator<OutputIt, Char>::value&& | ||||
|         detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value> | ||||
| inline auto format_to(OutputIt out, const Locale& loc, const S& format_str, | ||||
| template <typename Locale, typename OutputIt, typename S, typename... T, | ||||
|           typename Char = detail::format_string_char_t<S>, | ||||
|           bool enable = detail::is_output_iterator<OutputIt, Char>::value && | ||||
|                         detail::is_locale<Locale>::value && | ||||
|                         detail::is_exotic_char<Char>::value> | ||||
| inline auto format_to(OutputIt out, const Locale& loc, const S& fmt, | ||||
|                       T&&... args) -> | ||||
|     typename std::enable_if<enable, OutputIt>::type { | ||||
|   return vformat_to(out, loc, detail::to_string_view(format_str), | ||||
|                     fmt::make_format_args<buffer_context<Char>>(args...)); | ||||
|   return vformat_to(out, loc, detail::to_string_view(fmt), | ||||
|                     fmt::make_format_args<buffered_context<Char>>(args...)); | ||||
| } | ||||
|  | ||||
| template <typename OutputIt, typename Char, typename... Args, | ||||
|           FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& | ||||
|                             detail::is_exotic_char<Char>::value)> | ||||
| inline auto vformat_to_n( | ||||
|     OutputIt out, size_t n, basic_string_view<Char> format_str, | ||||
|     basic_format_args<buffer_context<type_identity_t<Char>>> args) | ||||
| inline auto vformat_to_n(OutputIt out, size_t n, basic_string_view<Char> fmt, | ||||
|                          typename detail::vformat_args<Char>::type args) | ||||
|     -> format_to_n_result<OutputIt> { | ||||
|   using traits = detail::fixed_buffer_traits; | ||||
|   auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n); | ||||
|   detail::vformat_to(buf, format_str, args); | ||||
|   detail::vformat_to(buf, fmt, args); | ||||
|   return {buf.out(), buf.count()}; | ||||
| } | ||||
|  | ||||
| template <typename OutputIt, typename S, typename... T, | ||||
|           typename Char = char_t<S>, | ||||
|           typename Char = detail::format_string_char_t<S>, | ||||
|           FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& | ||||
|                             detail::is_exotic_char<Char>::value)> | ||||
| inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args) | ||||
|     -> format_to_n_result<OutputIt> { | ||||
|   return vformat_to_n(out, n, detail::to_string_view(fmt), | ||||
|                       fmt::make_format_args<buffer_context<Char>>(args...)); | ||||
|   return vformat_to_n(out, n, fmt::basic_string_view<Char>(fmt), | ||||
|                       fmt::make_format_args<buffered_context<Char>>(args...)); | ||||
| } | ||||
|  | ||||
| template <typename S, typename... T, typename Char = char_t<S>, | ||||
| template <typename S, typename... T, | ||||
|           typename Char = detail::format_string_char_t<S>, | ||||
|           FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)> | ||||
| inline auto formatted_size(const S& fmt, T&&... args) -> size_t { | ||||
|   auto buf = detail::counting_buffer<Char>(); | ||||
|   detail::vformat_to(buf, detail::to_string_view(fmt), | ||||
|                      fmt::make_format_args<buffer_context<Char>>(args...)); | ||||
|                      fmt::make_format_args<buffered_context<Char>>(args...)); | ||||
|   return buf.count(); | ||||
| } | ||||
|  | ||||
| @@ -247,9 +322,48 @@ template <typename... T> void println(wformat_string<T...> fmt, T&&... args) { | ||||
|   return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...)); | ||||
| } | ||||
|  | ||||
| /** | ||||
|   Converts *value* to ``std::wstring`` using the default format for type *T*. | ||||
|  */ | ||||
| inline auto vformat(const text_style& ts, wstring_view fmt, wformat_args args) | ||||
|     -> std::wstring { | ||||
|   auto buf = wmemory_buffer(); | ||||
|   detail::vformat_to(buf, ts, fmt, args); | ||||
|   return {buf.data(), buf.size()}; | ||||
| } | ||||
|  | ||||
| template <typename... T> | ||||
| inline auto format(const text_style& ts, wformat_string<T...> fmt, T&&... args) | ||||
|     -> std::wstring { | ||||
|   return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...)); | ||||
| } | ||||
|  | ||||
| template <typename... T> | ||||
| FMT_DEPRECATED void print(std::FILE* f, const text_style& ts, | ||||
|                           wformat_string<T...> fmt, const T&... args) { | ||||
|   vprint(f, ts, fmt, fmt::make_wformat_args(args...)); | ||||
| } | ||||
|  | ||||
| template <typename... T> | ||||
| FMT_DEPRECATED void print(const text_style& ts, wformat_string<T...> fmt, | ||||
|                           const T&... args) { | ||||
|   return print(stdout, ts, fmt, args...); | ||||
| } | ||||
|  | ||||
| inline void vprint(std::wostream& os, wstring_view fmt, wformat_args args) { | ||||
|   auto buffer = basic_memory_buffer<wchar_t>(); | ||||
|   detail::vformat_to(buffer, fmt, args); | ||||
|   detail::write_buffer(os, buffer); | ||||
| } | ||||
|  | ||||
| template <typename... T> | ||||
| void print(std::wostream& os, wformat_string<T...> fmt, T&&... args) { | ||||
|   vprint(os, fmt, fmt::make_format_args<buffered_context<wchar_t>>(args...)); | ||||
| } | ||||
|  | ||||
| template <typename... T> | ||||
| void println(std::wostream& os, wformat_string<T...> fmt, T&&... args) { | ||||
|   print(os, L"{}\n", fmt::format(fmt, std::forward<T>(args)...)); | ||||
| } | ||||
|  | ||||
| /// Converts `value` to `std::wstring` using the default format for type `T`. | ||||
| template <typename T> inline auto to_wstring(const T& value) -> std::wstring { | ||||
|   return format(FMT_STRING(L"{}"), value); | ||||
| } | ||||
|   | ||||
| @@ -1,38 +1,59 @@ | ||||
| module; | ||||
|  | ||||
| #define FMT_MODULE | ||||
|  | ||||
| #ifdef _MSVC_LANG | ||||
| #  define FMT_CPLUSPLUS _MSVC_LANG | ||||
| #else | ||||
| #  define FMT_CPLUSPLUS __cplusplus | ||||
| #endif | ||||
|  | ||||
| // Put all implementation-provided headers into the global module fragment | ||||
| // to prevent attachment to this module. | ||||
| #include <algorithm> | ||||
| #ifndef FMT_IMPORT_STD | ||||
| #  include <algorithm> | ||||
| #  include <bitset> | ||||
| #  include <chrono> | ||||
| #  include <cmath> | ||||
| #  include <complex> | ||||
| #  include <cstddef> | ||||
| #  include <cstdint> | ||||
| #  include <cstdio> | ||||
| #  include <cstdlib> | ||||
| #  include <cstring> | ||||
| #  include <ctime> | ||||
| #  include <exception> | ||||
| #  if FMT_CPLUSPLUS > 202002L | ||||
| #    include <expected> | ||||
| #  endif | ||||
| #  include <filesystem> | ||||
| #  include <fstream> | ||||
| #  include <functional> | ||||
| #  include <iterator> | ||||
| #  include <limits> | ||||
| #  include <locale> | ||||
| #  include <memory> | ||||
| #  include <optional> | ||||
| #  include <ostream> | ||||
| #  include <source_location> | ||||
| #  include <stdexcept> | ||||
| #  include <string> | ||||
| #  include <string_view> | ||||
| #  include <system_error> | ||||
| #  include <thread> | ||||
| #  include <type_traits> | ||||
| #  include <typeinfo> | ||||
| #  include <utility> | ||||
| #  include <variant> | ||||
| #  include <vector> | ||||
| #else | ||||
| #  include <limits.h> | ||||
| #  include <stdint.h> | ||||
| #  include <stdio.h> | ||||
| #  include <time.h> | ||||
| #endif | ||||
| #include <cerrno> | ||||
| #include <chrono> | ||||
| #include <climits> | ||||
| #include <cmath> | ||||
| #include <cstddef> | ||||
| #include <cstdint> | ||||
| #include <cstdio> | ||||
| #include <cstdlib> | ||||
| #include <cstring> | ||||
| #include <ctime> | ||||
| #include <exception> | ||||
| #include <filesystem> | ||||
| #include <fstream> | ||||
| #include <functional> | ||||
| #include <iterator> | ||||
| #include <limits> | ||||
| #include <locale> | ||||
| #include <memory> | ||||
| #include <optional> | ||||
| #include <ostream> | ||||
| #include <stdexcept> | ||||
| #include <string> | ||||
| #include <string_view> | ||||
| #include <system_error> | ||||
| #include <thread> | ||||
| #include <type_traits> | ||||
| #include <typeinfo> | ||||
| #include <utility> | ||||
| #include <variant> | ||||
| #include <vector> | ||||
| #include <version> | ||||
|  | ||||
| #if __has_include(<cxxabi.h>) | ||||
| @@ -70,6 +91,10 @@ module; | ||||
|  | ||||
| export module fmt; | ||||
|  | ||||
| #ifdef FMT_IMPORT_STD | ||||
| import std; | ||||
| #endif | ||||
|  | ||||
| #define FMT_EXPORT export | ||||
| #define FMT_BEGIN_EXPORT export { | ||||
| #define FMT_END_EXPORT } | ||||
| @@ -83,6 +108,10 @@ export module fmt; | ||||
| extern "C++" { | ||||
| #endif | ||||
|  | ||||
| #ifndef FMT_OS | ||||
| #  define FMT_OS 1 | ||||
| #endif | ||||
|  | ||||
| // All library-provided declarations and definitions must be in the module | ||||
| // purview to be exported. | ||||
| #include "fmt/args.h" | ||||
| @@ -90,8 +119,12 @@ extern "C++" { | ||||
| #include "fmt/color.h" | ||||
| #include "fmt/compile.h" | ||||
| #include "fmt/format.h" | ||||
| #include "fmt/os.h" | ||||
| #if FMT_OS | ||||
| #  include "fmt/os.h" | ||||
| #endif | ||||
| #include "fmt/ostream.h" | ||||
| #include "fmt/printf.h" | ||||
| #include "fmt/ranges.h" | ||||
| #include "fmt/std.h" | ||||
| #include "fmt/xchar.h" | ||||
|  | ||||
| @@ -104,5 +137,17 @@ extern "C++" { | ||||
| module :private; | ||||
| #endif | ||||
|  | ||||
| #include "format.cc" | ||||
| #include "os.cc" | ||||
| #ifdef FMT_ATTACH_TO_GLOBAL_MODULE | ||||
| extern "C++" { | ||||
| #endif | ||||
|  | ||||
| #if FMT_HAS_INCLUDE("format.cc") | ||||
| #  include "format.cc" | ||||
| #endif | ||||
| #if FMT_OS && FMT_HAS_INCLUDE("os.cc") | ||||
| #  include "os.cc" | ||||
| #endif | ||||
|  | ||||
| #ifdef FMT_ATTACH_TO_GLOBAL_MODULE | ||||
| } | ||||
| #endif | ||||
|   | ||||
| @@ -15,7 +15,8 @@ template FMT_API auto dragonbox::to_decimal(float x) noexcept | ||||
| template FMT_API auto dragonbox::to_decimal(double x) noexcept | ||||
|     -> dragonbox::decimal_fp<double>; | ||||
|  | ||||
| #ifndef FMT_STATIC_THOUSANDS_SEPARATOR | ||||
| #if FMT_USE_LOCALE | ||||
| // DEPRECATED! locale_ref in the detail namespace | ||||
| template FMT_API locale_ref::locale_ref(const std::locale& loc); | ||||
| template FMT_API auto locale_ref::get<std::locale>() const -> std::locale; | ||||
| #endif | ||||
| @@ -26,8 +27,10 @@ template FMT_API auto thousands_sep_impl(locale_ref) | ||||
|     -> thousands_sep_result<char>; | ||||
| template FMT_API auto decimal_point_impl(locale_ref) -> char; | ||||
|  | ||||
| // DEPRECATED! | ||||
| template FMT_API void buffer<char>::append(const char*, const char*); | ||||
|  | ||||
| // DEPRECATED! | ||||
| template FMT_API void vformat_to(buffer<char>&, string_view, | ||||
|                                  typename vformat_args<>::type, locale_ref); | ||||
|  | ||||
|   | ||||
| @@ -12,47 +12,51 @@ | ||||
|  | ||||
| #include "fmt/os.h" | ||||
|  | ||||
| #include <climits> | ||||
| #ifndef FMT_MODULE | ||||
| #  include <climits> | ||||
|  | ||||
| #if FMT_USE_FCNTL | ||||
| #  include <sys/stat.h> | ||||
| #  include <sys/types.h> | ||||
| #  if FMT_USE_FCNTL | ||||
| #    include <sys/stat.h> | ||||
| #    include <sys/types.h> | ||||
|  | ||||
| #  ifdef _WRS_KERNEL    // VxWorks7 kernel | ||||
| #    include <ioLib.h>  // getpagesize | ||||
| #    ifdef _WRS_KERNEL    // VxWorks7 kernel | ||||
| #      include <ioLib.h>  // getpagesize | ||||
| #    endif | ||||
|  | ||||
| #    ifndef _WIN32 | ||||
| #      include <unistd.h> | ||||
| #    else | ||||
| #      ifndef WIN32_LEAN_AND_MEAN | ||||
| #        define WIN32_LEAN_AND_MEAN | ||||
| #      endif | ||||
| #      include <io.h> | ||||
| #    endif  // _WIN32 | ||||
| #  endif    // FMT_USE_FCNTL | ||||
|  | ||||
| #  ifdef _WIN32 | ||||
| #    include <windows.h> | ||||
| #  endif | ||||
|  | ||||
| #  ifndef _WIN32 | ||||
| #    include <unistd.h> | ||||
| #  else | ||||
| #    ifndef WIN32_LEAN_AND_MEAN | ||||
| #      define WIN32_LEAN_AND_MEAN | ||||
| #    endif | ||||
| #    include <io.h> | ||||
|  | ||||
| #    ifndef S_IRUSR | ||||
| #      define S_IRUSR _S_IREAD | ||||
| #    endif | ||||
| #    ifndef S_IWUSR | ||||
| #      define S_IWUSR _S_IWRITE | ||||
| #    endif | ||||
| #    ifndef S_IRGRP | ||||
| #      define S_IRGRP 0 | ||||
| #    endif | ||||
| #    ifndef S_IWGRP | ||||
| #      define S_IWGRP 0 | ||||
| #    endif | ||||
| #    ifndef S_IROTH | ||||
| #      define S_IROTH 0 | ||||
| #    endif | ||||
| #    ifndef S_IWOTH | ||||
| #      define S_IWOTH 0 | ||||
| #    endif | ||||
| #  endif  // _WIN32 | ||||
| #endif    // FMT_USE_FCNTL | ||||
| #endif | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| #  include <windows.h> | ||||
| #  ifndef S_IRUSR | ||||
| #    define S_IRUSR _S_IREAD | ||||
| #  endif | ||||
| #  ifndef S_IWUSR | ||||
| #    define S_IWUSR _S_IWRITE | ||||
| #  endif | ||||
| #  ifndef S_IRGRP | ||||
| #    define S_IRGRP 0 | ||||
| #  endif | ||||
| #  ifndef S_IWGRP | ||||
| #    define S_IWGRP 0 | ||||
| #  endif | ||||
| #  ifndef S_IROTH | ||||
| #    define S_IROTH 0 | ||||
| #  endif | ||||
| #  ifndef S_IWOTH | ||||
| #    define S_IWOTH 0 | ||||
| #  endif | ||||
| #endif | ||||
|  | ||||
| namespace { | ||||
| @@ -156,7 +160,7 @@ void detail::format_windows_error(detail::buffer<char>& out, int error_code, | ||||
| } | ||||
|  | ||||
| void report_windows_error(int error_code, const char* message) noexcept { | ||||
|   report_error(detail::format_windows_error, error_code, message); | ||||
|   do_report_error(detail::format_windows_error, error_code, message); | ||||
| } | ||||
| #endif  // _WIN32 | ||||
|  | ||||
| @@ -182,12 +186,14 @@ void buffered_file::close() { | ||||
| } | ||||
|  | ||||
| int buffered_file::descriptor() const { | ||||
| #if !defined(fileno) | ||||
| #ifdef FMT_HAS_SYSTEM | ||||
|   // fileno is a macro on OpenBSD. | ||||
| #  ifdef fileno | ||||
| #    undef fileno | ||||
| #  endif | ||||
|   int fd = FMT_POSIX_CALL(fileno(file_)); | ||||
| #elif defined(FMT_HAS_SYSTEM) | ||||
|   // fileno is a macro on OpenBSD so we cannot use FMT_POSIX_CALL. | ||||
| #  define FMT_DISABLE_MACRO | ||||
|   int fd = FMT_SYSTEM(fileno FMT_DISABLE_MACRO(file_)); | ||||
| #elif defined(_WIN32) | ||||
|   int fd = _fileno(file_); | ||||
| #else | ||||
|   int fd = fileno(file_); | ||||
| #endif | ||||
| @@ -200,6 +206,7 @@ int buffered_file::descriptor() const { | ||||
| #  ifdef _WIN32 | ||||
| using mode_t = int; | ||||
| #  endif | ||||
|  | ||||
| constexpr mode_t default_open_mode = | ||||
|     S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; | ||||
|  | ||||
| @@ -301,29 +308,6 @@ void file::dup2(int fd, std::error_code& ec) noexcept { | ||||
|   if (result == -1) ec = std::error_code(errno, std::generic_category()); | ||||
| } | ||||
|  | ||||
| void file::pipe(file& read_end, file& write_end) { | ||||
|   // Close the descriptors first to make sure that assignments don't throw | ||||
|   // and there are no leaks. | ||||
|   read_end.close(); | ||||
|   write_end.close(); | ||||
|   int fds[2] = {}; | ||||
| #  ifdef _WIN32 | ||||
|   // Make the default pipe capacity same as on Linux 2.6.11+. | ||||
|   enum { DEFAULT_CAPACITY = 65536 }; | ||||
|   int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY)); | ||||
| #  else | ||||
|   // Don't retry as the pipe function doesn't return EINTR. | ||||
|   // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html | ||||
|   int result = FMT_POSIX_CALL(pipe(fds)); | ||||
| #  endif | ||||
|   if (result != 0) | ||||
|     FMT_THROW(system_error(errno, FMT_STRING("cannot create pipe"))); | ||||
|   // The following assignments don't throw because read_fd and write_fd | ||||
|   // are closed. | ||||
|   read_end = file(fds[0]); | ||||
|   write_end = file(fds[1]); | ||||
| } | ||||
|  | ||||
| buffered_file file::fdopen(const char* mode) { | ||||
| // Don't retry as fdopen doesn't return EINTR. | ||||
| #  if defined(__MINGW32__) && defined(_POSIX_) | ||||
| @@ -352,6 +336,24 @@ file file::open_windows_file(wcstring_view path, int oflag) { | ||||
| } | ||||
| #  endif | ||||
|  | ||||
| pipe::pipe() { | ||||
|   int fds[2] = {}; | ||||
| #  ifdef _WIN32 | ||||
|   // Make the default pipe capacity same as on Linux 2.6.11+. | ||||
|   enum { DEFAULT_CAPACITY = 65536 }; | ||||
|   int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY)); | ||||
| #  else | ||||
|   // Don't retry as the pipe function doesn't return EINTR. | ||||
|   // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html | ||||
|   int result = FMT_POSIX_CALL(pipe(fds)); | ||||
| #  endif | ||||
|   if (result != 0) | ||||
|     FMT_THROW(system_error(errno, FMT_STRING("cannot create pipe"))); | ||||
|   // The following assignments don't throw. | ||||
|   read_end = file(fds[0]); | ||||
|   write_end = file(fds[1]); | ||||
| } | ||||
|  | ||||
| #  if !defined(__MSDOS__) | ||||
| long getpagesize() { | ||||
| #    ifdef _WIN32 | ||||
| @@ -372,31 +374,25 @@ long getpagesize() { | ||||
| } | ||||
| #  endif | ||||
|  | ||||
| namespace detail { | ||||
|  | ||||
| void file_buffer::grow(size_t) { | ||||
|   if (this->size() == this->capacity()) flush(); | ||||
| void ostream::grow(buffer<char>& buf, size_t) { | ||||
|   if (buf.size() == buf.capacity()) static_cast<ostream&>(buf).flush(); | ||||
| } | ||||
|  | ||||
| file_buffer::file_buffer(cstring_view path, | ||||
|                          const detail::ostream_params& params) | ||||
|     : file_(path, params.oflag) { | ||||
| ostream::ostream(cstring_view path, const detail::ostream_params& params) | ||||
|     : buffer<char>(grow), file_(path, params.oflag) { | ||||
|   set(new char[params.buffer_size], params.buffer_size); | ||||
| } | ||||
|  | ||||
| file_buffer::file_buffer(file_buffer&& other) | ||||
|     : detail::buffer<char>(other.data(), other.size(), other.capacity()), | ||||
| ostream::ostream(ostream&& other) noexcept | ||||
|     : buffer<char>(grow, other.data(), other.size(), other.capacity()), | ||||
|       file_(std::move(other.file_)) { | ||||
|   other.clear(); | ||||
|   other.set(nullptr, 0); | ||||
| } | ||||
|  | ||||
| file_buffer::~file_buffer() { | ||||
| ostream::~ostream() { | ||||
|   flush(); | ||||
|   delete[] data(); | ||||
| } | ||||
| }  // namespace detail | ||||
|  | ||||
| ostream::~ostream() = default; | ||||
| #endif  // FMT_USE_FCNTL | ||||
| FMT_END_NAMESPACE | ||||
|   | ||||
| @@ -4,17 +4,28 @@ clibrary( | ||||
|     name="hfsutils", | ||||
|     srcs=[ | ||||
|         "./libhfs/block.c", | ||||
|         "./libhfs/block.h", | ||||
|         "./libhfs/btree.c", | ||||
|         "./libhfs/btree.h", | ||||
|         "./libhfs/data.c", | ||||
|         "./libhfs/data.h", | ||||
|         "./libhfs/file.c", | ||||
|         "./libhfs/file.h", | ||||
|         "./libhfs/hfs.c", | ||||
|         "./libhfs/hfs.h", | ||||
|         "./libhfs/low.c", | ||||
|         "./libhfs/low.h", | ||||
|         "./libhfs/medium.c", | ||||
|         "./libhfs/medium.h", | ||||
|         "./libhfs/memcmp.c", | ||||
|         "./libhfs/node.c", | ||||
|         "./libhfs/node.h", | ||||
|         "./libhfs/record.c", | ||||
|         "./libhfs/record.h", | ||||
|         "./libhfs/version.c", | ||||
|         "./libhfs/version.h", | ||||
|         "./libhfs/volume.c", | ||||
|         "./libhfs/volume.h", | ||||
|     ], | ||||
|     hdrs={ | ||||
|         "apple.h": "./libhfs/apple.h", | ||||
|   | ||||
							
								
								
									
										23
									
								
								dep/lexy/LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								dep/lexy/LICENSE
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| Boost Software License - Version 1.0 - August 17th, 2003 | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person or organization | ||||
| obtaining a copy of the software and accompanying documentation covered by | ||||
| this license (the "Software") to use, reproduce, display, distribute, | ||||
| execute, and transmit the Software, and to prepare derivative works of the | ||||
| Software, and to permit third-parties to whom the Software is furnished to | ||||
| do so, all subject to the following: | ||||
|  | ||||
| The copyright notices in the Software and this entire statement, including | ||||
| the above license grant, this restriction and the following disclaimer, | ||||
| must be included in all copies of the Software, in whole or in part, and | ||||
| all derivative works of the Software, unless such copies or derivative | ||||
| works are solely in the form of machine-executable object code generated by | ||||
| a source language processor. | ||||
|  | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT | ||||
| SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE | ||||
| FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, | ||||
| ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||||
| DEALINGS IN THE SOFTWARE. | ||||
							
								
								
									
										175
									
								
								dep/lexy/README.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								dep/lexy/README.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,175 @@ | ||||
| = lexy | ||||
|  | ||||
| ifdef::env-github[] | ||||
| image:https://img.shields.io/endpoint?url=https%3A%2F%2Fwww.jonathanmueller.dev%2Fproject%2Flexy%2Findex.json[Project Status,link=https://www.jonathanmueller.dev/project/] | ||||
| image:https://github.com/foonathan/lexy/workflows/Main%20CI/badge.svg[Build Status] | ||||
| image:https://img.shields.io/badge/try_it_online-blue[Playground,link=https://lexy.foonathan.net/playground] | ||||
| endif::[] | ||||
|  | ||||
| lexy is a parser combinator library for {cpp}17 and onwards. | ||||
| It allows you to write a parser by specifying it in a convenient {cpp} DSL, | ||||
| which gives you all the flexibility and control of a handwritten parser without any of the manual work. | ||||
|  | ||||
| ifdef::env-github[] | ||||
| *Documentation*: https://lexy.foonathan.net/[lexy.foonathan.net] | ||||
| endif::[] | ||||
|  | ||||
| .IPv4 address parser | ||||
| -- | ||||
| ifndef::env-github[] | ||||
| [.godbolt-example] | ||||
| .+++<a href="https://godbolt.org/z/scvajjE17", title="Try it online">{{< svg "icons/play.svg" >}}</a>+++ | ||||
| endif::[] | ||||
| [source,cpp] | ||||
| ---- | ||||
| namespace dsl = lexy::dsl; | ||||
|  | ||||
| // Parse an IPv4 address into a `std::uint32_t`. | ||||
| struct ipv4_address | ||||
| { | ||||
|     // What is being matched. | ||||
|     static constexpr auto rule = []{ | ||||
|         // Match a sequence of (decimal) digits and convert it into a std::uint8_t. | ||||
|         auto octet = dsl::integer<std::uint8_t>; | ||||
|  | ||||
|         // Match four of them separated by periods. | ||||
|         return dsl::times<4>(octet, dsl::sep(dsl::period)) + dsl::eof; | ||||
|     }(); | ||||
|  | ||||
|     // How the matched output is being stored. | ||||
|     static constexpr auto value | ||||
|         = lexy::callback<std::uint32_t>([](std::uint8_t a, std::uint8_t b, std::uint8_t c, std::uint8_t d) { | ||||
|             return (a << 24) | (b << 16) | (c << 8) | d; | ||||
|         }); | ||||
| }; | ||||
| ---- | ||||
| -- | ||||
|  | ||||
| == Features | ||||
|  | ||||
| Full control:: | ||||
|   * *Describe the parser, not some abstract grammar*: | ||||
|     Unlike parser generators that use some table driven magic for parsing, lexy's grammar is just syntax sugar for a hand-written recursive descent parser. | ||||
|     The parsing algorithm does exactly what you've instructed it to do -- no more ambiguities or weird shift/reduce errors! | ||||
|   * *No implicit backtracking or lookahead*: | ||||
|     It will only backtrack when you say it should, and only lookahead when and how far you want it. | ||||
|     Don't worry about rules that have side-effects, they won't be executed unnecessarily thanks to the user-specified lookahead conditions. | ||||
|     https://lexy.foonathan.net/playground?example=peek[Try it online]. | ||||
|   * *Escape hatch for manual parsing*: | ||||
|     Sometimes you want to parse something that can't be expressed easily with lexy's facilities. | ||||
|     Don't worry, you can integrate a hand-written parser into the grammar at any point. | ||||
|     https://lexy.foonathan.net/playground/?example=scan[Try it online]. | ||||
|   * *Tracing*: | ||||
|     Figure out why the grammar isn't working the way you want it to. | ||||
|     https://lexy.foonathan.net/playground/?example=trace&mode=trace[Try it online]. | ||||
|  | ||||
| Easily integrated:: | ||||
|   * *A pure {cpp} DSL*: | ||||
|     No need to use an external grammar file; embed the grammar directly in your {cpp} project using operator overloading and functions. | ||||
|   * *Bring your own data structures*: | ||||
|     You can directly store results into your own types and have full control over all heap allocations. | ||||
|   * *Fully `constexpr` parsing*: | ||||
|     You want to parse a string literal at compile-time? You can do so. | ||||
|   * *Minimal standard library dependencies*: | ||||
|     The core parsing library only depends on fundamental headers such as `<type_traits>` or `<cstddef>`; no big includes like `<vector>` or `<algorithm>`. | ||||
|   * *Header-only core library* (by necessity, not by choice -- it's `constexpr` after all). | ||||
|  | ||||
| ifdef::env-github[Designed for text::] | ||||
| ifndef::env-github[Designed for text (e.g. {{< github-example json >}}, {{< github-example xml >}}, {{< github-example email >}}) ::] | ||||
|   * *Unicode support*: parse UTF-8, UTF-16, or UTF-32, and access the Unicode character database to query char classes or perform case folding. | ||||
|     https://lexy.foonathan.net/playground?example=identifier-unicode[Try it online]. | ||||
|   * *Convenience*: | ||||
|     Built-in rules for parsing nested structures, quotes and escape sequences. | ||||
|     https://lexy.foonathan.net/playground?example=parenthesized[Try it online]. | ||||
|   * *Automatic whitespace skipping*: | ||||
|     No need to manually handle whitespace or comments. | ||||
|     https://lexy.foonathan.net/playground/?example=whitespace_comment[Try it online]. | ||||
|  | ||||
| ifdef::env-github[Designed for programming languages::] | ||||
| ifndef::env-github[Designed for programming languages (e.g. {{< github-example calculator >}}, {{< github-example shell >}})::] | ||||
|   * *Keyword and identifier parsing*: | ||||
|     Reserve a set of keywords that won't be matched as regular identifiers. | ||||
|     https://lexy.foonathan.net/playground/?example=reserved_identifier[Try it online]. | ||||
|   * *Operator parsing*: | ||||
|     Parse unary/binary operators with different precedences and associativity, including chained comparisons `a < b < c`. | ||||
|     https://lexy.foonathan.net/playground/?example=expr[Try it online]. | ||||
|   * *Automatic error recovery*: | ||||
|     Log an error, recover, and continue parsing! | ||||
|     https://lexy.foonathan.net/playground/?example=recover[Try it online]. | ||||
|  | ||||
| ifdef::env-github[Designed for binary input::] | ||||
| ifndef::env-github[Designed for binary input (e.g. {{< github-example protobuf >}})::] | ||||
|   * *Bytes*: Rules for parsing `N` bytes or Nbit big/little endian integer. | ||||
|   * *Bits*: Rules for parsing individual bit patterns. | ||||
|   * *Blobs*: Rules for parsing TLV formats. | ||||
|  | ||||
| == FAQ | ||||
|  | ||||
| Why should I use lexy over XYZ?:: | ||||
|   lexy is closest to other PEG parsers. | ||||
|   However, they usually do more implicit backtracking, which can hurt performance and you need to be very careful with rules that have side-effects. | ||||
|   This is not the case for lexy, where backtracking is controlled using branch conditions. | ||||
|   lexy also gives you a lot of control over error reporting, supports error recovery, special support for operator precedence parsing, and other advanced features. | ||||
|  | ||||
|   http://boost-spirit.com/home/[Boost.Spirit]::: | ||||
|     The main difference: it is not a Boost library. | ||||
|     In addition, Boost.Spirit is quite old and doesn't support e.g. non-common ranges as input.  | ||||
|     Boost.Spirit also eagerly creates attributes from the rules, which can lead to nested tuples/variants while lexy uses callbacks which enables zero-copy parsing directly into your own data structure. | ||||
|     However, lexy's grammar is more verbose and designed to parser bigger grammars instead of the small one-off rules that Boost.Spirit is good at. | ||||
|   https://github.com/taocpp/PEGTL[PEGTL]::: | ||||
|     PEGTL is very similar and was a big inspiration. | ||||
|     The biggest difference is that lexy uses an operator based DSL instead of inheriting from templated classes as PEGTL does; | ||||
|     depending on your preference this can be an advantage or disadvantage. | ||||
|   Hand-written Parsers::: | ||||
|     Writing a handwritten parser is more manual work and error prone. | ||||
|     lexy automates that away without having to sacrifice control. | ||||
|     You can use it to quickly prototype a parser and then slowly replace more and more with a handwritten parser over time; | ||||
|     mixing a hand-written parser and a lexy grammar works seamlessly. | ||||
|  | ||||
| How bad are the compilation times?:: | ||||
| They're not as bad as you might expect (in debug mode, that is). | ||||
| + | ||||
| The example JSON parser compiles in about 2s on my machine. | ||||
| If we remove all the lexy specific parts and just benchmark the time it takes for the compiler to process the datastructure (and stdlib includes), | ||||
| that takes about 700ms. | ||||
| If we validate JSON only instead of parsing it, so remove the data structures and keep only the lexy specific parts, we're looking at about 840ms. | ||||
| + | ||||
| Keep in mind, that you can fully isolate lexy in a single translation unit that only needs to be touched when you change the parser. | ||||
| You can also split a lexy grammar into multiple translation units using the `dsl::subgrammar` rule. | ||||
|  | ||||
| How bad are the {cpp} error messages if you mess something up?:: | ||||
|   They're certainly worse than the error message lexy gives you. | ||||
|   The big problem here is that the first line gives you the error, followed by dozens of template instantiations, which end at your `lexy::parse` call. | ||||
|   Besides providing an external tool to filter those error messages, there is nothing I can do about that. | ||||
|  | ||||
| How fast is it?:: | ||||
|   Benchmarks are available in the `benchmarks/` directory. | ||||
|   A sample result of the JSON validator benchmark which compares the example JSON parser with various other implementations is available https://lexy.foonathan.net/benchmark_json/[here]. | ||||
|  | ||||
| Why is it called lexy?:: | ||||
|   I previously had a tokenizer library called foonathan/lex. | ||||
|   I've tried adding a parser to it, but found that the line between pure tokenization and parsing has become increasingly blurred. | ||||
|   lexy is a re-imagination on of the parser I've added to foonathan/lex, and I've simply kept a similar name. | ||||
|  | ||||
| ifdef::env-github[] | ||||
| == Documentation | ||||
|  | ||||
| The documentation, including tutorials, reference documentation, and an interactive playground can be found at https://lexy.foonathan.net/[lexy.foonathan.net]. | ||||
|  | ||||
| A minimal `CMakeLists.txt` that uses lexy can look like this: | ||||
|  | ||||
| .`CMakeLists.txt` | ||||
| ```cmake | ||||
| project(lexy-example) | ||||
|  | ||||
| include(FetchContent) | ||||
| FetchContent_Declare(lexy URL https://lexy.foonathan.net/download/lexy-src.zip) | ||||
| FetchContent_MakeAvailable(lexy) | ||||
|  | ||||
| add_executable(lexy_example) | ||||
| target_sources(lexy_example PRIVATE main.cpp) | ||||
| target_link_libraries(lexy_example PRIVATE foonathan::lexy) | ||||
| ``` | ||||
|  | ||||
| endif::[] | ||||
|  | ||||
							
								
								
									
										2
									
								
								dep/lexy/UPSTREAM.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								dep/lexy/UPSTREAM.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| This is a heavily truncated copy of https://github.com/foonathan/lexy, commit | ||||
| 20926cf. | ||||
							
								
								
									
										11
									
								
								dep/lexy/build.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								dep/lexy/build.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| from build.c import cxxlibrary | ||||
| from glob import glob | ||||
|  | ||||
| cxxlibrary( | ||||
|     name="lexy", | ||||
|     srcs=[], | ||||
|     hdrs={ | ||||
|         h: f"./include/{h}" | ||||
|         for h in glob("**/*.hpp", root_dir="dep/lexy/include", recursive=True) | ||||
|     }, | ||||
| ) | ||||
							
								
								
									
										68
									
								
								dep/lexy/include/lexy/_detail/any_ref.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								dep/lexy/include/lexy/_detail/any_ref.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| // Copyright (C) 2020-2025 Jonathan Müller and lexy contributors | ||||
| // SPDX-License-Identifier: BSL-1.0 | ||||
|  | ||||
| #ifndef LEXY_DETAIL_ANY_REF_HPP_INCLUDED | ||||
| #define LEXY_DETAIL_ANY_REF_HPP_INCLUDED | ||||
|  | ||||
| #include <lexy/_detail/config.hpp> | ||||
|  | ||||
| // Essentially a void*, but we can cast it in a constexpr context. | ||||
| // The cost is an extra layer of indirection. | ||||
|  | ||||
| namespace lexy::_detail | ||||
| { | ||||
| template <typename T> | ||||
| class any_holder; | ||||
|  | ||||
| // Store a pointer to this instead of a void*. | ||||
| class any_base | ||||
| { | ||||
| public: | ||||
|     any_base(const any_base&)            = delete; | ||||
|     any_base& operator=(const any_base&) = delete; | ||||
|  | ||||
|     template <typename T> | ||||
|     constexpr T& get() noexcept | ||||
|     { | ||||
|         return static_cast<any_holder<T>*>(this)->get(); | ||||
|     } | ||||
|     template <typename T> | ||||
|     constexpr const T& get() const noexcept | ||||
|     { | ||||
|         return static_cast<const any_holder<T>*>(this)->get(); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     constexpr any_base() = default; | ||||
|     ~any_base()          = default; | ||||
|  | ||||
|     template <typename T> | ||||
|     friend class any_holder; | ||||
| }; | ||||
|  | ||||
| using any_ref  = any_base*; | ||||
| using any_cref = const any_base*; | ||||
|  | ||||
| // Need to store the object in here. | ||||
| template <typename T> | ||||
| class any_holder : public any_base | ||||
| { | ||||
| public: | ||||
|     constexpr explicit any_holder(T&& obj) : _obj(LEXY_MOV(obj)) {} | ||||
|  | ||||
|     constexpr T& get() noexcept | ||||
|     { | ||||
|         return _obj; | ||||
|     } | ||||
|     constexpr const T& get() const noexcept | ||||
|     { | ||||
|         return _obj; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     T _obj; | ||||
| }; | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| #endif // LEXY_DETAIL_ANY_REF_HPP_INCLUDED | ||||
|  | ||||
							
								
								
									
										51
									
								
								dep/lexy/include/lexy/_detail/assert.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								dep/lexy/include/lexy/_detail/assert.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | ||||
| // Copyright (C) 2020-2025 Jonathan Müller and lexy contributors | ||||
| // SPDX-License-Identifier: BSL-1.0 | ||||
|  | ||||
| #ifndef LEXY_DETAIL_ASSERT_HPP_INCLUDED | ||||
| #define LEXY_DETAIL_ASSERT_HPP_INCLUDED | ||||
|  | ||||
| #include <lexy/_detail/config.hpp> | ||||
|  | ||||
| #ifndef LEXY_ENABLE_ASSERT | ||||
|  | ||||
| // By default, enable assertions if NDEBUG is not defined. | ||||
|  | ||||
| #    if NDEBUG | ||||
| #        define LEXY_ENABLE_ASSERT 0 | ||||
| #    else | ||||
| #        define LEXY_ENABLE_ASSERT 1 | ||||
| #    endif | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #if LEXY_ENABLE_ASSERT | ||||
|  | ||||
| // We want assertions: use assert() if that's available, otherwise abort. | ||||
| // We don't use assert() directly as that's not constexpr. | ||||
|  | ||||
| #    if NDEBUG | ||||
|  | ||||
| #        include <cstdlib> | ||||
| #        define LEXY_PRECONDITION(Expr) ((Expr) ? void(0) : std::abort()) | ||||
| #        define LEXY_ASSERT(Expr, Msg) ((Expr) ? void(0) : std::abort()) | ||||
|  | ||||
| #    else | ||||
|  | ||||
| #        include <cassert> | ||||
|  | ||||
| #        define LEXY_PRECONDITION(Expr) ((Expr) ? void(0) : assert(Expr)) | ||||
| #        define LEXY_ASSERT(Expr, Msg) ((Expr) ? void(0) : assert((Expr) && (Msg))) | ||||
|  | ||||
| #    endif | ||||
|  | ||||
| #else | ||||
|  | ||||
| // We don't want assertions. | ||||
|  | ||||
| #    define LEXY_PRECONDITION(Expr) static_cast<void>(sizeof(Expr)) | ||||
| #    define LEXY_ASSERT(Expr, Msg) static_cast<void>(sizeof(Expr)) | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #endif // LEXY_DETAIL_ASSERT_HPP_INCLUDED | ||||
|  | ||||
							
								
								
									
										160
									
								
								dep/lexy/include/lexy/_detail/buffer_builder.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								dep/lexy/include/lexy/_detail/buffer_builder.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,160 @@ | ||||
| // Copyright (C) 2020-2025 Jonathan Müller and lexy contributors | ||||
| // SPDX-License-Identifier: BSL-1.0 | ||||
|  | ||||
| #ifndef LEXY_DETAIL_BUFFER_BUILDER_HPP_INCLUDED | ||||
| #define LEXY_DETAIL_BUFFER_BUILDER_HPP_INCLUDED | ||||
|  | ||||
| #include <cstring> | ||||
| #include <lexy/_detail/assert.hpp> | ||||
| #include <lexy/_detail/config.hpp> | ||||
| #include <lexy/_detail/iterator.hpp> | ||||
| #include <new> | ||||
|  | ||||
| namespace lexy::_detail | ||||
| { | ||||
| // Builds a buffer: it has a read are and a write area. | ||||
| // The characters in the read area are already valid and can be read. | ||||
| // The characters in the write area are not valid, but can be written too. | ||||
| template <typename T> | ||||
| class buffer_builder | ||||
| { | ||||
|     static_assert(std::is_trivial_v<T>); | ||||
|  | ||||
|     static constexpr std::size_t total_size_bytes = 1024; | ||||
|     static constexpr std::size_t stack_buffer_size | ||||
|         = (total_size_bytes - 3 * sizeof(T*)) / sizeof(T); | ||||
|     static constexpr auto growth_factor = 2; | ||||
|  | ||||
| public: | ||||
|     buffer_builder() noexcept : _data(_stack_buffer), _read_size(0), _write_size(stack_buffer_size) | ||||
|     { | ||||
|         static_assert(sizeof(*this) == total_size_bytes, "invalid buffer size calculation"); | ||||
|     } | ||||
|  | ||||
|     ~buffer_builder() noexcept | ||||
|     { | ||||
|         // Free memory if we allocated any. | ||||
|         if (_data != _stack_buffer) | ||||
|             ::operator delete(_data); | ||||
|     } | ||||
|  | ||||
|     buffer_builder(const buffer_builder&)            = delete; | ||||
|     buffer_builder& operator=(const buffer_builder&) = delete; | ||||
|  | ||||
|     // The total capacity: read + write. | ||||
|     std::size_t capacity() const noexcept | ||||
|     { | ||||
|         return _read_size + _write_size; | ||||
|     } | ||||
|  | ||||
|     // The read area. | ||||
|     const T* read_data() const noexcept | ||||
|     { | ||||
|         return _data; | ||||
|     } | ||||
|     std::size_t read_size() const noexcept | ||||
|     { | ||||
|         return _read_size; | ||||
|     } | ||||
|  | ||||
|     // The write area. | ||||
|     T* write_data() noexcept | ||||
|     { | ||||
|         return _data + _read_size; | ||||
|     } | ||||
|     std::size_t write_size() const noexcept | ||||
|     { | ||||
|         return _write_size; | ||||
|     } | ||||
|  | ||||
|     // Clears the read area. | ||||
|     void clear() noexcept | ||||
|     { | ||||
|         _write_size += _read_size; | ||||
|         _read_size = 0; | ||||
|     } | ||||
|  | ||||
|     // Takes the first n characters of the write area and appends them to the read area. | ||||
|     void commit(std::size_t n) noexcept | ||||
|     { | ||||
|         LEXY_PRECONDITION(n <= _write_size); | ||||
|         _read_size += n; | ||||
|         _write_size -= n; | ||||
|     } | ||||
|  | ||||
|     // Increases the write area, invalidates all pointers. | ||||
|     void grow() | ||||
|     { | ||||
|         const auto cur_cap = capacity(); | ||||
|         const auto new_cap = growth_factor * cur_cap; | ||||
|  | ||||
|         // Allocate new memory. | ||||
|         auto memory = static_cast<T*>(::operator new(new_cap * sizeof(T))); | ||||
|         // Copy the read area into the new memory. | ||||
|         std::memcpy(memory, _data, _read_size); | ||||
|  | ||||
|         // Release the old memory, if there was any. | ||||
|         if (_data != _stack_buffer) | ||||
|             ::operator delete(_data); | ||||
|  | ||||
|         // Update for the new area. | ||||
|         _data = memory; | ||||
|         // _read_size hasn't been changed | ||||
|         _write_size = new_cap - _read_size; | ||||
|     } | ||||
|  | ||||
|     //=== iterator ===// | ||||
|     // Stable iterator over the memory. | ||||
|     class stable_iterator : public forward_iterator_base<stable_iterator, const T> | ||||
|     { | ||||
|     public: | ||||
|         constexpr stable_iterator() = default; | ||||
|  | ||||
|         explicit constexpr stable_iterator(const _detail::buffer_builder<T>& buffer, | ||||
|                                            std::size_t                       idx) noexcept | ||||
|         : _buffer(&buffer), _idx(idx) | ||||
|         {} | ||||
|  | ||||
|         constexpr const T& deref() const noexcept | ||||
|         { | ||||
|             LEXY_PRECONDITION(_idx != _buffer->read_size()); | ||||
|             return _buffer->read_data()[_idx]; | ||||
|         } | ||||
|  | ||||
|         constexpr void increment() noexcept | ||||
|         { | ||||
|             LEXY_PRECONDITION(_idx != _buffer->read_size()); | ||||
|             ++_idx; | ||||
|         } | ||||
|  | ||||
|         constexpr bool equal(stable_iterator rhs) const noexcept | ||||
|         { | ||||
|             if (!_buffer || !rhs._buffer) | ||||
|                 return !_buffer && !rhs._buffer; | ||||
|             else | ||||
|             { | ||||
|                 LEXY_PRECONDITION(_buffer == rhs._buffer); | ||||
|                 return _idx == rhs._idx; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         constexpr std::size_t index() const noexcept | ||||
|         { | ||||
|             return _idx; | ||||
|         } | ||||
|  | ||||
|     private: | ||||
|         const _detail::buffer_builder<T>* _buffer = nullptr; | ||||
|         std::size_t                       _idx    = 0; | ||||
|     }; | ||||
|  | ||||
| private: | ||||
|     T*          _data; | ||||
|     std::size_t _read_size; | ||||
|     std::size_t _write_size; | ||||
|     T           _stack_buffer[stack_buffer_size]; | ||||
| }; | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| #endif // LEXY_DETAIL_BUFFER_BUILDER_HPP_INCLUDED | ||||
|  | ||||
							
								
								
									
										368
									
								
								dep/lexy/include/lexy/_detail/code_point.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										368
									
								
								dep/lexy/include/lexy/_detail/code_point.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,368 @@ | ||||
| // Copyright (C) 2020-2025 Jonathan Müller and lexy contributors | ||||
| // SPDX-License-Identifier: BSL-1.0 | ||||
|  | ||||
| #ifndef LEXY_DETAIL_CODE_POINT_HPP_INCLUDED | ||||
| #define LEXY_DETAIL_CODE_POINT_HPP_INCLUDED | ||||
|  | ||||
| #include <lexy/input/base.hpp> | ||||
|  | ||||
| //=== encoding ===// | ||||
| namespace lexy::_detail | ||||
| { | ||||
| template <typename Encoding> | ||||
| constexpr std::size_t encode_code_point(char32_t cp, typename Encoding::char_type* buffer, | ||||
|                                         std::size_t size) | ||||
| { | ||||
|     if constexpr (std::is_same_v<Encoding, lexy::ascii_encoding>) | ||||
|     { | ||||
|         LEXY_PRECONDITION(size >= 1); | ||||
|  | ||||
|         *buffer = char(cp); | ||||
|         return 1; | ||||
|     } | ||||
|     else if constexpr (std::is_same_v<Encoding, | ||||
|                                       lexy::utf8_encoding> // | ||||
|                        || std::is_same_v<Encoding, lexy::utf8_char_encoding>) | ||||
|     { | ||||
|         using char_type = typename Encoding::char_type; | ||||
|         // Taken from http://www.herongyang.com/Unicode/UTF-8-UTF-8-Encoding-Algorithm.html. | ||||
|         if (cp <= 0x7F) | ||||
|         { | ||||
|             LEXY_PRECONDITION(size >= 1); | ||||
|  | ||||
|             buffer[0] = char_type(cp); | ||||
|             return 1; | ||||
|         } | ||||
|         else if (cp <= 0x07'FF) | ||||
|         { | ||||
|             LEXY_PRECONDITION(size >= 2); | ||||
|  | ||||
|             auto first  = (cp >> 6) & 0x1F; | ||||
|             auto second = (cp >> 0) & 0x3F; | ||||
|  | ||||
|             buffer[0] = char_type(0xC0 | first); | ||||
|             buffer[1] = char_type(0x80 | second); | ||||
|             return 2; | ||||
|         } | ||||
|         else if (cp <= 0xFF'FF) | ||||
|         { | ||||
|             LEXY_PRECONDITION(size >= 3); | ||||
|  | ||||
|             auto first  = (cp >> 12) & 0x0F; | ||||
|             auto second = (cp >> 6) & 0x3F; | ||||
|             auto third  = (cp >> 0) & 0x3F; | ||||
|  | ||||
|             buffer[0] = char_type(0xE0 | first); | ||||
|             buffer[1] = char_type(0x80 | second); | ||||
|             buffer[2] = char_type(0x80 | third); | ||||
|             return 3; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             LEXY_PRECONDITION(size >= 4); | ||||
|  | ||||
|             auto first  = (cp >> 18) & 0x07; | ||||
|             auto second = (cp >> 12) & 0x3F; | ||||
|             auto third  = (cp >> 6) & 0x3F; | ||||
|             auto fourth = (cp >> 0) & 0x3F; | ||||
|  | ||||
|             buffer[0] = char_type(0xF0 | first); | ||||
|             buffer[1] = char_type(0x80 | second); | ||||
|             buffer[2] = char_type(0x80 | third); | ||||
|             buffer[3] = char_type(0x80 | fourth); | ||||
|             return 4; | ||||
|         } | ||||
|     } | ||||
|     else if constexpr (std::is_same_v<Encoding, lexy::utf16_encoding>) | ||||
|     { | ||||
|         if (cp <= 0xFF'FF) | ||||
|         { | ||||
|             LEXY_PRECONDITION(size >= 1); | ||||
|  | ||||
|             buffer[0] = char16_t(cp); | ||||
|             return 1; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // Algorithm implemented from | ||||
|             // https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF. | ||||
|             LEXY_PRECONDITION(size >= 2); | ||||
|  | ||||
|             auto u_prime       = cp - 0x1'0000; | ||||
|             auto high_ten_bits = u_prime >> 10; | ||||
|             auto low_ten_bits  = u_prime & 0b0000'0011'1111'1111; | ||||
|  | ||||
|             buffer[0] = char16_t(0xD800 + high_ten_bits); | ||||
|             buffer[1] = char16_t(0xDC00 + low_ten_bits); | ||||
|             return 2; | ||||
|         } | ||||
|     } | ||||
|     else if constexpr (std::is_same_v<Encoding, lexy::utf32_encoding>) | ||||
|     { | ||||
|         LEXY_PRECONDITION(size >= 1); | ||||
|  | ||||
|         *buffer = cp; | ||||
|         return 1; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         static_assert(lexy::_detail::error<Encoding>, | ||||
|                       "cannot encode a code point in this encoding"); | ||||
|         (void)cp; | ||||
|         (void)buffer; | ||||
|         (void)size; | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| //=== parsing ===// | ||||
| namespace lexy::_detail | ||||
| { | ||||
| enum class cp_error | ||||
| { | ||||
|     success, | ||||
|     eof, | ||||
|     leads_with_trailing, | ||||
|     missing_trailing, | ||||
|     surrogate, | ||||
|     overlong_sequence, | ||||
|     out_of_range, | ||||
| }; | ||||
|  | ||||
| template <typename Reader> | ||||
| struct cp_result | ||||
| { | ||||
|     char32_t                cp; | ||||
|     cp_error                error; | ||||
|     typename Reader::marker end; | ||||
| }; | ||||
|  | ||||
| template <typename Reader> | ||||
| constexpr cp_result<Reader> parse_code_point(Reader reader) | ||||
| { | ||||
|     if constexpr (std::is_same_v<typename Reader::encoding, lexy::ascii_encoding>) | ||||
|     { | ||||
|         if (reader.peek() == Reader::encoding::eof()) | ||||
|             return {{}, cp_error::eof, reader.current()}; | ||||
|  | ||||
|         auto cur = reader.peek(); | ||||
|         reader.bump(); | ||||
|  | ||||
|         auto cp = static_cast<char32_t>(cur); | ||||
|         if (cp <= 0x7F) | ||||
|             return {cp, cp_error::success, reader.current()}; | ||||
|         else | ||||
|             return {cp, cp_error::out_of_range, reader.current()}; | ||||
|     } | ||||
|     else if constexpr (std::is_same_v<typename Reader::encoding, lexy::utf8_encoding> // | ||||
|                        || std::is_same_v<typename Reader::encoding, lexy::utf8_char_encoding>) | ||||
|     { | ||||
|         using uchar_t                = unsigned char; | ||||
|         constexpr auto payload_lead1 = 0b0111'1111; | ||||
|         constexpr auto payload_lead2 = 0b0001'1111; | ||||
|         constexpr auto payload_lead3 = 0b0000'1111; | ||||
|         constexpr auto payload_lead4 = 0b0000'0111; | ||||
|         constexpr auto payload_cont  = 0b0011'1111; | ||||
|  | ||||
|         constexpr auto pattern_lead1 = 0b0 << 7; | ||||
|         constexpr auto pattern_lead2 = 0b110 << 5; | ||||
|         constexpr auto pattern_lead3 = 0b1110 << 4; | ||||
|         constexpr auto pattern_lead4 = 0b11110 << 3; | ||||
|         constexpr auto pattern_cont  = 0b10 << 6; | ||||
|  | ||||
|         auto first = uchar_t(reader.peek()); | ||||
|         if ((first & ~payload_lead1) == pattern_lead1) | ||||
|         { | ||||
|             // ASCII character. | ||||
|             reader.bump(); | ||||
|             return {first, cp_error::success, reader.current()}; | ||||
|         } | ||||
|         else if ((first & ~payload_cont) == pattern_cont) | ||||
|         { | ||||
|             return {{}, cp_error::leads_with_trailing, reader.current()}; | ||||
|         } | ||||
|         else if ((first & ~payload_lead2) == pattern_lead2) | ||||
|         { | ||||
|             reader.bump(); | ||||
|  | ||||
|             auto second = uchar_t(reader.peek()); | ||||
|             if ((second & ~payload_cont) != pattern_cont) | ||||
|                 return {{}, cp_error::missing_trailing, reader.current()}; | ||||
|             reader.bump(); | ||||
|  | ||||
|             auto result = char32_t(first & payload_lead2); | ||||
|             result <<= 6; | ||||
|             result |= char32_t(second & payload_cont); | ||||
|  | ||||
|             // C0 and C1 are overlong ASCII. | ||||
|             if (first == 0xC0 || first == 0xC1) | ||||
|                 return {result, cp_error::overlong_sequence, reader.current()}; | ||||
|             else | ||||
|                 return {result, cp_error::success, reader.current()}; | ||||
|         } | ||||
|         else if ((first & ~payload_lead3) == pattern_lead3) | ||||
|         { | ||||
|             reader.bump(); | ||||
|  | ||||
|             auto second = uchar_t(reader.peek()); | ||||
|             if ((second & ~payload_cont) != pattern_cont) | ||||
|                 return {{}, cp_error::missing_trailing, reader.current()}; | ||||
|             reader.bump(); | ||||
|  | ||||
|             auto third = uchar_t(reader.peek()); | ||||
|             if ((third & ~payload_cont) != pattern_cont) | ||||
|                 return {{}, cp_error::missing_trailing, reader.current()}; | ||||
|             reader.bump(); | ||||
|  | ||||
|             auto result = char32_t(first & payload_lead3); | ||||
|             result <<= 6; | ||||
|             result |= char32_t(second & payload_cont); | ||||
|             result <<= 6; | ||||
|             result |= char32_t(third & payload_cont); | ||||
|  | ||||
|             auto cp = result; | ||||
|             if (0xD800 <= cp && cp <= 0xDFFF) | ||||
|                 return {cp, cp_error::surrogate, reader.current()}; | ||||
|             else if (first == 0xE0 && second < 0xA0) | ||||
|                 return {cp, cp_error::overlong_sequence, reader.current()}; | ||||
|             else | ||||
|                 return {cp, cp_error::success, reader.current()}; | ||||
|         } | ||||
|         else if ((first & ~payload_lead4) == pattern_lead4) | ||||
|         { | ||||
|             reader.bump(); | ||||
|  | ||||
|             auto second = uchar_t(reader.peek()); | ||||
|             if ((second & ~payload_cont) != pattern_cont) | ||||
|                 return {{}, cp_error::missing_trailing, reader.current()}; | ||||
|             reader.bump(); | ||||
|  | ||||
|             auto third = uchar_t(reader.peek()); | ||||
|             if ((third & ~payload_cont) != pattern_cont) | ||||
|                 return {{}, cp_error::missing_trailing, reader.current()}; | ||||
|             reader.bump(); | ||||
|  | ||||
|             auto fourth = uchar_t(reader.peek()); | ||||
|             if ((fourth & ~payload_cont) != pattern_cont) | ||||
|                 return {{}, cp_error::missing_trailing, reader.current()}; | ||||
|             reader.bump(); | ||||
|  | ||||
|             auto result = char32_t(first & payload_lead4); | ||||
|             result <<= 6; | ||||
|             result |= char32_t(second & payload_cont); | ||||
|             result <<= 6; | ||||
|             result |= char32_t(third & payload_cont); | ||||
|             result <<= 6; | ||||
|             result |= char32_t(fourth & payload_cont); | ||||
|  | ||||
|             auto cp = result; | ||||
|             if (cp > 0x10'FFFF) | ||||
|                 return {cp, cp_error::out_of_range, reader.current()}; | ||||
|             else if (first == 0xF0 && second < 0x90) | ||||
|                 return {cp, cp_error::overlong_sequence, reader.current()}; | ||||
|             else | ||||
|                 return {cp, cp_error::success, reader.current()}; | ||||
|         } | ||||
|         else // FE or FF | ||||
|         { | ||||
|             return {{}, cp_error::eof, reader.current()}; | ||||
|         } | ||||
|     } | ||||
|     else if constexpr (std::is_same_v<typename Reader::encoding, lexy::utf16_encoding>) | ||||
|     { | ||||
|         constexpr auto payload1 = 0b0000'0011'1111'1111; | ||||
|         constexpr auto payload2 = payload1; | ||||
|  | ||||
|         constexpr auto pattern1 = 0b110110 << 10; | ||||
|         constexpr auto pattern2 = 0b110111 << 10; | ||||
|  | ||||
|         if (reader.peek() == Reader::encoding::eof()) | ||||
|             return {{}, cp_error::eof, reader.current()}; | ||||
|  | ||||
|         auto first = char16_t(reader.peek()); | ||||
|         if ((first & ~payload1) == pattern1) | ||||
|         { | ||||
|             reader.bump(); | ||||
|             if (reader.peek() == Reader::encoding::eof()) | ||||
|                 return {{}, cp_error::missing_trailing, reader.current()}; | ||||
|  | ||||
|             auto second = char16_t(reader.peek()); | ||||
|             if ((second & ~payload2) != pattern2) | ||||
|                 return {{}, cp_error::missing_trailing, reader.current()}; | ||||
|             reader.bump(); | ||||
|  | ||||
|             // We've got a valid code point. | ||||
|             auto result = char32_t(first & payload1); | ||||
|             result <<= 10; | ||||
|             result |= char32_t(second & payload2); | ||||
|             result |= 0x10000; | ||||
|             return {result, cp_error::success, reader.current()}; | ||||
|         } | ||||
|         else if ((first & ~payload2) == pattern2) | ||||
|         { | ||||
|             return {{}, cp_error::leads_with_trailing, reader.current()}; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // Single code unit code point; always valid. | ||||
|             reader.bump(); | ||||
|             return {first, cp_error::success, reader.current()}; | ||||
|         } | ||||
|     } | ||||
|     else if constexpr (std::is_same_v<typename Reader::encoding, lexy::utf32_encoding>) | ||||
|     { | ||||
|         if (reader.peek() == Reader::encoding::eof()) | ||||
|             return {{}, cp_error::eof, reader.current()}; | ||||
|  | ||||
|         auto cur = reader.peek(); | ||||
|         reader.bump(); | ||||
|  | ||||
|         auto cp = cur; | ||||
|         if (cp > 0x10'FFFF) | ||||
|             return {cp, cp_error::out_of_range, reader.current()}; | ||||
|         else if (0xD800 <= cp && cp <= 0xDFFF) | ||||
|             return {cp, cp_error::surrogate, reader.current()}; | ||||
|         else | ||||
|             return {cp, cp_error::success, reader.current()}; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         static_assert(lexy::_detail::error<typename Reader::encoding>, | ||||
|                       "no known code point for this encoding"); | ||||
|         return {}; | ||||
|     } | ||||
| } | ||||
|  | ||||
| template <typename Reader> | ||||
| constexpr void recover_code_point(Reader& reader, cp_result<Reader> result) | ||||
| { | ||||
|     switch (result.error) | ||||
|     { | ||||
|     case cp_error::success: | ||||
|         // Consume the entire code point. | ||||
|         reader.reset(result.end); | ||||
|         break; | ||||
|     case cp_error::eof: | ||||
|         // We don't need to do anything to "recover" from EOF. | ||||
|         break; | ||||
|  | ||||
|     case cp_error::leads_with_trailing: | ||||
|         // Invalid code unit, consume to recover. | ||||
|         LEXY_PRECONDITION(result.end.position() == reader.position()); | ||||
|         reader.bump(); | ||||
|         break; | ||||
|  | ||||
|     case cp_error::missing_trailing: | ||||
|     case cp_error::surrogate: | ||||
|     case cp_error::out_of_range: | ||||
|     case cp_error::overlong_sequence: | ||||
|         // Consume all the invalid code units to recover. | ||||
|         reader.reset(result.end); | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| #endif // LEXY_DETAIL_CODE_POINT_HPP_INCLUDED | ||||
|  | ||||
							
								
								
									
										199
									
								
								dep/lexy/include/lexy/_detail/config.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										199
									
								
								dep/lexy/include/lexy/_detail/config.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,199 @@ | ||||
| // Copyright (C) 2020-2025 Jonathan Müller and lexy contributors | ||||
| // SPDX-License-Identifier: BSL-1.0 | ||||
|  | ||||
| #ifndef LEXY_DETAIL_CONFIG_HPP_INCLUDED | ||||
| #define LEXY_DETAIL_CONFIG_HPP_INCLUDED | ||||
|  | ||||
| #include <cstddef> | ||||
| #include <type_traits> | ||||
|  | ||||
| #if defined(LEXY_USER_CONFIG_HEADER) | ||||
| #    include LEXY_USER_CONFIG_HEADER | ||||
| #elif defined(__has_include) | ||||
| #    if __has_include(<lexy_user_config.hpp>) | ||||
| #        include <lexy_user_config.hpp> | ||||
| #    elif __has_include("lexy_user_config.hpp") | ||||
| #        include "lexy_user_config.hpp" | ||||
| #    endif | ||||
| #endif | ||||
|  | ||||
| #ifndef LEXY_HAS_UNICODE_DATABASE | ||||
| #    define LEXY_HAS_UNICODE_DATABASE 0 | ||||
| #endif | ||||
|  | ||||
| #ifndef LEXY_EXPERIMENTAL | ||||
| #    define LEXY_EXPERIMENTAL 0 | ||||
| #endif | ||||
|  | ||||
| //=== utility traits===// | ||||
| #define LEXY_MOV(...) static_cast<std::remove_reference_t<decltype(__VA_ARGS__)>&&>(__VA_ARGS__) | ||||
| #define LEXY_FWD(...) static_cast<decltype(__VA_ARGS__)>(__VA_ARGS__) | ||||
|  | ||||
| #define LEXY_DECLVAL(...) lexy::_detail::declval<__VA_ARGS__>() | ||||
|  | ||||
| #define LEXY_DECAY_DECLTYPE(...) std::decay_t<decltype(__VA_ARGS__)> | ||||
|  | ||||
| /// Creates a new type from the instantiation of a template. | ||||
| /// This is used to shorten type names. | ||||
| #define LEXY_INSTANTIATION_NEWTYPE(Name, Templ, ...)                                               \ | ||||
|     struct Name : Templ<__VA_ARGS__>                                                               \ | ||||
|     {                                                                                              \ | ||||
|         using Templ<__VA_ARGS__>::Templ;                                                           \ | ||||
|     } | ||||
|  | ||||
| namespace lexy::_detail | ||||
| { | ||||
| template <typename... T> | ||||
| constexpr bool error = false; | ||||
|  | ||||
| template <typename T> | ||||
| std::add_rvalue_reference_t<T> declval(); | ||||
|  | ||||
| template <typename T> | ||||
| constexpr void swap(T& lhs, T& rhs) noexcept | ||||
| { | ||||
|     T tmp = LEXY_MOV(lhs); | ||||
|     lhs   = LEXY_MOV(rhs); | ||||
|     rhs   = LEXY_MOV(tmp); | ||||
| } | ||||
|  | ||||
| template <typename T, typename U> | ||||
| constexpr bool is_decayed_same = std::is_same_v<std::decay_t<T>, std::decay_t<U>>; | ||||
|  | ||||
| template <typename T, typename Fallback> | ||||
| using type_or = std::conditional_t<std::is_void_v<T>, Fallback, T>; | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| //=== NTTP ===// | ||||
| #ifndef LEXY_HAS_NTTP | ||||
| //   See https://github.com/foonathan/lexy/issues/15. | ||||
| #    if __cpp_nontype_template_parameter_class >= 201806 || __cpp_nontype_template_args >= 201911 | ||||
| #        define LEXY_HAS_NTTP 1 | ||||
| #    else | ||||
| #        define LEXY_HAS_NTTP 0 | ||||
| #    endif | ||||
| #endif | ||||
|  | ||||
| #if LEXY_HAS_NTTP | ||||
| #    define LEXY_NTTP_PARAM auto | ||||
| #else | ||||
| #    define LEXY_NTTP_PARAM const auto& | ||||
| #endif | ||||
|  | ||||
| //=== consteval ===// | ||||
| #ifndef LEXY_HAS_CONSTEVAL | ||||
| #    if defined(_MSC_VER) && !defined(__clang__) | ||||
| //       Currently can't handle returning strings from consteval, check back later. | ||||
| #        define LEXY_HAS_CONSTEVAL 0 | ||||
| #    elif __cpp_consteval | ||||
| #        define LEXY_HAS_CONSTEVAL 1 | ||||
| #    else | ||||
| #        define LEXY_HAS_CONSTEVAL 0 | ||||
| #    endif | ||||
| #endif | ||||
|  | ||||
| #if LEXY_HAS_CONSTEVAL | ||||
| #    define LEXY_CONSTEVAL consteval | ||||
| #else | ||||
| #    define LEXY_CONSTEVAL constexpr | ||||
| #endif | ||||
|  | ||||
| //=== constexpr ===// | ||||
| #ifndef LEXY_HAS_CONSTEXPR_DTOR | ||||
| #    if __cpp_constexpr_dynamic_alloc | ||||
| #        define LEXY_HAS_CONSTEXPR_DTOR 1 | ||||
| #    else | ||||
| #        define LEXY_HAS_CONSTEXPR_DTOR 0 | ||||
| #    endif | ||||
| #endif | ||||
|  | ||||
| #if LEXY_HAS_CONSTEXPR_DTOR | ||||
| #    define LEXY_CONSTEXPR_DTOR constexpr | ||||
| #else | ||||
| #    define LEXY_CONSTEXPR_DTOR | ||||
| #endif | ||||
|  | ||||
| //=== char8_t ===// | ||||
| #ifndef LEXY_HAS_CHAR8_T | ||||
| #    if __cpp_char8_t | ||||
| #        define LEXY_HAS_CHAR8_T 1 | ||||
| #    else | ||||
| #        define LEXY_HAS_CHAR8_T 0 | ||||
| #    endif | ||||
| #endif | ||||
|  | ||||
| #if LEXY_HAS_CHAR8_T | ||||
|  | ||||
| #    define LEXY_CHAR_OF_u8 char8_t | ||||
| #    define LEXY_CHAR8_T char8_t | ||||
| #    define LEXY_CHAR8_STR(Str) u8##Str | ||||
|  | ||||
| #else | ||||
|  | ||||
| namespace lexy | ||||
| { | ||||
| using _char8_t = unsigned char; | ||||
| } // namespace lexy | ||||
|  | ||||
| #    define LEXY_CHAR_OF_u8 char | ||||
| #    define LEXY_CHAR8_T ::lexy::_char8_t | ||||
| #    define LEXY_CHAR8_STR(Str)                                                                    \ | ||||
|         LEXY_NTTP_STRING(::lexy::_detail::type_string, u8##Str)::c_str<LEXY_CHAR8_T> | ||||
|  | ||||
| #endif | ||||
|  | ||||
| //=== endianness ===// | ||||
| #ifndef LEXY_IS_LITTLE_ENDIAN | ||||
| #    if defined(__BYTE_ORDER__) | ||||
| #        if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ | ||||
| #            define LEXY_IS_LITTLE_ENDIAN 1 | ||||
| #        elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ | ||||
| #            define LEXY_IS_LITTLE_ENDIAN 0 | ||||
| #        else | ||||
| #            error "unsupported byte order" | ||||
| #        endif | ||||
| #    elif defined(_MSC_VER) | ||||
| #        define LEXY_IS_LITTLE_ENDIAN 1 | ||||
| #    else | ||||
| #        error "unknown endianness" | ||||
| #    endif | ||||
| #endif | ||||
|  | ||||
| //=== force inline ===// | ||||
| #ifndef LEXY_FORCE_INLINE | ||||
| #    if defined(__has_cpp_attribute) | ||||
| #        if __has_cpp_attribute(gnu::always_inline) | ||||
| #            define LEXY_FORCE_INLINE [[gnu::always_inline]] | ||||
| #        endif | ||||
| #    endif | ||||
| # | ||||
| #    ifndef LEXY_FORCE_INLINE | ||||
| #        define LEXY_FORCE_INLINE inline | ||||
| #    endif | ||||
| #endif | ||||
|  | ||||
| //=== empty_member ===// | ||||
| #ifndef LEXY_EMPTY_MEMBER | ||||
|  | ||||
| #    if defined(__has_cpp_attribute) | ||||
| #        if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 11 | ||||
| //           GCC <= 11 has buggy support, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101040 | ||||
| #            define LEXY_HAS_EMPTY_MEMBER 0 | ||||
| #        elif __has_cpp_attribute(no_unique_address) | ||||
| #            define LEXY_HAS_EMPTY_MEMBER 1 | ||||
| #        endif | ||||
| #    endif | ||||
| #    ifndef LEXY_HAS_EMPTY_MEMBER | ||||
| #        define LEXY_HAS_EMPTY_MEMBER 0 | ||||
| #    endif | ||||
|  | ||||
| #    if LEXY_HAS_EMPTY_MEMBER | ||||
| #        define LEXY_EMPTY_MEMBER [[no_unique_address]] | ||||
| #    else | ||||
| #        define LEXY_EMPTY_MEMBER | ||||
| #    endif | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #endif // LEXY_DETAIL_CONFIG_HPP_INCLUDED | ||||
|  | ||||
							
								
								
									
										35
									
								
								dep/lexy/include/lexy/_detail/detect.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								dep/lexy/include/lexy/_detail/detect.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| // Copyright (C) 2020-2025 Jonathan Müller and lexy contributors | ||||
| // SPDX-License-Identifier: BSL-1.0 | ||||
|  | ||||
| #ifndef LEXY_DETAIL_DETECT_HPP_INCLUDED | ||||
| #define LEXY_DETAIL_DETECT_HPP_INCLUDED | ||||
|  | ||||
| #include <lexy/_detail/config.hpp> | ||||
|  | ||||
| namespace lexy::_detail | ||||
| { | ||||
| template <typename... Args> | ||||
| using void_t = void; | ||||
|  | ||||
| template <template <typename...> typename Op, typename Void, typename... Args> | ||||
| struct _detector : std::false_type | ||||
| { | ||||
|     template <typename Fallback> | ||||
|     using type_or = Fallback; | ||||
| }; | ||||
| template <template <typename...> typename Op, typename... Args> | ||||
| struct _detector<Op, void_t<Op<Args...>>, Args...> : std::true_type | ||||
| { | ||||
|     template <typename Fallback> | ||||
|     using type_or = Op<Args...>; | ||||
| }; | ||||
|  | ||||
| template <template <typename...> typename Op, typename... Args> | ||||
| constexpr bool is_detected = _detector<Op, void, Args...>::value; | ||||
|  | ||||
| template <typename Fallback, template <typename...> typename Op, typename... Args> | ||||
| using detected_or = typename _detector<Op, void, Args...>::template type_or<Fallback>; | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| #endif // LEXY_DETAIL_DETECT_HPP_INCLUDED | ||||
|  | ||||
							
								
								
									
										64
									
								
								dep/lexy/include/lexy/_detail/integer_sequence.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								dep/lexy/include/lexy/_detail/integer_sequence.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,64 @@ | ||||
| // Copyright (C) 2020-2025 Jonathan Müller and lexy contributors | ||||
| // SPDX-License-Identifier: BSL-1.0 | ||||
|  | ||||
| #ifndef LEXY_DETAIL_INTEGER_SEQUENCE_HPP_INCLUDED | ||||
| #define LEXY_DETAIL_INTEGER_SEQUENCE_HPP_INCLUDED | ||||
|  | ||||
| #include <lexy/_detail/config.hpp> | ||||
|  | ||||
| namespace lexy::_detail | ||||
| { | ||||
| template <typename T, T... Indices> | ||||
| struct integer_sequence | ||||
| { | ||||
|     using type = integer_sequence<T, Indices...>; | ||||
| }; | ||||
| template <std::size_t... Indices> | ||||
| using index_sequence = integer_sequence<std::size_t, Indices...>; | ||||
|  | ||||
| #if defined(__clang__) | ||||
| template <std::size_t Size> | ||||
| using make_index_sequence = __make_integer_seq<integer_sequence, std::size_t, Size>; | ||||
| #elif defined(__GNUC__) && __GNUC__ >= 8 | ||||
| template <std::size_t Size> | ||||
| using make_index_sequence = index_sequence<__integer_pack(Size)...>; | ||||
| #elif defined(_MSC_VER) | ||||
| template <std::size_t Size> | ||||
| using make_index_sequence = __make_integer_seq<integer_sequence, std::size_t, Size>; | ||||
| #else | ||||
|  | ||||
| // Adapted from https://stackoverflow.com/a/32223343. | ||||
| template <class Sequence1, class Sequence2> | ||||
| struct concat_seq; | ||||
| template <std::size_t... I1, std::size_t... I2> | ||||
| struct concat_seq<index_sequence<I1...>, index_sequence<I2...>> | ||||
| { | ||||
|     using type = index_sequence<I1..., (sizeof...(I1) + I2)...>; | ||||
| }; | ||||
|  | ||||
| template <size_t N> | ||||
| struct _make_index_sequence : concat_seq<typename _make_index_sequence<N / 2>::type, | ||||
|                                          typename _make_index_sequence<N - N / 2>::type> | ||||
| {}; | ||||
| template <> | ||||
| struct _make_index_sequence<0> | ||||
| { | ||||
|     using type = index_sequence<>; | ||||
| }; | ||||
| template <> | ||||
| struct _make_index_sequence<1> | ||||
| { | ||||
|     using type = index_sequence<0>; | ||||
| }; | ||||
|  | ||||
| template <std::size_t Size> | ||||
| using make_index_sequence = typename _make_index_sequence<Size>::type; | ||||
|  | ||||
| #endif | ||||
|  | ||||
| template <typename... T> | ||||
| using index_sequence_for = make_index_sequence<sizeof...(T)>; | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| #endif // LEXY_DETAIL_INTEGER_SEQUENCE_HPP_INCLUDED | ||||
|  | ||||
							
								
								
									
										70
									
								
								dep/lexy/include/lexy/_detail/invoke.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								dep/lexy/include/lexy/_detail/invoke.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | ||||
| // Copyright (C) 2020-2025 Jonathan Müller and lexy contributors | ||||
| // SPDX-License-Identifier: BSL-1.0 | ||||
|  | ||||
| #ifndef LEXY_DETAIL_INVOKE_HPP_INCLUDED | ||||
| #define LEXY_DETAIL_INVOKE_HPP_INCLUDED | ||||
|  | ||||
| #include <lexy/_detail/config.hpp> | ||||
|  | ||||
| namespace lexy::_detail | ||||
| { | ||||
| template <typename MemberPtr, bool = std::is_member_object_pointer_v<MemberPtr>> | ||||
| struct _mem_invoker; | ||||
| template <typename R, typename ClassT> | ||||
| struct _mem_invoker<R ClassT::*, true> | ||||
| { | ||||
|     static constexpr decltype(auto) invoke(R ClassT::*f, ClassT& object) | ||||
|     { | ||||
|         return object.*f; | ||||
|     } | ||||
|     static constexpr decltype(auto) invoke(R ClassT::*f, const ClassT& object) | ||||
|     { | ||||
|         return object.*f; | ||||
|     } | ||||
|  | ||||
|     template <typename Ptr> | ||||
|     static constexpr auto invoke(R ClassT::*f, Ptr&& ptr) -> decltype((*LEXY_FWD(ptr)).*f) | ||||
|     { | ||||
|         return (*LEXY_FWD(ptr)).*f; | ||||
|     } | ||||
| }; | ||||
| template <typename F, typename ClassT> | ||||
| struct _mem_invoker<F ClassT::*, false> | ||||
| { | ||||
|     template <typename ObjectT, typename... Args> | ||||
|     static constexpr auto _invoke(int, F ClassT::*f, ObjectT&& object, Args&&... args) | ||||
|         -> decltype((LEXY_FWD(object).*f)(LEXY_FWD(args)...)) | ||||
|     { | ||||
|         return (LEXY_FWD(object).*f)(LEXY_FWD(args)...); | ||||
|     } | ||||
|     template <typename PtrT, typename... Args> | ||||
|     static constexpr auto _invoke(short, F ClassT::*f, PtrT&& ptr, Args&&... args) | ||||
|         -> decltype(((*LEXY_FWD(ptr)).*f)(LEXY_FWD(args)...)) | ||||
|     { | ||||
|         return ((*LEXY_FWD(ptr)).*f)(LEXY_FWD(args)...); | ||||
|     } | ||||
|  | ||||
|     template <typename... Args> | ||||
|     static constexpr auto invoke(F ClassT::*f, | ||||
|                                  Args&&... args) -> decltype(_invoke(0, f, LEXY_FWD(args)...)) | ||||
|     { | ||||
|         return _invoke(0, f, LEXY_FWD(args)...); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| template <typename ClassT, typename F, typename... Args> | ||||
| constexpr auto invoke(F ClassT::*f, Args&&... args) | ||||
|     -> decltype(_mem_invoker<F ClassT::*>::invoke(f, LEXY_FWD(args)...)) | ||||
| { | ||||
|     return _mem_invoker<F ClassT::*>::invoke(f, LEXY_FWD(args)...); | ||||
| } | ||||
|  | ||||
| template <typename F, typename... Args> | ||||
| constexpr auto invoke(F&& f, Args&&... args) -> decltype(LEXY_FWD(f)(LEXY_FWD(args)...)) | ||||
| { | ||||
|     return LEXY_FWD(f)(LEXY_FWD(args)...); | ||||
| } | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| #endif // LEXY_DETAIL_INVOKE_HPP_INCLUDED | ||||
|  | ||||
							
								
								
									
										244
									
								
								dep/lexy/include/lexy/_detail/iterator.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										244
									
								
								dep/lexy/include/lexy/_detail/iterator.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,244 @@ | ||||
| // Copyright (C) 2020-2025 Jonathan Müller and lexy contributors | ||||
| // SPDX-License-Identifier: BSL-1.0 | ||||
|  | ||||
| #ifndef LEXY_DETAIL_ITERATOR_HPP_INCLUDED | ||||
| #define LEXY_DETAIL_ITERATOR_HPP_INCLUDED | ||||
|  | ||||
| #include <lexy/_detail/assert.hpp> | ||||
| #include <lexy/_detail/config.hpp> | ||||
| #include <lexy/_detail/detect.hpp> | ||||
| #include <lexy/_detail/std.hpp> | ||||
|  | ||||
| //=== iterator algorithms ===// | ||||
| namespace lexy::_detail | ||||
| { | ||||
| // Can't use std::is_base_of_v<std::random_access_iterator_tag, ...> without including <iterator>. | ||||
| template <typename Iterator> | ||||
| using _detect_random_access = decltype(LEXY_DECLVAL(Iterator) - LEXY_DECLVAL(Iterator)); | ||||
| template <typename Iterator> | ||||
| constexpr auto is_random_access_iterator = is_detected<_detect_random_access, Iterator>; | ||||
|  | ||||
| template <typename Iterator, typename Sentinel> | ||||
| constexpr std::size_t range_size(Iterator begin, Sentinel end) | ||||
| { | ||||
|     if constexpr (std::is_same_v<Iterator, Sentinel> && is_random_access_iterator<Iterator>) | ||||
|     { | ||||
|         return static_cast<std::size_t>(end - begin); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         std::size_t result = 0; | ||||
|         for (auto cur = begin; cur != end; ++cur) | ||||
|             ++result; | ||||
|         return result; | ||||
|     } | ||||
| } | ||||
|  | ||||
| template <typename Iterator> | ||||
| constexpr Iterator next(Iterator iter) | ||||
| { | ||||
|     return ++iter; | ||||
| } | ||||
| template <typename Iterator> | ||||
| constexpr Iterator next(Iterator iter, std::size_t n) | ||||
| { | ||||
|     if constexpr (is_random_access_iterator<Iterator>) | ||||
|     { | ||||
|         return iter + n; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         for (auto i = 0u; i != n; ++i) | ||||
|             ++iter; | ||||
|         return iter; | ||||
|     } | ||||
| } | ||||
|  | ||||
| template <typename Iterator, typename Sentinel> | ||||
| constexpr Iterator next_clamped(Iterator iter, std::size_t n, Sentinel end) | ||||
| { | ||||
|     if constexpr (is_random_access_iterator<Iterator> && std::is_same_v<Iterator, Sentinel>) | ||||
|     { | ||||
|         auto remaining = std::size_t(end - iter); | ||||
|         if (remaining < n) | ||||
|             return end; | ||||
|         else | ||||
|             return iter + n; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         for (auto i = 0u; i != n; ++i) | ||||
|         { | ||||
|             if (iter == end) | ||||
|                 break; | ||||
|             ++iter; | ||||
|         } | ||||
|         return iter; | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Used for assertions. | ||||
| template <typename Iterator, typename Sentinel> | ||||
| constexpr bool precedes([[maybe_unused]] Iterator first, [[maybe_unused]] Sentinel after) | ||||
| { | ||||
|     if constexpr (is_random_access_iterator<Iterator> && std::is_same_v<Iterator, Sentinel>) | ||||
|         return first <= after; | ||||
|     else | ||||
|         return true; | ||||
| } | ||||
|  | ||||
| // Requires: begin <= end_a && begin <= end_b. | ||||
| // Returns min(end_a, end_b). | ||||
| template <typename Iterator> | ||||
| constexpr Iterator min_range_end(Iterator begin, Iterator end_a, Iterator end_b) | ||||
| { | ||||
|     if constexpr (is_random_access_iterator<Iterator>) | ||||
|     { | ||||
|         LEXY_PRECONDITION(begin <= end_a && begin <= end_b); | ||||
|         if (end_a <= end_b) | ||||
|             return end_a; | ||||
|         else | ||||
|             return end_b; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         auto cur = begin; | ||||
|         while (cur != end_a && cur != end_b) | ||||
|             ++cur; | ||||
|         return cur; | ||||
|     } | ||||
| } | ||||
|  | ||||
| // Requires: begin <= end_a && begin <= end_b. | ||||
| // Returns max(end_a, end_b). | ||||
| template <typename Iterator> | ||||
| constexpr Iterator max_range_end(Iterator begin, Iterator end_a, Iterator end_b) | ||||
| { | ||||
|     if constexpr (is_random_access_iterator<Iterator>) | ||||
|     { | ||||
|         LEXY_PRECONDITION(begin <= end_a && begin <= end_b); | ||||
|         if (end_a <= end_b) | ||||
|             return end_b; | ||||
|         else | ||||
|             return end_a; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         auto cur = begin; | ||||
|         while (true) | ||||
|         { | ||||
|             if (cur == end_a) | ||||
|                 return end_b; | ||||
|             else if (cur == end_b) | ||||
|                 return end_a; | ||||
|  | ||||
|             ++cur; | ||||
|         } | ||||
|         return begin; // unreachable | ||||
|     } | ||||
| } | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| //=== facade classes ===// | ||||
| namespace lexy::_detail | ||||
| { | ||||
| template <typename T> | ||||
| struct _proxy_pointer | ||||
| { | ||||
|     T value; | ||||
|  | ||||
|     constexpr T* operator->() noexcept | ||||
|     { | ||||
|         return &value; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| template <typename Derived, typename T, typename Reference = T&, typename Pointer = const T*> | ||||
| struct forward_iterator_base | ||||
| { | ||||
|     using value_type        = std::remove_cv_t<T>; | ||||
|     using reference         = Reference; | ||||
|     using pointer           = lexy::_detail::type_or<Pointer, _proxy_pointer<value_type>>; | ||||
|     using difference_type   = std::ptrdiff_t; | ||||
|     using iterator_category = std::forward_iterator_tag; | ||||
|  | ||||
|     constexpr reference operator*() const noexcept | ||||
|     { | ||||
|         return static_cast<const Derived&>(*this).deref(); | ||||
|     } | ||||
|     constexpr pointer operator->() const noexcept | ||||
|     { | ||||
|         if constexpr (std::is_void_v<Pointer>) | ||||
|             return pointer{**this}; | ||||
|         else | ||||
|             return &**this; | ||||
|     } | ||||
|  | ||||
|     constexpr Derived& operator++() noexcept | ||||
|     { | ||||
|         auto& derived = static_cast<Derived&>(*this); | ||||
|         derived.increment(); | ||||
|         return derived; | ||||
|     } | ||||
|     constexpr Derived operator++(int) noexcept | ||||
|     { | ||||
|         auto& derived = static_cast<Derived&>(*this); | ||||
|         auto  copy    = derived; | ||||
|         derived.increment(); | ||||
|         return copy; | ||||
|     } | ||||
|  | ||||
|     friend constexpr bool operator==(const Derived& lhs, const Derived& rhs) | ||||
|     { | ||||
|         return lhs.equal(rhs); | ||||
|     } | ||||
|     friend constexpr bool operator!=(const Derived& lhs, const Derived& rhs) | ||||
|     { | ||||
|         return !lhs.equal(rhs); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| template <typename Derived, typename T, typename Reference = T&, typename Pointer = const T*> | ||||
| struct bidirectional_iterator_base : forward_iterator_base<Derived, T, Reference, Pointer> | ||||
| { | ||||
|     using iterator_category = std::bidirectional_iterator_tag; | ||||
|  | ||||
|     constexpr Derived& operator--() noexcept | ||||
|     { | ||||
|         auto& derived = static_cast<Derived&>(*this); | ||||
|         derived.decrement(); | ||||
|         return derived; | ||||
|     } | ||||
|     constexpr Derived operator--(int) noexcept | ||||
|     { | ||||
|         auto& derived = static_cast<Derived&>(*this); | ||||
|         auto  copy    = derived; | ||||
|         derived.decrement(); | ||||
|         return copy; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| template <typename Derived, typename Iterator> | ||||
| struct sentinel_base | ||||
| { | ||||
|     friend constexpr bool operator==(const Iterator& lhs, Derived) noexcept | ||||
|     { | ||||
|         return lhs.is_end(); | ||||
|     } | ||||
|     friend constexpr bool operator!=(const Iterator& lhs, Derived) noexcept | ||||
|     { | ||||
|         return !(lhs == Derived{}); | ||||
|     } | ||||
|     friend constexpr bool operator==(Derived, const Iterator& rhs) noexcept | ||||
|     { | ||||
|         return rhs == Derived{}; | ||||
|     } | ||||
|     friend constexpr bool operator!=(Derived, const Iterator& rhs) noexcept | ||||
|     { | ||||
|         return !(rhs == Derived{}); | ||||
|     } | ||||
| }; | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| #endif // LEXY_DETAIL_ITERATOR_HPP_INCLUDED | ||||
|  | ||||
							
								
								
									
										246
									
								
								dep/lexy/include/lexy/_detail/lazy_init.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										246
									
								
								dep/lexy/include/lexy/_detail/lazy_init.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,246 @@ | ||||
| // Copyright (C) 2020-2025 Jonathan Müller and lexy contributors | ||||
| // SPDX-License-Identifier: BSL-1.0 | ||||
|  | ||||
| #ifndef LEXY_DETAIL_LAZY_INIT_HPP_INCLUDED | ||||
| #define LEXY_DETAIL_LAZY_INIT_HPP_INCLUDED | ||||
|  | ||||
| #include <lexy/_detail/assert.hpp> | ||||
| #include <lexy/_detail/config.hpp> | ||||
| #include <lexy/_detail/std.hpp> | ||||
|  | ||||
| namespace lexy::_detail | ||||
| { | ||||
| template <typename T> | ||||
| struct _lazy_init_storage_trivial | ||||
| { | ||||
|     bool _init; | ||||
|     union | ||||
|     { | ||||
|         char _empty; | ||||
|         T    _value; | ||||
|     }; | ||||
|  | ||||
|     constexpr _lazy_init_storage_trivial() noexcept : _init(false), _empty() {} | ||||
|  | ||||
|     template <typename... Args> | ||||
|     constexpr _lazy_init_storage_trivial(int, Args&&... args) | ||||
|     : _init(true), _value(LEXY_FWD(args)...) | ||||
|     {} | ||||
|  | ||||
|     template <typename... Args> | ||||
|     constexpr void _construct(Args&&... args) | ||||
|     { | ||||
|         *this = _lazy_init_storage_trivial(0, LEXY_FWD(args)...); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| template <typename T> | ||||
| struct _lazy_init_storage_non_trivial | ||||
| { | ||||
|     bool _init; | ||||
|     union | ||||
|     { | ||||
|         char _empty; | ||||
|         T    _value; | ||||
|     }; | ||||
|  | ||||
|     constexpr _lazy_init_storage_non_trivial() noexcept : _init(false), _empty() {} | ||||
|  | ||||
|     template <typename... Args> | ||||
|     LEXY_CONSTEXPR_DTOR void _construct(Args&&... args) | ||||
|     { | ||||
|         _detail::construct_at(&_value, LEXY_FWD(args)...); | ||||
|         _init = true; | ||||
|     } | ||||
|  | ||||
|     // Cannot add noexcept due to https://github.com/llvm/llvm-project/issues/59854. | ||||
|     LEXY_CONSTEXPR_DTOR ~_lazy_init_storage_non_trivial() /* noexcept */ | ||||
|     { | ||||
|         if (_init) | ||||
|             _value.~T(); | ||||
|     } | ||||
|  | ||||
|     LEXY_CONSTEXPR_DTOR _lazy_init_storage_non_trivial( | ||||
|         _lazy_init_storage_non_trivial&& other) noexcept | ||||
|     : _init(other._init), _empty() | ||||
|     { | ||||
|         if (_init) | ||||
|             _detail::construct_at(&_value, LEXY_MOV(other._value)); | ||||
|     } | ||||
|  | ||||
|     LEXY_CONSTEXPR_DTOR _lazy_init_storage_non_trivial& operator=( | ||||
|         _lazy_init_storage_non_trivial&& other) noexcept | ||||
|     { | ||||
|         if (_init && other._init) | ||||
|             _value = LEXY_MOV(other._value); | ||||
|         else if (_init && !other._init) | ||||
|         { | ||||
|             _value.~T(); | ||||
|             _init = false; | ||||
|         } | ||||
|         else if (!_init && other._init) | ||||
|         { | ||||
|             _detail::construct_at(&_value, LEXY_MOV(other._value)); | ||||
|             _init = true; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // Both not initialized, nothing to do. | ||||
|         } | ||||
|  | ||||
|         return *this; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| template <typename T> | ||||
| constexpr auto _lazy_init_trivial = [] { | ||||
|     // https://www.foonathan.net/2021/03/trivially-copyable/ | ||||
|     return std::is_trivially_destructible_v<T>          // | ||||
|            && std::is_trivially_copy_constructible_v<T> // | ||||
|            && std::is_trivially_copy_assignable_v<T>    // | ||||
|            && std::is_trivially_move_constructible_v<T> // | ||||
|            && std::is_trivially_move_assignable_v<T>; | ||||
| }(); | ||||
| template <typename T> | ||||
| using _lazy_init_storage = std::conditional_t<_lazy_init_trivial<T>, _lazy_init_storage_trivial<T>, | ||||
|                                               _lazy_init_storage_non_trivial<T>>; | ||||
|  | ||||
| template <typename T> | ||||
| class lazy_init : _lazy_init_storage<T> | ||||
| { | ||||
| public: | ||||
|     using value_type = T; | ||||
|  | ||||
|     constexpr lazy_init() noexcept = default; | ||||
|  | ||||
|     template <typename... Args> | ||||
|     constexpr T& emplace(Args&&... args) | ||||
|     { | ||||
|         if (*this) | ||||
|             this->_value = T(LEXY_FWD(args)...); | ||||
|         else | ||||
|             this->_construct(LEXY_FWD(args)...); | ||||
|  | ||||
|         return this->_value; | ||||
|     } | ||||
|  | ||||
|     template <typename Fn, typename... Args> | ||||
|     constexpr T& emplace_result(Fn&& fn, Args&&... args) | ||||
|     { | ||||
|         return emplace(LEXY_FWD(fn)(LEXY_FWD(args)...)); | ||||
|     } | ||||
|  | ||||
|     constexpr explicit operator bool() const noexcept | ||||
|     { | ||||
|         return this->_init; | ||||
|     } | ||||
|  | ||||
|     constexpr T& operator*() & noexcept | ||||
|     { | ||||
|         LEXY_PRECONDITION(*this); | ||||
|         return this->_value; | ||||
|     } | ||||
|     constexpr const T& operator*() const& noexcept | ||||
|     { | ||||
|         LEXY_PRECONDITION(*this); | ||||
|         return this->_value; | ||||
|     } | ||||
|     constexpr T&& operator*() && noexcept | ||||
|     { | ||||
|         LEXY_PRECONDITION(*this); | ||||
|         return LEXY_MOV(this->_value); | ||||
|     } | ||||
|     constexpr const T&& operator*() const&& noexcept | ||||
|     { | ||||
|         LEXY_PRECONDITION(*this); | ||||
|         return LEXY_MOV(this->_value); | ||||
|     } | ||||
|  | ||||
|     constexpr T* operator->() noexcept | ||||
|     { | ||||
|         LEXY_PRECONDITION(*this); | ||||
|         return &this->_value; | ||||
|     } | ||||
|     constexpr const T* operator->() const noexcept | ||||
|     { | ||||
|         LEXY_PRECONDITION(*this); | ||||
|         return &this->_value; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     template <typename... Args> | ||||
|     constexpr explicit lazy_init(int, Args&&... args) noexcept | ||||
|     : _lazy_init_storage<T>(0, LEXY_FWD(args)...) | ||||
|     {} | ||||
| }; | ||||
| template <typename T> | ||||
| class lazy_init<T&> | ||||
| { | ||||
| public: | ||||
|     using value_type = T&; | ||||
|  | ||||
|     constexpr lazy_init() noexcept : _ptr(nullptr) {} | ||||
|  | ||||
|     constexpr T& emplace(T& ref) | ||||
|     { | ||||
|         _ptr = &ref; | ||||
|         return ref; | ||||
|     } | ||||
|  | ||||
|     template <typename Fn, typename... Args> | ||||
|     constexpr T& emplace_result(Fn&& fn, Args&&... args) | ||||
|     { | ||||
|         return emplace(LEXY_FWD(fn)(LEXY_FWD(args)...)); | ||||
|     } | ||||
|  | ||||
|     constexpr explicit operator bool() const noexcept | ||||
|     { | ||||
|         return _ptr != nullptr; | ||||
|     } | ||||
|  | ||||
|     constexpr T& operator*() const noexcept | ||||
|     { | ||||
|         LEXY_PRECONDITION(*this); | ||||
|         return *_ptr; | ||||
|     } | ||||
|  | ||||
|     constexpr T* operator->() const noexcept | ||||
|     { | ||||
|         LEXY_PRECONDITION(*this); | ||||
|         return _ptr; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     T* _ptr; | ||||
| }; | ||||
| template <> | ||||
| class lazy_init<void> | ||||
| { | ||||
| public: | ||||
|     using value_type = void; | ||||
|  | ||||
|     constexpr lazy_init() noexcept : _init(false) {} | ||||
|  | ||||
|     constexpr void emplace() | ||||
|     { | ||||
|         _init = true; | ||||
|     } | ||||
|     template <typename Fn, typename... Args> | ||||
|     constexpr void emplace_result(Fn&& fn, Args&&... args) | ||||
|     { | ||||
|         LEXY_FWD(fn)(LEXY_FWD(args)...); | ||||
|         _init = true; | ||||
|     } | ||||
|  | ||||
|     constexpr explicit operator bool() const noexcept | ||||
|     { | ||||
|         return _init; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     bool _init; | ||||
| }; | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| #endif // LEXY_DETAIL_LAZY_INIT_HPP_INCLUDED | ||||
|  | ||||
							
								
								
									
										152
									
								
								dep/lexy/include/lexy/_detail/memory_resource.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								dep/lexy/include/lexy/_detail/memory_resource.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | ||||
| // Copyright (C) 2020-2025 Jonathan Müller and lexy contributors | ||||
| // SPDX-License-Identifier: BSL-1.0 | ||||
|  | ||||
| #ifndef LEXY_DETAIL_MEMORY_RESOURCE_HPP_INCLUDED | ||||
| #define LEXY_DETAIL_MEMORY_RESOURCE_HPP_INCLUDED | ||||
|  | ||||
| #include <cstring> | ||||
| #include <lexy/_detail/assert.hpp> | ||||
| #include <lexy/_detail/config.hpp> | ||||
| #include <new> | ||||
|  | ||||
| #if 0 // NOLINT | ||||
| // Subset of the interface of std::pmr::memory_resource. | ||||
| class MemoryResource | ||||
| { | ||||
| public: | ||||
|     void* allocate(std::size_t bytes, std::size_t alignment); | ||||
|     void deallocate(void* ptr, std::size_t bytes, std::size_t alignment); | ||||
|  | ||||
|     friend bool operator==(const MemoryResource& lhs, const MemoryResource& rhs); | ||||
| }; | ||||
| #endif | ||||
|  | ||||
| namespace lexy::_detail | ||||
| { | ||||
| class default_memory_resource | ||||
| { | ||||
| public: | ||||
|     static void* allocate(std::size_t bytes, std::size_t alignment) | ||||
|     { | ||||
|         if (alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__) | ||||
|             return ::operator new(bytes, std::align_val_t{alignment}); | ||||
|         else | ||||
|             return ::operator new(bytes); | ||||
|     } | ||||
|  | ||||
|     static void deallocate(void* ptr, std::size_t bytes, std::size_t alignment) noexcept | ||||
|     { | ||||
| #if LEXY_ENABLE_ASSERT | ||||
|         // In debug mode, we fill freed memory with 0xFF to detect dangling lexemes. | ||||
|         // For default, ASCII, bytes, this is just a noticable value. | ||||
|         // For UTF-8, this is the EOF integer value as its an invalid code unit. | ||||
|         // For UTF-16, this is the code point 0xFFFF, which is the replacement character. | ||||
|         // For UTF-32, this is an out of range code point. | ||||
|         std::memset(ptr, 0xFF, bytes); | ||||
| #endif | ||||
|  | ||||
| #ifdef __cpp_sized_deallocation | ||||
|         if (alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__) | ||||
|             ::operator delete(ptr, bytes, std::align_val_t{alignment}); | ||||
|         else | ||||
|             ::operator delete(ptr, bytes); | ||||
| #else | ||||
|         (void)bytes; | ||||
|  | ||||
|         if (alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__) | ||||
|             ::operator delete(ptr, std::align_val_t{alignment}); | ||||
|         else | ||||
|             ::operator delete(ptr); | ||||
| #endif | ||||
|     } | ||||
|  | ||||
|     friend constexpr bool operator==(default_memory_resource, default_memory_resource) noexcept | ||||
|     { | ||||
|         return true; | ||||
|     } | ||||
| }; | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| namespace lexy::_detail | ||||
| { | ||||
| template <typename MemoryResource> | ||||
| class _memory_resource_ptr_empty | ||||
| { | ||||
| public: | ||||
|     constexpr explicit _memory_resource_ptr_empty(MemoryResource*) noexcept {} | ||||
|     constexpr explicit _memory_resource_ptr_empty(void*) noexcept {} | ||||
|  | ||||
|     constexpr auto operator*() const noexcept | ||||
|     { | ||||
|         return MemoryResource{}; | ||||
|     } | ||||
|  | ||||
|     constexpr auto operator->() const noexcept | ||||
|     { | ||||
|         struct proxy | ||||
|         { | ||||
|             MemoryResource _resource; | ||||
|  | ||||
|             constexpr MemoryResource* operator->() noexcept | ||||
|             { | ||||
|                 return &_resource; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         return proxy{}; | ||||
|     } | ||||
|  | ||||
|     constexpr MemoryResource* get() const noexcept | ||||
|     { | ||||
|         return nullptr; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| template <typename MemoryResource> | ||||
| class _memory_resource_ptr | ||||
| { | ||||
| public: | ||||
|     constexpr explicit _memory_resource_ptr(MemoryResource* resource) noexcept : _resource(resource) | ||||
|     { | ||||
|         LEXY_PRECONDITION(resource); | ||||
|     } | ||||
|  | ||||
|     constexpr MemoryResource& operator*() const noexcept | ||||
|     { | ||||
|         return *_resource; | ||||
|     } | ||||
|  | ||||
|     constexpr MemoryResource* operator->() const noexcept | ||||
|     { | ||||
|         return _resource; | ||||
|     } | ||||
|  | ||||
|     constexpr MemoryResource* get() const noexcept | ||||
|     { | ||||
|         return _resource; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     MemoryResource* _resource; | ||||
| }; | ||||
|  | ||||
| // clang-format off | ||||
| template <typename MemoryResource> | ||||
| using memory_resource_ptr | ||||
|     = std::conditional_t<std::is_void_v<MemoryResource>, | ||||
|             _memory_resource_ptr_empty<default_memory_resource>, | ||||
|             std::conditional_t<std::is_empty_v<MemoryResource>, | ||||
|                 _memory_resource_ptr_empty<MemoryResource>, | ||||
|                 _memory_resource_ptr<MemoryResource>>>; | ||||
| // clang-format on | ||||
|  | ||||
| template <typename MemoryResource, typename = std::enable_if_t<std::is_void_v<MemoryResource> | ||||
|                                                                || std::is_empty_v<MemoryResource>>> | ||||
| constexpr MemoryResource* get_memory_resource() | ||||
| { | ||||
|     return nullptr; | ||||
| } | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| #endif // LEXY_DETAIL_MEMORY_RESOURCE_HPP_INCLUDED | ||||
|  | ||||
							
								
								
									
										147
									
								
								dep/lexy/include/lexy/_detail/nttp_string.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								dep/lexy/include/lexy/_detail/nttp_string.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,147 @@ | ||||
| // Copyright (C) 2020-2025 Jonathan Müller and lexy contributors | ||||
| // SPDX-License-Identifier: BSL-1.0 | ||||
|  | ||||
| #ifndef LEXY_DETAIL_NTTP_STRING_HPP_INCLUDED | ||||
| #define LEXY_DETAIL_NTTP_STRING_HPP_INCLUDED | ||||
|  | ||||
| #include <lexy/_detail/assert.hpp> | ||||
| #include <lexy/_detail/config.hpp> | ||||
| #include <lexy/encoding.hpp> | ||||
|  | ||||
| namespace lexy::_detail | ||||
| { | ||||
| // Note: we can't use type_string<auto...>, it doesn't work on older GCC. | ||||
| template <typename CharT, CharT... Cs> | ||||
| struct type_string | ||||
| { | ||||
|     using char_type = CharT; | ||||
|  | ||||
|     template <template <typename C, C...> typename T> | ||||
|     using rename = T<CharT, Cs...>; | ||||
|  | ||||
|     static constexpr auto size = sizeof...(Cs); | ||||
|  | ||||
|     template <typename T = char_type> | ||||
|     static constexpr T c_str[sizeof...(Cs) + 1] = {transcode_char<T>(Cs)..., T()}; | ||||
| }; | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| #if LEXY_HAS_NTTP // string NTTP implementation | ||||
|  | ||||
| #    include <lexy/_detail/integer_sequence.hpp> | ||||
|  | ||||
| namespace lexy::_detail | ||||
| { | ||||
| template <std::size_t N, typename CharT> | ||||
| struct string_literal | ||||
| { | ||||
|     CharT data[N]; | ||||
|  | ||||
|     using char_type = CharT; | ||||
|  | ||||
|     LEXY_CONSTEVAL string_literal(const CharT* str) : data{} | ||||
|     { | ||||
|         for (auto i = 0u; i != N; ++i) | ||||
|             data[i] = str[i]; | ||||
|     } | ||||
|     LEXY_CONSTEVAL string_literal(CharT c) : data{} | ||||
|     { | ||||
|         data[0] = c; | ||||
|     } | ||||
|  | ||||
|     static LEXY_CONSTEVAL auto size() | ||||
|     { | ||||
|         return N; | ||||
|     } | ||||
| }; | ||||
| template <std::size_t N, typename CharT> | ||||
| string_literal(const CharT (&)[N]) -> string_literal<N - 1, CharT>; | ||||
| template <typename CharT> | ||||
| string_literal(CharT) -> string_literal<1, CharT>; | ||||
|  | ||||
| template <template <typename C, C... Cs> typename T, string_literal Str, std::size_t... Idx> | ||||
| auto _to_type_string(index_sequence<Idx...>) | ||||
| { | ||||
|     return T<typename decltype(Str)::char_type, Str.data[Idx]...>{}; | ||||
| } | ||||
| template <template <typename C, C... Cs> typename T, string_literal Str> | ||||
| using to_type_string | ||||
|     = decltype(_to_type_string<T, Str>(make_index_sequence<decltype(Str)::size()>{})); | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| #    define LEXY_NTTP_STRING(T, Str)                                                               \ | ||||
|         ::lexy::_detail::to_type_string<T, ::lexy::_detail::string_literal(Str)> | ||||
|  | ||||
| #elif defined(__GNUC__) // literal implementation | ||||
|  | ||||
| #    pragma GCC diagnostic push | ||||
| #    pragma GCC diagnostic ignored "-Wpedantic" | ||||
| #    ifdef __clang__ | ||||
| #        pragma GCC diagnostic ignored "-Wgnu-string-literal-operator-template" | ||||
| #    endif | ||||
|  | ||||
| template <typename CharT, CharT... Cs> | ||||
| constexpr ::lexy::_detail::type_string<CharT, Cs...> operator""_lexy_string_udl() | ||||
| { | ||||
|     return {}; | ||||
| } | ||||
|  | ||||
| #    define LEXY_NTTP_STRING(T, Str) decltype(Str##_lexy_string_udl)::rename<T> | ||||
|  | ||||
| #    pragma GCC diagnostic pop | ||||
|  | ||||
| #else // string<Cs...> macro implementation | ||||
|  | ||||
| namespace lexy::_detail | ||||
| { | ||||
| template <typename A, typename B> | ||||
| struct cat_; | ||||
| template <typename CharT, CharT... C1, CharT... C2> | ||||
| struct cat_<type_string<CharT, C1...>, type_string<CharT, C2...>> | ||||
| { | ||||
|     using type = type_string<CharT, C1..., C2...>; | ||||
| }; | ||||
| template <typename A, typename B> | ||||
| using cat = typename cat_<A, B>::type; | ||||
|  | ||||
| template <template <typename CharT, CharT...> typename T, typename TypeString, std::size_t Size, | ||||
|           std::size_t MaxSize> | ||||
| struct macro_type_string | ||||
| { | ||||
|     static_assert(Size <= MaxSize, "string out of range"); | ||||
|     using type = typename TypeString::template rename<T>; | ||||
| }; | ||||
|  | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| #    define LEXY_NTTP_STRING_LENGTH(Str) (sizeof(Str) / sizeof(Str[0]) - 1) | ||||
|  | ||||
| // extract Ith character if not out of bounds | ||||
| #    define LEXY_NTTP_STRING1(Str, I)                                                              \ | ||||
|         ::std::conditional_t<                                                                      \ | ||||
|             (I < LEXY_NTTP_STRING_LENGTH(Str)),                                                    \ | ||||
|             ::lexy::_detail::type_string<::LEXY_DECAY_DECLTYPE(Str[0]),                            \ | ||||
|                                          (I >= LEXY_NTTP_STRING_LENGTH(Str) ? Str[0] : Str[I])>,   \ | ||||
|             ::lexy::_detail::type_string<::LEXY_DECAY_DECLTYPE(Str[0])>> | ||||
|  | ||||
| // recursively split the string in two | ||||
| #    define LEXY_NTTP_STRING2(Str, I)                                                              \ | ||||
|         ::lexy::_detail::cat<LEXY_NTTP_STRING1(Str, I), LEXY_NTTP_STRING1(Str, I + 1)> | ||||
| #    define LEXY_NTTP_STRING4(Str, I)                                                              \ | ||||
|         ::lexy::_detail::cat<LEXY_NTTP_STRING2(Str, I), LEXY_NTTP_STRING2(Str, I + 2)> | ||||
| #    define LEXY_NTTP_STRING8(Str, I)                                                              \ | ||||
|         ::lexy::_detail::cat<LEXY_NTTP_STRING4(Str, I), LEXY_NTTP_STRING4(Str, I + 4)> | ||||
| #    define LEXY_NTTP_STRING16(Str, I)                                                             \ | ||||
|         ::lexy::_detail::cat<LEXY_NTTP_STRING8(Str, I), LEXY_NTTP_STRING8(Str, I + 8)> | ||||
| #    define LEXY_NTTP_STRING32(Str, I)                                                             \ | ||||
|         ::lexy::_detail::cat<LEXY_NTTP_STRING16(Str, I), LEXY_NTTP_STRING16(Str, I + 16)> | ||||
|  | ||||
| // instantiate with overflow check | ||||
| #    define LEXY_NTTP_STRING(T, Str)                                                               \ | ||||
|         ::lexy::_detail::macro_type_string<T, LEXY_NTTP_STRING32(Str, 0),                          \ | ||||
|                                            LEXY_NTTP_STRING_LENGTH(Str), 32>::type | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #endif // LEXY_DETAIL_NTTP_STRING_HPP_INCLUDED | ||||
|  | ||||
							
								
								
									
										72
									
								
								dep/lexy/include/lexy/_detail/stateless_lambda.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								dep/lexy/include/lexy/_detail/stateless_lambda.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | ||||
| // Copyright (C) 2020-2025 Jonathan Müller and lexy contributors | ||||
| // SPDX-License-Identifier: BSL-1.0 | ||||
|  | ||||
| #ifndef LEXY_DETAIL_STATELESS_LAMBDA_HPP_INCLUDED | ||||
| #define LEXY_DETAIL_STATELESS_LAMBDA_HPP_INCLUDED | ||||
|  | ||||
| #include <lexy/_detail/config.hpp> | ||||
|  | ||||
| namespace lexy::_detail | ||||
| { | ||||
| template <typename Lambda> | ||||
| struct stateless_lambda | ||||
| { | ||||
|     static_assert(std::is_class_v<Lambda>); | ||||
|     static_assert(std::is_empty_v<Lambda>); | ||||
|  | ||||
|     static constexpr Lambda get() | ||||
|     { | ||||
|         if constexpr (std::is_default_constructible_v<Lambda>) | ||||
|         { | ||||
|             // We're using C++20, lambdas are default constructible. | ||||
|             return Lambda(); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // We're not having C++20; use a sequence of weird workarounds to legally construct a | ||||
|             // Lambda object without invoking any constructors. | ||||
|             // This works and is well-defined, but sadly not constexpr. | ||||
|             // Taken from: https://www.youtube.com/watch?v=yTb6xz_FSkY | ||||
|  | ||||
|             // We're defining two standard layout types that have a char as a common initial | ||||
|             // sequence (as the Lambda is empty, it doesn't add anymore members to B). | ||||
|             struct A | ||||
|             { | ||||
|                 char member; | ||||
|             }; | ||||
|             struct B : Lambda | ||||
|             { | ||||
|                 char member; | ||||
|             }; | ||||
|             static_assert(std::is_standard_layout_v<A> && std::is_standard_layout_v<B>); | ||||
|  | ||||
|             // We put the two types in a union and initialize the a member, which we can do. | ||||
|             union storage_t | ||||
|             { | ||||
|                 A a; | ||||
|                 B b; | ||||
|             } storage{}; | ||||
|  | ||||
|             // We can now take the address of member via b, as it is in the common initial sequence. | ||||
|             auto char_ptr = &storage.b.member; | ||||
|             // char_ptr is a pointer to the first member of B, so we can reinterpret_cast it to a | ||||
|             // pointer to B. | ||||
|             auto b_ptr = reinterpret_cast<B*>(char_ptr); | ||||
|             // Now we're having a pointer to a B object, which can we can cast to the base class | ||||
|             // Lambda. | ||||
|             auto lambda_ptr = static_cast<Lambda*>(b_ptr); | ||||
|             // Dereference the pointer to get the lambda object. | ||||
|             return *lambda_ptr; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     template <typename... Args> | ||||
|     constexpr decltype(auto) operator()(Args&&... args) const | ||||
|     { | ||||
|         return get()(LEXY_FWD(args)...); | ||||
|     } | ||||
| }; | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| #endif // LEXY_DETAIL_STATELESS_LAMBDA_HPP_INCLUDED | ||||
|  | ||||
							
								
								
									
										98
									
								
								dep/lexy/include/lexy/_detail/std.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								dep/lexy/include/lexy/_detail/std.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,98 @@ | ||||
| // Copyright (C) 2020-2025 Jonathan Müller and lexy contributors | ||||
| // SPDX-License-Identifier: BSL-1.0 | ||||
|  | ||||
| #ifndef LEXY_DETAIL_STD_HPP_INCLUDED | ||||
| #define LEXY_DETAIL_STD_HPP_INCLUDED | ||||
|  | ||||
| #include <lexy/_detail/config.hpp> | ||||
|  | ||||
| //=== iterator tags ===// | ||||
| #if defined(__GLIBCXX__) | ||||
|  | ||||
| namespace std | ||||
| { | ||||
| _GLIBCXX_BEGIN_NAMESPACE_VERSION | ||||
| struct forward_iterator_tag; | ||||
| struct bidirectional_iterator_tag; | ||||
| _GLIBCXX_END_NAMESPACE_VERSION | ||||
| } // namespace std | ||||
|  | ||||
| #elif defined(_LIBCPP_VERSION) | ||||
|  | ||||
| _LIBCPP_BEGIN_NAMESPACE_STD | ||||
| struct forward_iterator_tag; | ||||
| struct bidirectional_iterator_tag; | ||||
| _LIBCPP_END_NAMESPACE_STD | ||||
|  | ||||
| #else | ||||
|  | ||||
| // Forward declaring things in std is not allowed, but I'm willing to take the risk. | ||||
|  | ||||
| namespace std | ||||
| { | ||||
| struct forward_iterator_tag; | ||||
| struct bidirectional_iterator_tag; | ||||
| } // namespace std | ||||
|  | ||||
| #endif | ||||
|  | ||||
| //=== (constexpr) construct_at ===// | ||||
| #if !LEXY_HAS_CONSTEXPR_DTOR | ||||
|  | ||||
| namespace lexy::_detail | ||||
| { | ||||
| // We don't have constexpr dtor's, so this is just a regular function. | ||||
| template <typename T, typename... Args> | ||||
| T* construct_at(T* ptr, Args&&... args) | ||||
| { | ||||
|     return ::new ((void*)ptr) T(LEXY_FWD(args)...); | ||||
| } | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| #elif defined(_MSC_VER) | ||||
|  | ||||
| namespace lexy::_detail | ||||
| { | ||||
| // MSVC can make it constexpr if marked with an attribute given by a macro. | ||||
| template <typename T, typename... Args> | ||||
| constexpr T* construct_at(T* ptr, Args&&... args) | ||||
| { | ||||
| #    if defined(_MSVC_CONSTEXPR) | ||||
|     _MSVC_CONSTEXPR | ||||
| #    endif | ||||
|     return ::new ((void*)ptr) T(LEXY_FWD(args)...); | ||||
| } | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| #else | ||||
|  | ||||
| namespace lexy::_detail | ||||
| { | ||||
| struct _construct_at_tag | ||||
| {}; | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| namespace std | ||||
| { | ||||
| // GCC only allows constexpr placement new inside a function called `std::construct_at`. | ||||
| // So we write our own. | ||||
| template <typename T, typename... Args> | ||||
| constexpr T* construct_at(lexy::_detail::_construct_at_tag, T* ptr, Args&&... args) | ||||
| { | ||||
|     return ::new ((void*)ptr) T(LEXY_FWD(args)...); | ||||
| } | ||||
| } // namespace std | ||||
|  | ||||
| namespace lexy::_detail | ||||
| { | ||||
| template <typename T, typename... Args> | ||||
| constexpr T* construct_at(T* ptr, Args&&... args) | ||||
| { | ||||
|     return std::construct_at(lexy::_detail::_construct_at_tag{}, ptr, LEXY_FWD(args)...); | ||||
| } | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| #endif | ||||
|  | ||||
| #endif // LEXY_DETAIL_STD_HPP_INCLUDED | ||||
|  | ||||
							
								
								
									
										212
									
								
								dep/lexy/include/lexy/_detail/string_view.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								dep/lexy/include/lexy/_detail/string_view.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,212 @@ | ||||
| // Copyright (C) 2020-2025 Jonathan Müller and lexy contributors | ||||
| // SPDX-License-Identifier: BSL-1.0 | ||||
|  | ||||
| #ifndef LEXY_DETAIL_STRING_VIEW_HPP_INCLUDED | ||||
| #define LEXY_DETAIL_STRING_VIEW_HPP_INCLUDED | ||||
|  | ||||
| #include <lexy/_detail/assert.hpp> | ||||
| #include <lexy/_detail/config.hpp> | ||||
| #include <lexy/_detail/integer_sequence.hpp> | ||||
|  | ||||
| namespace lexy::_detail | ||||
| { | ||||
| struct null_terminated | ||||
| {}; | ||||
|  | ||||
| template <typename CharT> | ||||
| class basic_string_view | ||||
| { | ||||
|     static constexpr CharT empty_string[] = {CharT()}; | ||||
|  | ||||
| public: | ||||
|     using char_type = CharT; | ||||
|  | ||||
|     //=== constructor ===// | ||||
|     constexpr basic_string_view() noexcept : _ptr(empty_string), _size(0u), _null_terminated(true) | ||||
|     {} | ||||
|  | ||||
|     constexpr basic_string_view(const char_type* str) noexcept | ||||
|     : _ptr(str), _size(0u), _null_terminated(true) | ||||
|     { | ||||
|         while (*str++) | ||||
|             ++_size; | ||||
|     } | ||||
|  | ||||
|     constexpr basic_string_view(const char_type* ptr, std::size_t size) noexcept | ||||
|     : _ptr(ptr), _size(size), _null_terminated(false) | ||||
|     {} | ||||
|     constexpr basic_string_view(null_terminated, const char_type* ptr, std::size_t size) noexcept | ||||
|     : _ptr(ptr), _size(size), _null_terminated(true) | ||||
|     { | ||||
|         LEXY_PRECONDITION(_ptr[_size] == CharT()); | ||||
|     } | ||||
|  | ||||
|     constexpr basic_string_view(const char_type* begin, const char_type* end) noexcept | ||||
|     : _ptr(begin), _size(std::size_t(end - begin)), _null_terminated(false) | ||||
|     { | ||||
|         LEXY_PRECONDITION(begin <= end); | ||||
|     } | ||||
|  | ||||
|     //=== access ===// | ||||
|     using iterator = const char_type*; | ||||
|  | ||||
|     constexpr iterator begin() const noexcept | ||||
|     { | ||||
|         return _ptr; | ||||
|     } | ||||
|     constexpr iterator end() const noexcept | ||||
|     { | ||||
|         return _ptr + _size; | ||||
|     } | ||||
|  | ||||
|     constexpr bool empty() const noexcept | ||||
|     { | ||||
|         return _size == 0u; | ||||
|     } | ||||
|     constexpr std::size_t size() const noexcept | ||||
|     { | ||||
|         return _size; | ||||
|     } | ||||
|     constexpr std::size_t length() const noexcept | ||||
|     { | ||||
|         return _size; | ||||
|     } | ||||
|  | ||||
|     constexpr char_type operator[](std::size_t i) const noexcept | ||||
|     { | ||||
|         LEXY_PRECONDITION(i <= _size); | ||||
|         return _ptr[i]; | ||||
|     } | ||||
|     constexpr char_type front() const noexcept | ||||
|     { | ||||
|         LEXY_PRECONDITION(!empty()); | ||||
|         return *_ptr; | ||||
|     } | ||||
|     constexpr char_type back() const noexcept | ||||
|     { | ||||
|         LEXY_PRECONDITION(!empty()); | ||||
|         return _ptr[_size - 1]; | ||||
|     } | ||||
|  | ||||
|     constexpr const char_type* data() const noexcept | ||||
|     { | ||||
|         return _ptr; | ||||
|     } | ||||
|  | ||||
|     constexpr bool is_null_terminated() const noexcept | ||||
|     { | ||||
|         return _null_terminated; | ||||
|     } | ||||
|  | ||||
|     constexpr const char_type* c_str() const noexcept | ||||
|     { | ||||
|         LEXY_PRECONDITION(is_null_terminated()); | ||||
|         return _ptr; | ||||
|     } | ||||
|  | ||||
|     //=== operations ===// | ||||
|     static constexpr std::size_t npos = std::size_t(-1); | ||||
|  | ||||
|     constexpr void remove_prefix(std::size_t n) noexcept | ||||
|     { | ||||
|         LEXY_PRECONDITION(n <= _size); | ||||
|         _ptr += n; | ||||
|         _size -= n; | ||||
|     } | ||||
|     constexpr void remove_suffix(std::size_t n) noexcept | ||||
|     { | ||||
|         LEXY_PRECONDITION(n <= _size); | ||||
|         _size -= n; | ||||
|         _null_terminated = false; | ||||
|     } | ||||
|  | ||||
|     constexpr basic_string_view substr(std::size_t pos, std::size_t length = npos) const noexcept | ||||
|     { | ||||
|         LEXY_PRECONDITION(pos < _size); | ||||
|         if (length >= _size - pos) | ||||
|         { | ||||
|             auto result             = basic_string_view(_ptr + pos, end()); | ||||
|             result._null_terminated = _null_terminated; | ||||
|             return result; | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             // Note that we're loosing null-terminated-ness. | ||||
|             return basic_string_view(_ptr + pos, length); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     constexpr bool starts_with(basic_string_view prefix) const noexcept | ||||
|     { | ||||
|         return substr(0, prefix.size()) == prefix; | ||||
|     } | ||||
|     constexpr bool try_remove_prefix(basic_string_view prefix) noexcept | ||||
|     { | ||||
|         if (!starts_with(prefix)) | ||||
|             return false; | ||||
|  | ||||
|         remove_prefix(prefix.length()); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     constexpr std::size_t find(basic_string_view str, std::size_t pos = 0) const noexcept | ||||
|     { | ||||
|         for (auto i = pos; i < length(); ++i) | ||||
|         { | ||||
|             if (substr(i, str.length()) == str) | ||||
|                 return i; | ||||
|         } | ||||
|  | ||||
|         return npos; | ||||
|     } | ||||
|     constexpr std::size_t find(CharT c, std::size_t pos = 0) const noexcept | ||||
|     { | ||||
|         return find(basic_string_view(&c, 1), pos); | ||||
|     } | ||||
|  | ||||
|     //=== comparison ===// | ||||
|     friend constexpr bool operator==(basic_string_view<CharT> lhs, | ||||
|                                      basic_string_view<CharT> rhs) noexcept | ||||
|     { | ||||
|         if (lhs.size() != rhs.size()) | ||||
|             return false; | ||||
|  | ||||
|         for (auto a = lhs.begin(), b = rhs.begin(); a != lhs.end(); ++a, ++b) | ||||
|             if (*a != *b) | ||||
|                 return false; | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     friend constexpr bool operator!=(basic_string_view<CharT> lhs, | ||||
|                                      basic_string_view<CharT> rhs) noexcept | ||||
|     { | ||||
|         return !(lhs == rhs); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     const CharT* _ptr; | ||||
|     std::size_t  _size; | ||||
|     bool         _null_terminated; | ||||
| }; | ||||
| using string_view = basic_string_view<char>; | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| namespace lexy::_detail | ||||
| { | ||||
| template <auto FnPtr, typename Indices = make_index_sequence<FnPtr().size()>> | ||||
| struct _string_view_holder; | ||||
| template <auto FnPtr, std::size_t... Indices> | ||||
| struct _string_view_holder<FnPtr, index_sequence<Indices...>> | ||||
| { | ||||
|     static constexpr auto view = FnPtr(); | ||||
|  | ||||
|     static constexpr typename decltype(view)::char_type value[] = {view[Indices]..., {}}; | ||||
| }; | ||||
|  | ||||
| template <auto FnPtr> | ||||
| inline constexpr const auto* make_cstr = _string_view_holder<FnPtr>::value; | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| #endif // LEXY_DETAIL_STRING_VIEW_HPP_INCLUDED | ||||
|  | ||||
							
								
								
									
										251
									
								
								dep/lexy/include/lexy/_detail/swar.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								dep/lexy/include/lexy/_detail/swar.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,251 @@ | ||||
| // Copyright (C) 2020-2025 Jonathan Müller and lexy contributors | ||||
| // SPDX-License-Identifier: BSL-1.0 | ||||
|  | ||||
| #ifndef LEXY_DETAIL_SWAR_HPP_INCLUDED | ||||
| #define LEXY_DETAIL_SWAR_HPP_INCLUDED | ||||
|  | ||||
| #include <climits> | ||||
| #include <cstdint> | ||||
| #include <cstring> | ||||
| #include <lexy/_detail/config.hpp> | ||||
| #include <lexy/input/base.hpp> | ||||
|  | ||||
| #if defined(_MSC_VER) | ||||
| #    include <intrin.h> | ||||
| #endif | ||||
|  | ||||
| namespace lexy::_detail | ||||
| { | ||||
| // Contains the chars in little endian order; rightmost bits are first char. | ||||
| using swar_int = std::uintmax_t; | ||||
|  | ||||
| // The number of chars that can fit into one SWAR. | ||||
| template <typename CharT> | ||||
| constexpr auto swar_length = [] { | ||||
|     static_assert(sizeof(CharT) < sizeof(swar_int) && sizeof(swar_int) % sizeof(CharT) == 0); | ||||
|     return sizeof(swar_int) / sizeof(CharT); | ||||
| }(); | ||||
|  | ||||
| template <typename CharT> | ||||
| constexpr auto char_bit_size = sizeof(CharT) * CHAR_BIT; | ||||
|  | ||||
| template <typename CharT> | ||||
| constexpr auto make_uchar(CharT c) | ||||
| { | ||||
|     if constexpr (std::is_same_v<CharT, LEXY_CHAR8_T>) | ||||
|         // Not all libstdc++ support char8_t and std::make_unsigned_t. | ||||
|         return c; | ||||
|     else | ||||
|         return std::make_unsigned_t<CharT>(c); | ||||
| } | ||||
| template <typename CharT> | ||||
| using uchar_t = decltype(make_uchar(CharT())); | ||||
|  | ||||
| // Returns a swar_int filled with the specific char. | ||||
| template <typename CharT> | ||||
| constexpr swar_int swar_fill(CharT _c) | ||||
| { | ||||
|     auto c = make_uchar(_c); | ||||
|  | ||||
|     auto result = swar_int(0); | ||||
|     for (auto i = 0u; i != swar_length<CharT>; ++i) | ||||
|     { | ||||
|         result <<= char_bit_size<CharT>; | ||||
|         result |= c; | ||||
|     } | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| // Returns a swar_int filled with the complement of the specific char. | ||||
| template <typename CharT> | ||||
| constexpr swar_int swar_fill_compl(CharT _c) | ||||
| { | ||||
|     auto c = uchar_t<CharT>(~uchar_t<CharT>(_c)); | ||||
|  | ||||
|     auto result = swar_int(0); | ||||
|     for (auto i = 0u; i != swar_length<CharT>; ++i) | ||||
|     { | ||||
|         result <<= char_bit_size<CharT>; | ||||
|         result |= c; | ||||
|     } | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| constexpr void _swar_pack(swar_int&, int) {} | ||||
| template <typename H, typename... T> | ||||
| constexpr void _swar_pack(swar_int& result, int index, H h, T... t) | ||||
| { | ||||
|     if (std::size_t(index) == char_bit_size<swar_int>) | ||||
|         return; | ||||
|  | ||||
|     if (index >= 0) | ||||
|         result |= swar_int(make_uchar(h)) << index; | ||||
|  | ||||
|     _swar_pack(result, index + int(char_bit_size<H>), t...); | ||||
| } | ||||
|  | ||||
| template <typename CharT> | ||||
| struct swar_pack_result | ||||
| { | ||||
|     swar_int    value; | ||||
|     swar_int    mask; | ||||
|     std::size_t count; | ||||
|  | ||||
|     constexpr CharT operator[](std::size_t idx) const | ||||
|     { | ||||
|         constexpr auto mask = (swar_int(1) << char_bit_size<CharT>)-1; | ||||
|         return (value >> idx * char_bit_size<CharT>)&mask; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| // Returns a swar_int containing the specified characters. | ||||
| // If more are provided than fit, will only take the first couple ones. | ||||
| template <int SkipFirstNChars = 0, typename... CharT> | ||||
| constexpr auto swar_pack(CharT... cs) | ||||
| { | ||||
|     using char_type = std::common_type_t<CharT...>; | ||||
|     swar_pack_result<char_type> result{0, 0, 0}; | ||||
|  | ||||
|     _swar_pack(result.value, -SkipFirstNChars * int(char_bit_size<char_type>), cs...); | ||||
|  | ||||
|     auto count = int(sizeof...(CharT)) - SkipFirstNChars; | ||||
|     if (count <= 0) | ||||
|     { | ||||
|         result.mask  = 0; | ||||
|         result.count = 0; | ||||
|     } | ||||
|     else if (count >= int(swar_length<char_type>)) | ||||
|     { | ||||
|         result.mask  = swar_int(-1); | ||||
|         result.count = swar_length<char_type>; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         result.mask  = swar_int(swar_int(1) << count * int(char_bit_size<char_type>)) - 1; | ||||
|         result.count = std::size_t(count); | ||||
|     } | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| // Returns the index of the char that is different between lhs and rhs. | ||||
| template <typename CharT> | ||||
| constexpr std::size_t swar_find_difference(swar_int lhs, swar_int rhs) | ||||
| { | ||||
|     if (lhs == rhs) | ||||
|         return swar_length<CharT>; | ||||
|  | ||||
|     auto mask = lhs ^ rhs; | ||||
|  | ||||
| #if defined(__GNUC__) | ||||
|     auto bit_idx = __builtin_ctzll(mask); | ||||
| #elif defined(_MSC_VER) && defined(_WIN64) | ||||
|     unsigned long bit_idx; | ||||
|     _BitScanForward64(&bit_idx, mask); | ||||
| #elif defined(_MSC_VER) | ||||
|     unsigned long bit_idx = 0; | ||||
|     if (!_BitScanForward(&bit_idx, static_cast<std::uint32_t>(mask)) | ||||
|         && _BitScanForward(&bit_idx, mask >> 32)) | ||||
|         bit_idx += 32; | ||||
| #else | ||||
| #    error "unsupported compiler; please file an issue" | ||||
| #endif | ||||
|  | ||||
|     return std::size_t(bit_idx) / char_bit_size<CharT>; | ||||
| } | ||||
|  | ||||
| // Returns true if v has a char less than N. | ||||
| template <typename CharT, CharT N> | ||||
| constexpr bool swar_has_char_less(swar_int v) | ||||
| { | ||||
|     // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord | ||||
|  | ||||
|     constexpr auto offset      = swar_fill(CharT(N)); | ||||
|     auto           zero_or_msb = v - offset; | ||||
|  | ||||
|     constexpr auto msb_mask = swar_fill(CharT(1 << (char_bit_size<CharT> - 1))); | ||||
|     auto           not_msb  = ~v & msb_mask; | ||||
|  | ||||
|     return zero_or_msb & not_msb; | ||||
| } | ||||
|  | ||||
| // Returns true if v has a zero char. | ||||
| template <typename CharT> | ||||
| constexpr bool swar_has_zero(swar_int v) | ||||
| { | ||||
|     return swar_has_char_less<CharT, 1>(v); | ||||
| } | ||||
|  | ||||
| // Returns true if v contains the specified char. | ||||
| template <typename CharT, CharT C> | ||||
| constexpr bool swar_has_char(swar_int v) | ||||
| { | ||||
|     if constexpr (C == 0) | ||||
|     { | ||||
|         return swar_has_zero<CharT>(v); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         constexpr auto mask = swar_fill(C); | ||||
|         return swar_has_zero<CharT>(v ^ mask); | ||||
|     } | ||||
| } | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| namespace lexy::_detail | ||||
| { | ||||
| struct _swar_base | ||||
| {}; | ||||
| template <typename Reader> | ||||
| constexpr auto is_swar_reader = std::is_base_of_v<_swar_base, Reader>; | ||||
|  | ||||
| template <typename Derived> | ||||
| class swar_reader_base : _swar_base | ||||
| { | ||||
| public: | ||||
|     swar_int peek_swar() const | ||||
|     { | ||||
|         auto ptr = static_cast<const Derived&>(*this).position(); | ||||
|  | ||||
|         swar_int result; | ||||
| #if LEXY_IS_LITTLE_ENDIAN | ||||
|         std::memcpy(&result, ptr, sizeof(swar_int)); | ||||
| #else | ||||
|         using char_type = typename Derived::encoding::char_type; | ||||
|         auto dst        = reinterpret_cast<char*>(&result); | ||||
|         auto length     = sizeof(swar_int) / sizeof(char_type); | ||||
|         for (auto i = 0u; i != length; ++i) | ||||
|         { | ||||
|             std::memcpy(dst + i, ptr + length - i - 1, sizeof(char_type)); | ||||
|         } | ||||
| #endif | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
|     void bump_swar() | ||||
|     { | ||||
|         auto ptr = static_cast<Derived&>(*this).position(); | ||||
|         ptr += swar_length<typename Derived::encoding::char_type>; | ||||
|         static_cast<Derived&>(*this).reset({ptr}); | ||||
|     } | ||||
|     void bump_swar(std::size_t char_count) | ||||
|     { | ||||
|         auto ptr = static_cast<Derived&>(*this).position(); | ||||
|         ptr += char_count; | ||||
|         static_cast<Derived&>(*this).reset({ptr}); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| constexpr std::size_t round_size_for_swar(std::size_t size_in_bytes) | ||||
| { | ||||
|     // We round up to the next multiple. | ||||
|     if (auto remainder = size_in_bytes % sizeof(swar_int); remainder > 0) | ||||
|         size_in_bytes += sizeof(swar_int) - remainder; | ||||
|     // Then add one extra space of padding on top. | ||||
|     size_in_bytes += sizeof(swar_int); | ||||
|     return size_in_bytes; | ||||
| } | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| #endif // LEXY_DETAIL_SWAR_HPP_INCLUDED | ||||
|  | ||||
							
								
								
									
										119
									
								
								dep/lexy/include/lexy/_detail/tuple.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								dep/lexy/include/lexy/_detail/tuple.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,119 @@ | ||||
| // Copyright (C) 2020-2025 Jonathan Müller and lexy contributors | ||||
| // SPDX-License-Identifier: BSL-1.0 | ||||
|  | ||||
| #ifndef LEXY_DETAIL_TUPLE_HPP_INCLUDED | ||||
| #define LEXY_DETAIL_TUPLE_HPP_INCLUDED | ||||
|  | ||||
| #include <lexy/_detail/config.hpp> | ||||
| #include <lexy/_detail/integer_sequence.hpp> | ||||
|  | ||||
| namespace lexy::_detail | ||||
| { | ||||
| template <std::size_t Idx, typename T> | ||||
| struct _tuple_holder | ||||
| { | ||||
| #if !defined(__GNUC__) || defined(__clang__) | ||||
|     // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105795 | ||||
|     LEXY_EMPTY_MEMBER | ||||
| #endif | ||||
|     T value; | ||||
| }; | ||||
|  | ||||
| template <std::size_t Idx, typename... T> | ||||
| struct _nth_type; | ||||
| template <std::size_t Idx, typename H, typename... T> | ||||
| struct _nth_type<Idx, H, T...> | ||||
| { | ||||
|     using type = typename _nth_type<Idx - 1, T...>::type; | ||||
| }; | ||||
| template <typename H, typename... T> | ||||
| struct _nth_type<0, H, T...> | ||||
| { | ||||
|     using type = H; | ||||
| }; | ||||
|  | ||||
| template <typename T> | ||||
| struct _tuple_get_type | ||||
| { | ||||
|     using type = T&; | ||||
| }; | ||||
| template <typename T> | ||||
| struct _tuple_get_type<T&&> | ||||
| { | ||||
|     using type = T&&; | ||||
| }; | ||||
|  | ||||
| template <typename Indices, typename... T> | ||||
| class _tuple; | ||||
| template <std::size_t... Idx, typename... T> | ||||
| class _tuple<index_sequence<Idx...>, T...> : public _tuple_holder<Idx, T>... | ||||
| { | ||||
| public: | ||||
|     constexpr _tuple() = default; | ||||
|  | ||||
|     template <typename... Args> | ||||
|     constexpr _tuple(Args&&... args) : _tuple_holder<Idx, T>{LEXY_FWD(args)}... | ||||
|     {} | ||||
| }; | ||||
|  | ||||
| template <typename... T> | ||||
| struct tuple : _tuple<index_sequence_for<T...>, T...> | ||||
| { | ||||
|     constexpr tuple() = default; | ||||
|  | ||||
|     template <typename... Args> | ||||
|     constexpr explicit tuple(Args&&... args) | ||||
|     : _tuple<index_sequence_for<T...>, T...>(LEXY_FWD(args)...) | ||||
|     {} | ||||
|  | ||||
|     template <std::size_t N> | ||||
|     using element_type = typename _nth_type<N, T...>::type; | ||||
|  | ||||
|     template <std::size_t N> | ||||
|     constexpr decltype(auto) get() noexcept | ||||
|     { | ||||
|         // NOLINTNEXTLINE: this is fine. | ||||
|         auto&& holder = static_cast<_tuple_holder<N, element_type<N>>&>(*this); | ||||
|         // NOLINTNEXTLINE | ||||
|         return static_cast<typename _tuple_get_type<element_type<N>>::type>(holder.value); | ||||
|     } | ||||
|     template <std::size_t N> | ||||
|     constexpr decltype(auto) get() const noexcept | ||||
|     { | ||||
|         // NOLINTNEXTLINE: this is fine. | ||||
|         auto&& holder = static_cast<const _tuple_holder<N, element_type<N>>&>(*this); | ||||
|         // NOLINTNEXTLINE | ||||
|         return static_cast<typename _tuple_get_type<const element_type<N>>::type>(holder.value); | ||||
|     } | ||||
|  | ||||
|     static constexpr auto index_sequence() | ||||
|     { | ||||
|         return index_sequence_for<T...>{}; | ||||
|     } | ||||
| }; | ||||
| template <> | ||||
| struct tuple<> | ||||
| { | ||||
|     constexpr tuple() = default; | ||||
|  | ||||
|     static constexpr auto index_sequence() | ||||
|     { | ||||
|         return index_sequence_for<>{}; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| template <typename... Args> | ||||
| constexpr auto make_tuple(Args&&... args) | ||||
| { | ||||
|     return tuple<std::decay_t<Args>...>(LEXY_FWD(args)...); | ||||
| } | ||||
|  | ||||
| template <typename... Args> | ||||
| constexpr auto forward_as_tuple(Args&&... args) | ||||
| { | ||||
|     return tuple<Args&&...>(LEXY_FWD(args)...); | ||||
| } | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| #endif // LEXY_DETAIL_TUPLE_HPP_INCLUDED | ||||
|  | ||||
							
								
								
									
										130
									
								
								dep/lexy/include/lexy/_detail/type_name.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								dep/lexy/include/lexy/_detail/type_name.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,130 @@ | ||||
| // Copyright (C) 2020-2025 Jonathan Müller and lexy contributors | ||||
| // SPDX-License-Identifier: BSL-1.0 | ||||
|  | ||||
| #ifndef LEXY_DETAIL_TYPE_NAME_HPP_INCLUDED | ||||
| #define LEXY_DETAIL_TYPE_NAME_HPP_INCLUDED | ||||
|  | ||||
| #include <lexy/_detail/config.hpp> | ||||
| #include <lexy/_detail/detect.hpp> | ||||
| #include <lexy/_detail/integer_sequence.hpp> | ||||
| #include <lexy/_detail/string_view.hpp> | ||||
|  | ||||
| namespace lexy::_detail | ||||
| { | ||||
| template <typename T> | ||||
| using _detect_name_f = std::enable_if_t<std::is_convertible_v<decltype(T::name()), string_view>>; | ||||
| template <typename T> | ||||
| using _detect_name_v = decltype(T::name); | ||||
|  | ||||
| template <typename T> | ||||
| constexpr auto _full_type_name() | ||||
| { | ||||
| #if defined(__clang__) | ||||
| #    define LEXY_HAS_AUTOMATIC_TYPE_NAME 1 | ||||
| #    define LEXY_HAS_CONSTEXPR_AUTOMATIC_TYPE_NAME 1 | ||||
|  | ||||
|     constexpr auto prefix = string_view("auto lexy::_detail::_full_type_name() [T = "); | ||||
|     constexpr auto suffix = string_view("]"); | ||||
|  | ||||
|     auto function = string_view(__PRETTY_FUNCTION__); | ||||
|     function.remove_prefix(prefix.length()); | ||||
|     function.remove_suffix(suffix.length()); | ||||
|     function.try_remove_prefix("(anonymous namespace)::"); | ||||
|     return function; | ||||
|  | ||||
| #elif defined(__GNUC__) | ||||
| #    define LEXY_HAS_AUTOMATIC_TYPE_NAME 1 | ||||
| #    if __GNUC__ > 8 | ||||
| #        define LEXY_HAS_CONSTEXPR_AUTOMATIC_TYPE_NAME 1 | ||||
| #    else | ||||
| #        define LEXY_HAS_CONSTEXPR_AUTOMATIC_TYPE_NAME 0 | ||||
| #    endif | ||||
|  | ||||
|     constexpr auto prefix | ||||
|         = string_view("constexpr auto lexy::_detail::_full_type_name() [with T = "); | ||||
|     constexpr auto suffix = string_view("]"); | ||||
|  | ||||
|     auto function = string_view(__PRETTY_FUNCTION__); | ||||
|     function.remove_prefix(prefix.length()); | ||||
|     function.remove_suffix(suffix.length()); | ||||
|     function.try_remove_prefix("{anonymous}::"); | ||||
|     return function; | ||||
|  | ||||
| #elif defined(_MSC_VER) | ||||
| #    define LEXY_HAS_AUTOMATIC_TYPE_NAME 1 | ||||
| #    define LEXY_HAS_CONSTEXPR_AUTOMATIC_TYPE_NAME 1 | ||||
|  | ||||
|     constexpr auto prefix = string_view("auto __cdecl lexy::_detail::_full_type_name<"); | ||||
|     constexpr auto suffix = string_view(">(void)"); | ||||
|  | ||||
|     auto function = string_view(__FUNCSIG__); | ||||
|     function.remove_prefix(prefix.length()); | ||||
|     function.remove_suffix(suffix.length()); | ||||
|     function.try_remove_prefix("struct ") || function.try_remove_prefix("class "); | ||||
|     function.try_remove_prefix("`anonymous-namespace'::"); | ||||
|     return function; | ||||
|  | ||||
| #else | ||||
| #    define LEXY_HAS_AUTOMATIC_TYPE_NAME 0 | ||||
| #    define LEXY_HAS_CONSTEXPR_AUTOMATIC_TYPE_NAME 0 | ||||
|  | ||||
|     return string_view("unknown-type"); | ||||
|  | ||||
| #endif | ||||
| } | ||||
|  | ||||
| template <typename T, int NsCount> | ||||
| constexpr string_view _type_name() | ||||
| { | ||||
|     auto name = _full_type_name<T>(); | ||||
|     if (name.find('<') != string_view::npos && NsCount != 0) | ||||
|         return name; | ||||
|  | ||||
|     for (auto namespace_count = NsCount; namespace_count > 0; --namespace_count) | ||||
|     { | ||||
|         auto pos = name.find("::"); | ||||
|         if (pos == string_view::npos) | ||||
|             break; | ||||
|         name.remove_prefix(pos + 2); | ||||
|     } | ||||
|     return name; | ||||
| } | ||||
|  | ||||
| template <typename T, int NsCount = 1> | ||||
| constexpr const char* type_name() | ||||
| { | ||||
|     if constexpr (_detail::is_detected<_detect_name_f, T>) | ||||
|         return T::name(); | ||||
|     else if constexpr (_detail::is_detected<_detect_name_v, T>) | ||||
|         return T::name; | ||||
|     else if constexpr (LEXY_HAS_CONSTEXPR_AUTOMATIC_TYPE_NAME) | ||||
|         return make_cstr<_type_name<T, NsCount>>; | ||||
|     else | ||||
|         return "unknown-type"; | ||||
| } | ||||
|  | ||||
| template <typename T, int NsCount> | ||||
| inline constexpr const char* _type_id_holder = type_name<T, NsCount>(); | ||||
|  | ||||
| // Returns a unique address for each type. | ||||
| // For implementation reasons, it also doubles as the pointer to the name. | ||||
| template <typename T, int NsCount = 1> | ||||
| constexpr const char* const* type_id() | ||||
| { | ||||
|     if constexpr (_detail::is_detected<_detect_name_v, T> // | ||||
|                   && !_detail::is_detected<_detect_name_f, T>) | ||||
|     { | ||||
|         // We can use the address of the static constexpr directly. | ||||
|         return &T::name; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         // We instantiate a variable template with a function unique by type. | ||||
|         // As the variable is inline, there should be a single address only. | ||||
|         return &_type_id_holder<T, NsCount>; | ||||
|     } | ||||
| } | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| #endif // LEXY_DETAIL_TYPE_NAME_HPP_INCLUDED | ||||
|  | ||||
							
								
								
									
										83
									
								
								dep/lexy/include/lexy/_detail/unicode_database.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								dep/lexy/include/lexy/_detail/unicode_database.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | ||||
| // AUTOGENERATED FILE --- DO NOT EDIT! | ||||
| // Generated by `support/generate-unicode-db.py`. | ||||
|  | ||||
| #define LEXY_UNICODE_DATABASE_VERSION "15.0.0" | ||||
|  | ||||
| namespace lexy::_unicode_db | ||||
| { | ||||
| constexpr const char* block_starts = "\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\021\025\026\027\030\031\032\033\034\035\036\037 !\"#$%&'(!)*+,-./0'\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\0211\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\0212\021\021\0213\021456789\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021:;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<\021=>?@ABCDEFGH\021IJKLMNOPQRSTUVWXYZ[\\]^_`a\021\021\021bcdeeeeeeeeef\021\021\021\021geeeeeeeeeeeeeee\021\021heeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\021\021ijeekl\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021m\021\021\021\021noeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeep\021qreeeeeeeeeseeeeeeeeeeeeeeeeeetuvwxyz{|''}eeee~\177\200\201e\202ee\203\204\205ee\206\207\210e\211\212\213\214''\215\216\217'\220\221eeee\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\222\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\223\224\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\225\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\226eeeeeeeeeeee\021\021\227eeeee\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\230\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\231eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" | ||||
|     "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" | ||||
|     "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" | ||||
|     "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\232\233eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\234" | ||||
|     "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\234"; | ||||
| constexpr const char* blocks = "\000\000\000\000\000\000\000\000\000\001\001\001\001\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\002\003\003\003\004\003\003\003\005\006\003\007\003\010\003\003\011\011\011\011\011\011\011\011\011\011\003\003\007\007\007\003\003\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\005\003\006\013\014\013\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\005\007\006\007\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\002\003\004\004\004\004\016\003\013\016\017\020\007\021\016\013\016\007\022\022\013\023\003\024\013\022\017\025\022\022\022\003\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\007\012\012\012\012\012\012\012\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\007\015\015\015\015\015\015\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\027\015\026\015\026\015\026\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\030\026\015\026\015\026\015\031\015\032\026\015\026\015\033\026\015\034\034\026\015\015\035\036\037\026\015\034 \015!\"\026\015\015\015!#\015$\026\015\026\015\026\015%\026\015%\015\015\026\015%\026\015&&\026\015\026\015'\026\015\015(\026\015\015\015(((()*\015)*\015)*\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015)*\015\026\015+,\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015-\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\015\015\015\015\015.\026\015/0\015\015\026\015123\026\015\026\015\026\015\026\015\026\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015(\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015444444444555555544\013\013\013\013555555555555\013\013\013\013\013\013\013\013\013\013\013\013\013\01344444\013\013\013\013\013\013\0135\0135\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\0136666666666666666666666666666666666666666666666666666666666666666666667666666666666666666666666666666666666666666\026\015\026\0155\013\026\015889\015\015\015\003:8888\013\013;\024<<<8=8>>\015\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\0128\012\012\012\012\012\012\012\012\012\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015?\015\015\015\015\015\015\015\015\015\015\015\015@AB\027\027\027CD\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015EF\015\015GH\007\026\015I\026\015\015---" | ||||
|     "JJJJJJJJJJJJJJJJ\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\01666666KK\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015L\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\0158MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM885\003\003\003\003\003\003\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\003\01088\016\016\00486666666666666666666666666666666NNNNNNNNNNNNNN\010N\003NN\003NN\003N88888888(((((((((((((((((((((((((((8888((((\003\00388888888888\021\021\021\021\021\021\007\007\007\003\003\004\003\003\016\016NNNNNNNNNNN\003\021\003\003\003((((((((((((((((((((((((((((((((5((((((((((NNNNNNNNNNNNN6NNNNNNN\011\011\011\011\011\011\011\011\011\011\003\003\003\003((N(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\003(NNNNNNN\021\01666NNNN55NN\016666N((\011\011\011\011\011\011\011\011\011\011(((\016\016(\003\003\003\003\003\003\003\003\003\003\003\003\003\0038\021(N((((((((((((((((((((((((((((((NNNNNNNNNNNNNNNN6666666666688(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((NNNNNNNNNNN(88888888888888\011\011\011\011\011\011\011\011\011\011(((((((((((((((((((((((((((((((((66666666655\016\003\003\0035886\004\004" | ||||
|     "((((((((((((((((((((((NN665NNNNNNNNN5NNN5NNNN688\003\003\003\003\003\003\003\003\003\003\003\003\003\003\0038(((((((((((((((((((((((((66688\0038(((((((((((88888((((((((((((((((((((((((\013((((((8\021\02188888866666666(((((((((((((((((((((((((((((((((((((((((56666666666NNNNNNNNNNNN66\021NNNNNNN666666NNNNNNNNNNNNNNNNNNNO((((((((((((((((((((((((((((((((((((((((((((((((((((((NO6(OOONNNNNNNNOOOO6OO(6666NNN((((((((((NN\003\003\011\011\011\011\011\011\011\011\011\011\0035(((((((((((((((NOO8((((((((88((88((((((((((((((((((((((8(((((((8(888((((886(OOONNNN88OO88OO6(88888888O8888((8(((NN88\011\011\011\011\011\011\011\011\011\011((\004\004\022\022\022\022\022\022\016\004(\003688NNO8((((((8888((88((((((((((((((((((((((8(((((((8((8((8((8868OOONN8888NN88NN6888N8888888((((8(8888888\011\011\011\011\011\011\011\011\011\011NN(((N\0038888888888NNO8(((((((((8(((8((((((((((((((((((((((8(((((((8((8(((((886(OOONNNNN8NNO8OO688(888888888888888((NN88\011\011\011\011\011\011\011\011\011\011\003\0048888888(NNN6668NOO8((((((((88((88((((((((((((((((((((((8(((((((8((8(((((886(ONONNNN88OO88OO688888886NO8888((8(((NN88\011\011\011\011\011\011\011\011\011\011\016(\022\022\022\022\022\0228888888888N(8((((((888(((8((((888((8(8((888((888(((888((((((((((((8888OONOO888OOO8OOO688(888888O88888888888888\011\011\011\011\011\011\011\011\011\011\022\022\022\016\016\016\016\016\016\004\01688888" | ||||
|     "NOOON((((((((8(((8(((((((((((((((((((((((8((((((((((((((((886(NNNOOOO8NNN8NNN68888888NN8(((88(88((NN88\011\011\011\011\011\011\011\011\011\0118888888\003\022\022\022\022\022\022\022\016(NOO\003((((((((8(((8(((((((((((((((((((((((8((((((((((8(((((886(ONOOOOO8NOO8OON68888888OO888888((8((NN88\011\011\011\011\011\011\011\011\011\0118((O888888888888NNOO(((((((((8(((8(((((((((((((((((((((((((((((((((((((((((66(OOONNNN8OOO8OOO6(\0168888(((O\022\022\022\022\022\022\022(((NN88\011\011\011\011\011\011\011\011\011\011\022\022\022\022\022\022\022\022\022\016((((((8NOO8((((((((((((((((((888((((((((((((((((((((((((8(((((((((8(88(((((((88868888OOONNN8N8OOOOOOOO888888\011\011\011\011\011\011\011\011\011\01188OO\003888888888888((((((((((((((((((((((((((((((((((((((((((((((((N(PNNNNNNN8888\004((((((5666666N6\003\011\011\011\011\011\011\011\011\011\011\003\0038888888888888888888888888888888888888((8(8(((((8((((((((((((((((((((((((8(8((((((((((N(PNNNNNN6NN(88(((((85866666N68\011\011\011\011\011\011\011\011\011\01188((((88888888888888888888888888888888(\016\016\016\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\016\003\016\016\01666\016\016\016\016\016\016\011\011\011\011\011\011\011\011\011\011\022\022\022\022\022\022\022\022\022\022\0166\0166\0166\005\006\005\006QQ((((((((8((((((((((((((((((((((((((((((((((((8888NNNNNNNNNNNNNNONNNN6\00366(((((NNNNNNNNNNN8NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN8\016\016\016\016\016\016\016\0166\016\016\016\016\016\0168\016\016\003\003\003\003\003\016\016\016\016\003\0038888888888888888888888888888888888888" | ||||
|     "(((((((((((((((((((((((((((((((((((((((((((OONNNNONNNNN6O66OONN(\011\011\011\011\011\011\011\011\011\011\003\003\003\003\003\003((((((OONN((((NNN(OOO((OOOOOOO(((NNNN(((((((((((((NOONNOOOOOON(O\011\011\011\011\011\011\011\011\011\011OOON\016\016RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR8R88888R88\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0034\015\015\015(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8((((88(((((((8(8((((88(((((((((((((((((((((((((((((((((((((((((8((((88(((((((((((((((((((((((((((((((((8((((88(((((((8(8((((88(((((((((((((((8(((((((((((((((((((((((((((((((((((((((((((((((((((((((((8((((88(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88666\003\003\003\003\003\003\003\003\003SSSSSSSSS\022\022\022\022\022\022\022\022\022\022\022888((((((((((((((((\016\016\016\016\016\016\016\016\016\016888888\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\02788TTTTTT88" | ||||
|     "\010((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\016\003(((((((((((((((((\002((((((((((((((((((((((((((\005\006888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\003\003\003UUU((((((((8888888((((((((((((((((((NN6Q888888888(((((((((((((((((((NNQ\003\003888888888((((((((((((((((((NN888888888888(((((((((((((8(((8NN888888888888((((((((((((((((((((((((((((((((((((((((((((((((((((66ONNNNNNNOOOOOOOONOO66666666666\003\003\0035\003\003\003\004(688\011\011\011\011\011\011\011\011\011\011888888\022\022\022\022\022\022\022\022\022\022888888\003\003\003\003\003\003\010\003\003\003\003666\0216\011\011\011\011\011\011\011\011\011\011888888(((((((((((((((((((((((((((((((((((5(((((((((((((((((((((((((((((((((((((((((((((((((((((8888888(((((VV((((((((((((((((((((((((((((((((((N(88888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888888888" | ||||
|     "(((((((((((((((((((((((((((((((8NNNOOOONNOOO8888OONOOOOOO6668888\016888\003\003\011\011\011\011\011\011\011\011\011\011((((((((((((((((((((((((((((((88(((((88888888888((((((((((((((((((((((((((((((((((((((((((((8888((((((((((((((((((((((((((888888\011\011\011\011\011\011\011\011\011\011S888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016(((((((((((((((((((((((NNOON88\003\003(((((((((((((((((((((((((((((((((((((((((((((((((((((ONONNNNNNN86ONOONNNNNNNNOOOOOONN66666666886\011\011\011\011\011\011\011\011\011\011888888\011\011\011\011\011\011\011\011\011\011888888\003\003\003\003\003\003\0035\003\003\003\003\003\0038866666666666666KNN66666666666NNN8888888888888888888888888888888888888888888888888NNNNO(((((((((((((((((((((((((((((((((((((((((((((((6ONNNNNONOOOOONOQ((((((((888\011\011\011\011\011\011\011\011\011\011\003\003\003\003\003\003\003\016\016\016\016\016\016\016\016\016\016666666666\016\016\016\016\016\016\016\016\016\003\0038NNO((((((((((((((((((((((((((((((ONNNNOONNQ6NN((\011\011\011\011\011\011\011\011\011\011((((((((((((((((((((((((((((((((((((((((((((6ONNOOONONNNQQ88888888\003\003\003\003((((((((((((((((((((((((((((((((((((OOOOOOOONNNNNNNNOON6888\003\003\003\003\003\011\011\011\011\011\011\011\011\011\011888(((\011\011\011\011\011\011\011\011\011\011((((((((((((((((((((((((((((((555555\003\003WXYZZ[\\]^8888888___________________________________________88___\003\003\003\003\003\003\003\00388888888666\0036666666666666Q6666666((((6((((((6((Q66(88888" | ||||
|     "\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015444444444444444444444444444444444444444444444444444444444444444\015\015\015\015\015\015\015\015\015\015\015\015\0154\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0154444444444444444444444444444444444444666666666666666666666666666666666666666NNNNNNNNNNNNNN66666666666\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\015\015\015\015`\015\015a\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\015\015\015\015\015\015\015bbbbbbbb\015\015\015\015\015\01588bbbbbb88\015\015\015\015\015\015\015\015bbbbbbbb\015\015\015\015\015\015\015\015bbbbbbbb\015\015\015\015\015\01588bbbbbb88\015\015\015\015\015\015\015\0158b8b8b8b\015\015\015\015\015\015\015\015bbbbbbbb\015\015\015\015\015\015\015\015\015\015\015\015\015\01588\015\015\015\015\015\015\015\015cccccccc\015\015\015\015\015\015\015\015cccccccc\015\015\015\015\015\015\015\015cccccccc\015\015\015\015\0158\015\015bbdde\013f\013\013\013\015\015\0158\015\015gggge\013\013\013\015\015\015\01588\015\015bbhh8\013\013\013\015\015\015\015\015\015\015\015bbiiI\013\013\01388\015\015\0158\015\015jjkke\013\0138\002\002\002\002\002\002\002\002\002\002\002\021ll\021\021\010\010\010\010\010\010\003\003\020\025\005\020\020\025\005\020\003\003\003\003\003\003\003\003mn\021\021\021\021\021\002\003\003\003\003\003\003\003\003\003\020\025\003\003\003\003\014\014\003\003\003\007\005\006\003\003\003\003\003\003\003\003\003\003\003\007\003\014\003\003\003\003\003\003\003\003\003\003\002\021\021\021\021\0218\021\021\021\021\021\021\021\021\021\021\022488\022\022\022\022\022\022\007\007\007\005\0064\022\022\022\022\022\022\022\022\022\022\007\007\007\005\00684444444444444888\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\004\0048888888888888886666666666666KKKK6KKK666666666666888888888888888" | ||||
|     "\016\016\027\016\016\016\016\027\016\016\015\027\027\027\015\015\027\027\027\015\016\027\016\016o\027\027\027\027\027\016\016\016\016\016\016\027\016p\016\027\016qr\027\027s\015\027\027t\027\015((((\015\016\016\015\015\027\027\007\007\007\007\007\027\015\015\015\015\016\007\016\016\015\016\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022uuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvvUUU\026\015UUUU\022\016\0168888\007\007\007\007\007\016\016\016\016\016\007\007\016\016\016\016\007\016\016\007\016\016\007\016\016\016\016\016\016\016\007\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\007\007\016\016\007\016\007\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\016\016\016\016\016\016\016\016\005\006\005\006\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\007\007\016\016\016\016\016\016\016\005\006\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\007\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\007\007\007\007\007\007\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888888888888888888888888\016\016\016\016\016\016\016\016\016\016\016888888888888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016wwwwwwwwwwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxx\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022" | ||||
|     "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\007\016\016\016\016\016\016\016\016\016\007\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\007\007\007\007\007\007\007\007\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\007\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\005\006\005\006\005\006\005\006\005\006\005\006\005\006\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\007\007\007\007\007\005\006\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\005\006\005\006\005\006\005\006\005\006\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016" | ||||
|     "\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\005\006\005\006\005\006\005\006\005\006\005\006\005\006\005\006\005\006\005\006\005\006\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\005\006\005\006\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\005\006\007\007\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\007\016\016\007\007\007\007\007\007\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\026\015yz{\015\015\026\015\026\015\026\015|}~\177\015\026\015\015\026\015\015\015\015\015\01544\200\200\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\016\016\016\016\016\016\026\015\026\015666\026\01588888\003\003\003\003\022\003\003\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0158\01588888\01588((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888885\003888888888888886(((((((((((((((((((((((888888888(((((((8(((((((8(((((((8(((((((8(((((((8(((((((8(((((((8(((((((8NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN" | ||||
|     "\003\003\020\025\020\025\003\003\003\020\025\003\020\025\003\003\003\003\003\003\003\003\003\010\003\003\010\003\020\025\003\003\020\025\005\006\005\006\005\006\005\006\003\003\003\003\003\201\003\003\003\003\003\003\003\003\003\003\010\010\003\003\003\003\010\003\005\003\003\003\003\003\003\003\003\003\003\003\003\003\016\016\003\003\003\005\006\005\006\005\006\005\006\0108888888888888888888888888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688888888888888888888888888\016\016\016\016\016\016\016\016\016\016\016\0168888\002\003\003\003\0165(U\005\006\005\006\005\006\005\006\005\006\016\016\005\006\005\006\005\006\005\006\010\005\006\006\016UUUUUUUUU6666QQ\01055555\016\016UUU5(\003\016\0168((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8866\013\01355(\010((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\003555(88888(((((((((((((((((((((((((((((((((((((((((((8((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8\016\016\022\022\022\022\016\016\016\016\016\016\016\016\016\016((((((((((((((((((((((((((((((((\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888888((((((((((((((((" | ||||
|     "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168\022\022\022\022\022\022\022\022\022\022\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\022\022\022\022\022\022\022\022\016\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\022\022\022\022\022\022\022\022\022\022\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016(((((((((((((((((((((5(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888((((((((((((((((((((((((((((((((((((((((555555\003\003" | ||||
|     "((((((((((((5\003\003\003((((((((((((((((\011\011\011\011\011\011\011\011\011\011((88888888888888888888\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015(6KKK\003NNNNNNNN66\0035\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\01544NN((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((UUUUUUUUUU66\003\003\003\003\003\00388888888\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013555555555\013\013\026\015\026\015\026\015\026\015\026\015\026\015\026\015\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\0154\015\015\015\015\015\015\015\015\026\015\026\015\202\026\015\026\015\026\015\026\015\026\0155\013\013\026\015\203\015(\026\015\026\015\015\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\204\205\206\207\204\015\210\211\212\213\026\015\026\015\026\015\026\015\026\015\026\015\026\015\026\015\214\215\216\026\015\026\01588888\026\0158\0158\015\026\015\026\015888888888888888888888888444\026\015(44\015(((((((N(((6((((N(((((((((((((((((((((((OONNO\016\016\016\0166888\022\022\022\022\022\022\016\016\004\016888888((((((((((((((((((((((((((((((((((((((((((((((((((((\003\003\003\00388888888OO((((((((((((((((((((((((((((((((((((((((((((((((((OOOOOOOOOOOOOOOO6N88888888\003\003\011\011\011\011\011\011\011\011\011\011888888666666666666666666((((((\003\003\003(\003((N\011\011\011\011\011\011\011\011\011\011((((((((((((((((((((((((((((NNNNN666\003\003(((((((((((((((((((((((NNNNNNNNNNNOQ88888888888\003(((((((((((((((((((((((((((((888NNNO(((((((((((((((((((((((((((((((((((((((((((((((6OONNNNOONNOOQ\003\003\003\003\003\003\003\003\003\003\003\003\00385\011\011\011\011\011\011\011\011\011\0118888\003\003(((((N5(((((((((\011\011\011\011\011\011\011\011\011\011(((((8" | ||||
|     "(((((((((((((((((((((((((((((((((((((((((NNNNNNOONNOONN888888888(((N((((((((NO88\011\011\011\011\011\011\011\011\011\01188\003\003\003\003((((((((((((((((5((((((\016\016\016(ONO((((((((((((((((((((((((((((((((((((((((((((((((((N(NNN((NN(((((N6(6(888888888888888888888888((5\003\003(((((((((((ONNOO\003\003(55O68888888888((((((88((((((88((((((888888888(((((((8(((((((8\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0134444\015\015\015\015\015\015\015\015\0154\013\0138888\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217\217(((((((((((((((((((((((((((((((((((OONOONOO\003Q688\011\011\011\011\011\011\011\011\011\011888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((888888888888(((((((((((((((((((((((8888(((((((((((((((((((((((((((((((((((((((((((((((((8888\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220\220" | ||||
|     "\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888888888888888888888888888888888888\015\015\015\015\015\015\015888888888888\015\015\015\015\01588888(N((((((((((\007(((((((((((((8(((((8(8((8((8((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\013\0138888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\222\222\222\222\222\222((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((" | ||||
|     "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\006\005\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88((((((((((((((((((((((((((((((((((((((((((((((((((((((8888888\01688888888888888888888888888888888((((((((((\222\222\004\016\016\0166666666666666666\003\003\003\003\003\003\003\005\006\0038888886666666666666666\003\010\010\014\014\005\006\005\006\005\006\005\006\005\006\005\006\005\006\005\006\003\003\005\006\003\003\003\003\014\014\014\003\003\0038\003\003\003\003\010\005\006\005\006\005\006\003\003\003\007\010\007\007\0078\003\004\003\0038888\222(\222(\2228\222(\222(\222(\222(\222((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88\0218\003\003\003\004\003\003\003\005\006\003\007\003\010\003\003\011\011\011\011\011\011\011\011\011\011\003\003\007\007\007\003\003\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\005\003\006\013\014\013\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\005\007\006\007\005\006\003\005\006\003\003((((((((((5(((((((((((((((((((((((((((((((((((((((((((((\223\223(((((((((((((((((((((((((((((((888((((((88((((((88((((((88(((888\004\004\007\013\016\004\0048\016\007\007\007\007\016\0168888888888\021\021\021\016\01688((((((((((((8((((((((((((((((((((((((((8(((((((((((((((((((8((8(((((((((((((((88((((((((((((((8888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888" | ||||
|     "\003\003\0038888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022888\016\016\016\016\016\016\016\016\016UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU\022\022\022\022\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\022\022\016\016\0168\016\016\016\016\016\016\016\016\016\016\016\016\016888\01688888888888888888888888888888888888888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01668888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((888(((((((((((((((((((((((((((((((((((((((((((((((((8888888888888886\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\0228888((((((((((((((((((((((((((((((((\022\022\022\022888888888((((((((((((((((((((U((((((((U88888((((((((((((((((((((((((((((((((((((((NNNNN88888((((((((((((((((((((((((((((((8\003((((((((((((((((((((((((((((((((((((8888((((((((\003UUUUU888888888888888888888888888888888888888888\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88\011\011\011\011\011\011\011\011\011\011888888\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\224\2248888\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0158888" | ||||
|     "((((((((((((((((((((((((((((((((((((((((88888888((((((((((((((((((((((((((((((((((((((((((((((((((((88888888888\003\225\225\225\225\225\225\225\225\225\225\2258\225\225\225\225\225\225\225\225\225\225\225\225\225\225\2258\225\225\225\225\225\225\2258\225\2258\015\015\015\015\015\015\015\015\015\015\0158\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0158\015\015\015\015\015\015\0158\015\0158888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((888888888((((((((((((((((((((((8888888888((((((((88888888888888888888888845544484444444444444444444444444444444444444444448444444444888888888888888888888888888888888888888888888888888888888888888888888((((((88(8((((((((((((((((((((((((((((((((((((((((((((8((888(88(((((((((((((((((((((((8\003\022\022\022\022\022\022\022\022(((((((((((((((((((((((\016\016\022\022\022\022\022\022\022(((((((((((((((((((((((((((((((88888888\022\022\022\022\022\022\022\022\022888888888888888888888888888888888888888888888888(((((((((((((((((((8((88888\022\022\022\022\022((((((((((((((((((((((\022\022\022\022\022\022888\003((((((((((((((((((((((((((88888\0038888888888888888888888888888888888888888888888888888888888888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888\022\022((\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\02288\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022" | ||||
|     "(NNN8NN88888NNNN((((8(((8(((((((((((((((((((((((((((((8866688886\022\022\022\022\022\022\022\022\0228888888\003\003\003\003\003\003\003\003\0038888888(((((((((((((((((((((((((((((\022\022\003(((((((((((((((((((((((((((((\022\022\02288888888888888888888888888888888((((((((\016((((((((((((((((((((((((((((668888\022\022\022\022\022\003\003\003\003\003\003\003888888888((((((((((((((((((((((((((((((((((((((((((((((((((((((888\003\003\003\003\003\003\003((((((((((((((((((((((88\022\022\022\022\022\022\022\022(((((((((((((((((((88888\022\022\022\022\022\022\022\022((((((((((((((((((8888888\003\003\003\003888888888888\022\022\022\022\022\022\02288888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888888888888888888888888888888888888888888888888888888===================================================8888888888888\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0158888888\022\022\022\022\022\022((((((((((((((((((((((((((((((((((((NNNN88888888\011\011\011\011\011\011\011\011\011\011888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888" | ||||
|     "888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\0228((((((((((((((((((((((((((((((((((((((((((8NN\01088((888888888888888888888888888888888888888888888888888888888888888888888888888666(((((((((((((((((((((((((((((\022\022\022\022\022\022\022\022\022\022(88888888((((((((((((((((((((((66666666666\022\022\022\022\003\003\003\003\0038888888888888888888888((((((((((((((((((6666\003\003\003\00388888888888888888888888888888888888888(((((((((((((((((((((\022\022\022\022\022\022\02288888888888888888888(((((((((((((((((((((((888888888ONO(((((((((((((((((((((((((((((((((((((((((((((((((((((NNNNNNNNNNNNNN6\003\003\003\003\003\003\0038888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\011\011\011\011\011\011\011\011\011\0116((NN(8888888886NNO(((((((((((((((((((((((((((((((((((((((((((((OOONNNNOO66\003\003\021\003\003\003\003N8888888888\02188(((((((((((((((((((((((((8888888\011\011\011\011\011\011\011\011\011\011888888NNN((((((((((((((((((((((((((((((((((((NNNNNONNNNNN668\011\011\011\011\011\011\011\011\011\011\003\003\003\003(OO(88888888(((((((((((((((((((((((((((((((((((6\003\003(888888888NNO((((((((((((((((((((((((((((((((((((((((((((((((OOONNNNNNNNNOQ((((\003\003\003\0036666\003ON\011\011\011\011\011\011\011\011\011\011(\003(\003\003\0038\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\02288888888888" | ||||
|     "((((((((((((((((((8(((((((((((((((((((((((((OOONNNOONQ6N\003\003\003\003\003\003N((N88888888888888888888888888888888888888888888888888888888888888(((((((8(8((((8(((((((((((((((8((((((((((\003888888(((((((((((((((((((((((((((((((((((((((((((((((NOOONNNNNN6688888\011\011\011\011\011\011\011\011\011\011888888NNOO8((((((((88((88((((((((((((((((((((((8(((((((8((8(((((866(OONOOOO88OO88OOQ88(888888O88888(((((OO886666666888666668888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((OOONNNNNNNNOO6NNO6((((\003\003\003\003\003\011\011\011\011\011\011\011\011\011\011\003\0038\0036(((888888888888888888888888888888((((((((((((((((((((((((((((((((((((((((((((((((OOONNNNNNONOOOONNO66((\003(88888888\011\011\011\011\011\011\011\011\011\0118888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((OOONNNN88OOOONNO66\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003\003((((NN8888888888888888888888888888888888" | ||||
|     "((((((((((((((((((((((((((((((((((((((((((((((((OOONNNNNNNNOONO6N\003\003\003(88888888888\011\011\011\011\011\011\011\011\011\011888888\003\003\003\003\003\003\003\003\003\003\003\003\0038888888888888888888(((((((((((((((((((((((((((((((((((((((((((NONOONNNNNNQ6(\003888888\011\011\011\011\011\011\011\011\011\011888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((88NNNOONNNNONNNN68888\011\011\011\011\011\011\011\011\011\011\022\022\003\003\003\016(((((((88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888((((((((((((((((((((((((((((((((((((((((((((OOONNNNNNNNNO66\0038888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\011\011\011\011\011\011\011\011\011\011\022\022\022\022\022\022\022\022\022888888888888((((((((88(88((((((((8((8((((((((((((((((((((((((OOOOOO8OO88NNQ6(O(O6\003\003\003888888888\011\011\011\011\011\011\011\011\011\0118888888888888888888888888888888888888888888888888888888888888888888888((((((((88(((((((((((((((((((((((((((((((((((((((OOONNNN88NNOOOO6(\003(O888888888888888888888888888" | ||||
|     "(NNNNNNNNNN((((((((((((((((((((((((((((((((((((((((66NNNNO(NNNN\003\003\003\003\003\003\003\003688888888(NNNNNNOONNN((((((((((((((((((((((((((((((((((((((((((((((NNNNNNNNNNNNNO66\003\003\003(\003\003\003\003\0038888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888888\003\003\003\003\003\003\003\003\003\003888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((8(((((((((((((((((((((((((((((((((((((ONNNNNNN8NNNNNNO6(\003\003\003\003\0038888888888\011\011\011\011\011\011\011\011\011\011\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022888\003\003((((((((((((((((((((((((((((((88NNNNNNNNNNNNNNNNNNNNNN8ONNNNNNNONNONN8888888888888888888888888888888888888888888888888888888888888888888888888(((((((8((8((((((((((((((((((((((((((((((((((((((NNNNNN888N8NN8NNN6N66(N88888888\011\011\011\011\011\011\011\011\011\011888888((((((8((8((((((((((((((((((((((((((((((((OOOOO8NN8OONO6(8888888\011\011\011\011\011\011\011\011\011\01188888888888888888888888888888888888888888888888888888888888888888888888888888888888888" | ||||
|     "88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((NNOO\003\0038888888NN(O(((((((((((((8((((((((((((((((((((((((((((((((((OONNNNN888OONQ6\003\003\003\003\003\003\003\003\003\003\003\003\003\011\011\011\011\011\011\011\011\011\01188888888888888888888888888888888888888888888888888888888888888888888888888888888888888(888888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\016\016\016\016\016\016\016\016\004\004\004\004\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888888888888\003((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU8\003\003\003\003\00388888888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((" | ||||
|     "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((\003\0038888888888888((((((((((((((((((((((((((((((((((((((((((((((((\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\0216((((((66666666666666688888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888" | ||||
|     "(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888888(((((((((((((((((((((((((((((((8\011\011\011\011\011\011\011\011\011\0118888\003\003(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8\011\011\011\011\011\011\011\011\011\011888888((((((((((((((((((((((((((((((8866666\0038888888888((((((((((((((((((((((((((((((((((((((((((((((((6666666\003\003\003\003\003\016\016\016\0165555\003\0168888888888\011\011\011\011\011\011\011\011\011\0118\022\022\022\022\022\022\0228(((((((((((((((((((((88888(((((((((((((((((((88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\003\003\003\00388888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888" | ||||
|     "(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888N(OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO8888888NNNN5555555555555888888888888888888888888888888888888888888888888888888888888888855\0035688888888888OO88888888888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((888888888888888888888888888888888888888888(((((((((8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888" | ||||
|     "8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888885555855555558558(((((((((((((((((((((((((((((((((((888888888888888(88888888888888888888888888888(((88(88888888888888((((88888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888(((((((((((((888(((((((((8888888((((((((((88\0166N\003\021\021\021\02188888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888" | ||||
|     "66666666666666666666666666666666666666666666668866666666666666666666666888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888888888888888888888888888888888888888888888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016QQ666\016\016\016QQQQQQ\021\021\021\021\021\021\021\02166666666\016\0166666666\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0166666\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016666\01688888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022888888888888" | ||||
|     "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\0158\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\0278\027\02788\02788\027\02788\027\027\027\0278\027\027\027\027\027\027\027\027\015\015\015\0158\0158\015\015\015\015\015\015\0158\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\0278\027\027\027\02788\027\027\027\027\027\027\027\0278\027\027\027\027\027\027\0278\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\0278\027\027\027\0278\027\027\027\027\0278\027888\027\027\027\027\027\027\0278\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\01588\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\007\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\007\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\007\015\015\015\015" | ||||
|     "\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\007\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\007\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\007\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\007\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\007\015\015\015\015\015\015\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\027\007\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\007\015\015\015\015\015\015\027\01588\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\011\0116666666666666666666666666666666666666666666666666666666\016\016\016\01666666666666666666666666666666666666666666666666666\016\016\016\016\016\016\016\0166\016\016\016\016\016\016\016\016\016\016\016\016\016\0166\016\016\003\003\003\003\00388888888888888866666866666666666666688888888888888888888888888888888888888888888888888888888888888888888888888888888\015\015\015\015\015\015\015\015\015\015(\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015888888\015\015\015\015\015\015888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888NNNNNNN8NNNNNNNNNNNNNNNNN88NNNNNNN8NN8NNNNN8888844444444444444444444444444444444444444444444444444444444444444888888888888888888888888888888888N8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888" | ||||
|     "(((((((((((((((((((((((((((((((((((((((((((((8886666666555555588\011\011\011\011\011\011\011\011\011\0118888(\01688888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888((((((((((((((((((((((((((((((688888888888888888((((((((((((((((((((((((((((((((((((((((((((6666\011\011\011\011\011\011\011\011\011\01188888\0048888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((((((((((((((((((((((56666\011\011\011\011\011\011\011\011\011\01188888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888(((((((8((((8((8(((((((((((((((8" | ||||
|     "(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88\022\022\022\022\022\022\022\022\022666666688888888888888888888888888888888888888888\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\226\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015666N66658888\011\011\011\011\011\011\011\011\011\0118888\003\003888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\016\022\022\022\004\022\022\022\0228888888888888888888888888888888888888888888888888888888888888888888888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\022\016\022\022\022\022\022\022\022\022\022\022\022\022\022\022\02288888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888" | ||||
|     "((((8(((((((((((((((((((((((((((8((8(88(8((((((((((8((((8(8(888888(8888(8(8(8(((8((8(88(8(8(8(8(8((8(88((((8(((((((8((((8((((8(8((((((((((8(((((((((((((((((88888(((8(((((8(((((((((((((((((8888888888888888888888888888888888888888888888888888\007\00788888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888888888\022\022\022\022\022\022\022\022\022\022\022\022\022\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\016\016\016\016\016\016\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\016\016\016\016\016\016\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\227\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688888888888888888888888888888888888888888888888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888\016\016\016\016\016\016\016\016\0168888888\016\01688888888888888\016\016\016\016\016\0168888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888" | ||||
|     "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\013\013\013\013\013\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888\016\016\016\016\016\016\016\016\016\016\016\016\016888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888\016\016\016\016\016\016\016\016\016\016\016\0168888\016888888888888888\016\016\016\016\016\016\016\016\016\016\016\0168888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688888888\016\016\016\016\016\016\016\016\016\016888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\01688\016\016888888888888888888888888888888888888888888888888888888888888888888888888888888" | ||||
|     "\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016888888888888\016\016\016\016\016\016\016\016\016\016\016\016\016\01688\016\016\016\016\016\016\016\016\016\016\016\016\016888\016\016\016\016\016\016\016\016\0168888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168\016\016\016\016\016\016\01688888888\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888\016\016\016\016\016\016\016\016\0168888888\016\016\016\016\016\016\016\016\0168888888\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\016\0168888888888888888888888888888888888888\011\011\011\011\011\011\011\011\011\011888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888888888888888888888888888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((888888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((" | ||||
|     "((((((((((((((((((((((((((((((88((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888888888888(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((8888888888888888888888888888888((((((((((((((((((((((((((((((8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888" | ||||
|     "(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((88888((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((888888888888888888888888888888888888888888888888888888888888888888888888888888888\021888888888888888888888888888888\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021\021888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888886666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666668888888888888888" | ||||
|     "\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\221\22188"; | ||||
|  | ||||
| constexpr std::size_t property_index(char32_t code_point) | ||||
| { | ||||
|     LEXY_PRECONDITION(code_point <= 0x10FFFF); | ||||
|     auto high = (code_point >> 8) & 0xFFFF; | ||||
|     auto low  = code_point & 0xFF; | ||||
|  | ||||
|     auto block_start = static_cast<unsigned char>(block_starts[high]); | ||||
|     auto block_index = block_start * 256u + low; | ||||
|     return static_cast<unsigned char>(blocks[block_index]); | ||||
| } | ||||
|  | ||||
| constexpr lexy::code_point::general_category_t category[] = {lexy::code_point::Cc,lexy::code_point::Cc,lexy::code_point::Zs,lexy::code_point::Po,lexy::code_point::Sc,lexy::code_point::Ps,lexy::code_point::Pe,lexy::code_point::Sm,lexy::code_point::Pd,lexy::code_point::Nd,lexy::code_point::Lu,lexy::code_point::Sk,lexy::code_point::Pc,lexy::code_point::Ll,lexy::code_point::So,lexy::code_point::Lo,lexy::code_point::Pi,lexy::code_point::Cf,lexy::code_point::No,lexy::code_point::Ll,lexy::code_point::Po,lexy::code_point::Pf,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Ll,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lo,lexy::code_point::Lu,lexy::code_point::Lt,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lm,lexy::code_point::Lm,lexy::code_point::Mn,lexy::code_point::Mn,lexy::code_point::Cn,lexy::code_point::Lm,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Ll,lexy::code_point::Lu,lexy::code_point::Ll,lexy::code_point::Ll,lexy::code_point::Ll,lexy::code_point::Ll,lexy::code_point::Ll,lexy::code_point::Ll,lexy::code_point::Lu,lexy::code_point::Ll,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Me,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Mn,lexy::code_point::Mc,lexy::code_point::Lo,lexy::code_point::Mc,lexy::code_point::Lu,lexy::code_point::No,lexy::code_point::Ll,lexy::code_point::Nl,lexy::code_point::Mn,lexy::code_point::Ll,lexy::code_point::Ll,lexy::code_point::Ll,lexy::code_point::Ll,lexy::code_point::Ll,lexy::code_point::Ll,lexy::code_point::Ll,lexy::code_point::Ll,lexy::code_point::Lu,lexy::code_point::Ll,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lt,lexy::code_point::Lu,lexy::code_point::Lt,lexy::code_point::Ll,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Cf,lexy::code_point::Zl,lexy::code_point::Zp,lexy::code_point::Sm,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::So,lexy::code_point::Lu,lexy::code_point::Nl,lexy::code_point::Nl,lexy::code_point::So,lexy::code_point::So,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lm,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Ll,lexy::code_point::Cs,lexy::code_point::Co,lexy::code_point::Lo,lexy::code_point::Lm,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::Lu,lexy::code_point::So,}; | ||||
|  | ||||
| enum binary_properties_t | ||||
| { | ||||
|     whitespace, | ||||
|     join_control, | ||||
|     alphabetic, | ||||
|     uppercase, | ||||
|     lowercase, | ||||
|     xid_start, | ||||
|     xid_continue, | ||||
|     _property_count, | ||||
| }; | ||||
| static_assert(static_cast<int>(_property_count) <= 8); | ||||
|  | ||||
| constexpr std::uint_least8_t binary_properties[] = {0,1,1,0,0,0,0,0,0,64,108,0,64,116,0,116,0,0,0,116,64,0,108,108,108,116,108,108,108,108,108,108,108,108,108,108,108,108,108,108,100,108,100,108,108,108,108,108,108,108,108,108,116,100,64,84,0,20,108,108,108,108,108,116,108,116,116,116,116,116,116,108,116,108,108,0,108,108,68,68,68,64,108,64,116,100,100,116,116,116,116,116,116,116,116,108,116,108,108,100,108,100,116,108,108,108,108,108,2,1,1,96,108,108,108,96,108,108,116,12,20,108,108,108,108,108,108,108,108,4,108,108,108,108,108,108,108,108,108,108,108,108,108,116,0,0,4,68,108,108,108,12,}; | ||||
|  | ||||
| constexpr std::int_least32_t case_folding_offset[] = {0,0,0,0,0,0,0,0,0,0,32,0,0,0,0,0,0,0,0,775,0,0,1,0,-121,-268,210,206,205,79,202,203,207,211,209,213,214,218,217,219,0,2,1,-97,-56,-130,10795,-163,10792,-195,69,71,0,0,0,116,0,0,116,38,37,64,63,1,8,-30,-25,-15,-22,-54,-48,-60,-64,-7,80,0,15,48,0,0,0,0,7264,0,-8,0,0,-6222,-6221,-6212,-6210,-6211,-6204,-6180,35267,-3008,-58,-7615,-8,-8,-74,-9,-7173,-86,-100,-112,-128,-126,0,0,0,0,-7517,-8383,-8262,0,28,16,0,26,0,-10743,-3814,-10727,-10780,-10749,-10783,-10782,-10815,0,-35332,-42280,-42308,-42319,-42315,-42305,-42258,-42282,-42261,928,-48,-42307,-35384,-38864,0,0,0,0,40,39,34,0,}; | ||||
| } // namespace lexy::_unicode_db | ||||
							
								
								
									
										275
									
								
								dep/lexy/include/lexy/action/base.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										275
									
								
								dep/lexy/include/lexy/action/base.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,275 @@ | ||||
| // Copyright (C) 2020-2025 Jonathan Müller and lexy contributors | ||||
| // SPDX-License-Identifier: BSL-1.0 | ||||
|  | ||||
| #ifndef LEXY_ACTION_BASE_HPP_INCLUDED | ||||
| #define LEXY_ACTION_BASE_HPP_INCLUDED | ||||
|  | ||||
| #include <lexy/_detail/config.hpp> | ||||
| #include <lexy/_detail/lazy_init.hpp> | ||||
| #include <lexy/_detail/type_name.hpp> | ||||
| #include <lexy/callback/noop.hpp> | ||||
| #include <lexy/dsl/base.hpp> | ||||
| #include <lexy/grammar.hpp> | ||||
|  | ||||
| //=== parse_context ===// | ||||
| namespace lexy | ||||
| { | ||||
| namespace _detail | ||||
| { | ||||
|     struct parse_context_var_base | ||||
|     { | ||||
|         const void*             id; | ||||
|         parse_context_var_base* next; | ||||
|  | ||||
|         constexpr parse_context_var_base(const void* id) : id(id), next(nullptr) {} | ||||
|  | ||||
|         template <typename Context> | ||||
|         constexpr void link(Context& context) | ||||
|         { | ||||
|             auto cb  = context.control_block; | ||||
|             next     = cb->vars; | ||||
|             cb->vars = this; | ||||
|         } | ||||
|  | ||||
|         template <typename Context> | ||||
|         constexpr void unlink(Context& context) | ||||
|         { | ||||
|             auto cb  = context.control_block; | ||||
|             cb->vars = next; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     template <typename Id, typename T> | ||||
|     struct parse_context_var : parse_context_var_base | ||||
|     { | ||||
|         static constexpr auto type_id = lexy::_detail::type_id<Id>(); | ||||
|  | ||||
|         T value; | ||||
|  | ||||
|         explicit constexpr parse_context_var(T&& value) | ||||
|         : parse_context_var_base(static_cast<const void*>(&type_id) /* NOLINT */), | ||||
|           value(LEXY_MOV(value)) | ||||
|         {} | ||||
|  | ||||
|         template <typename ControlBlock> | ||||
|         static constexpr T& get(const ControlBlock* cb) | ||||
|         { | ||||
|             for (auto cur = cb->vars; cur; cur = cur->next) | ||||
|                 if (cur->id == static_cast<const void*>(&type_id) /* NOLINT */) | ||||
|                     return static_cast<parse_context_var*>(cur)->value; | ||||
|  | ||||
|             LEXY_ASSERT(false, "context variable hasn't been created"); | ||||
|             return static_cast<parse_context_var*>(cb->vars)->value; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     template <typename Handler, typename State = void> | ||||
|     struct parse_context_control_block | ||||
|     { | ||||
|         using handler_type = Handler; | ||||
|         using state_type   = State; | ||||
|  | ||||
|         LEXY_EMPTY_MEMBER Handler parse_handler; | ||||
|         State*                    parse_state; | ||||
|  | ||||
|         parse_context_var_base* vars; | ||||
|  | ||||
|         int  cur_depth, max_depth; | ||||
|         bool enable_whitespace_skipping; | ||||
|  | ||||
|         constexpr parse_context_control_block(Handler&& handler, State* state, | ||||
|                                               std::size_t max_depth) | ||||
|         : parse_handler(LEXY_MOV(handler)), parse_state(state), // | ||||
|           vars(nullptr),                                        // | ||||
|           cur_depth(0), max_depth(static_cast<int>(max_depth)), enable_whitespace_skipping(true) | ||||
|         {} | ||||
|  | ||||
|         template <typename OtherHandler> | ||||
|         constexpr parse_context_control_block(Handler&& handler, | ||||
|                                               parse_context_control_block<OtherHandler, State>* cb) | ||||
|         : parse_handler(LEXY_MOV(handler)), parse_state(cb->parse_state), // | ||||
|           vars(cb->vars), cur_depth(cb->cur_depth), max_depth(cb->max_depth), | ||||
|           enable_whitespace_skipping(cb->enable_whitespace_skipping) | ||||
|         {} | ||||
|  | ||||
|         template <typename OtherHandler> | ||||
|         constexpr void copy_vars_from(parse_context_control_block<OtherHandler, State>* cb) | ||||
|         { | ||||
|             vars                       = cb->vars; | ||||
|             cur_depth                  = cb->cur_depth; | ||||
|             max_depth                  = cb->max_depth; | ||||
|             enable_whitespace_skipping = cb->enable_whitespace_skipping; | ||||
|         } | ||||
|     }; | ||||
| } // namespace _detail | ||||
|  | ||||
| // If a production doesn't define whitespace, we don't need to pass it and can shorten the template | ||||
| // name. | ||||
| template <typename Production> | ||||
| using _whitespace_production_of | ||||
|     = std::conditional_t<_production_defines_whitespace<Production>, Production, void>; | ||||
|  | ||||
| template <typename Handler, typename State, typename Production> | ||||
| using _production_value_type = | ||||
|     typename Handler::template value_callback<Production, State>::return_type; | ||||
|  | ||||
| template <typename Handler, typename State, typename Production, | ||||
|           typename WhitespaceProduction = _whitespace_production_of<Production>> | ||||
| struct _pc | ||||
| { | ||||
|     using handler_type = Handler; | ||||
|     using state_type   = State; | ||||
|  | ||||
|     using production            = Production; | ||||
|     using whitespace_production = WhitespaceProduction; | ||||
|     using value_type            = _production_value_type<Handler, State, Production>; | ||||
|  | ||||
|     typename Handler::event_handler                       handler; | ||||
|     _detail::parse_context_control_block<Handler, State>* control_block; | ||||
|     _detail::lazy_init<value_type>                        value; | ||||
|  | ||||
|     constexpr explicit _pc(_detail::parse_context_control_block<Handler, State>* cb) | ||||
|     : handler(Production{}), control_block(cb) | ||||
|     {} | ||||
|  | ||||
|     template <typename ChildProduction> | ||||
|     constexpr auto sub_context(ChildProduction) | ||||
|     { | ||||
|         // Update the whitespace production if necessary. | ||||
|         // If the current production is a token or defines whitespace, | ||||
|         // we change it to the current production (or void), otherwise keep it. | ||||
|         using new_whitespace_production | ||||
|             = std::conditional_t<is_token_production<ChildProduction> // | ||||
|                                      || _production_defines_whitespace<ChildProduction>, | ||||
|                                  _whitespace_production_of<ChildProduction>, WhitespaceProduction>; | ||||
|         return _pc<Handler, State, ChildProduction, new_whitespace_production>(control_block); | ||||
|     } | ||||
|  | ||||
|     constexpr auto value_callback() | ||||
|     { | ||||
|         using callback = typename Handler::template value_callback<Production, State>; | ||||
|         return callback(control_block->parse_state); | ||||
|     } | ||||
|  | ||||
|     template <typename Event, typename... Args> | ||||
|     constexpr auto on(Event ev, Args&&... args) | ||||
|     { | ||||
|         return handler.on(control_block->parse_handler, ev, LEXY_FWD(args)...); | ||||
|     } | ||||
| }; | ||||
| } // namespace lexy | ||||
|  | ||||
| //=== do_action ===// | ||||
| namespace lexy::_detail | ||||
| { | ||||
| struct final_parser | ||||
| { | ||||
|     template <typename Context, typename Reader, typename... Args> | ||||
|     LEXY_PARSER_FUNC static bool parse(Context& context, Reader&, Args&&... args) | ||||
|     { | ||||
|         context.value.emplace_result(context.value_callback(), LEXY_FWD(args)...); | ||||
|         return true; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| template <typename NextParser> | ||||
| struct context_finish_parser | ||||
| { | ||||
|     template <typename Context, typename Reader, typename SubContext, typename... Args> | ||||
|     LEXY_PARSER_FUNC static bool parse(Context& context, Reader& reader, SubContext& sub_context, | ||||
|                                        Args&&... args) | ||||
|     { | ||||
|         // Might need to skip whitespace, according to the original context. | ||||
|         using continuation | ||||
|             = std::conditional_t<lexy::is_token_production<typename SubContext::production>, | ||||
|                                  lexy::whitespace_parser<Context, NextParser>, NextParser>; | ||||
|  | ||||
|         // Pass the produced value to the next parser. | ||||
|         if constexpr (std::is_void_v<typename SubContext::value_type>) | ||||
|             return continuation::parse(context, reader, LEXY_FWD(args)...); | ||||
|         else | ||||
|             return continuation::parse(context, reader, LEXY_FWD(args)..., | ||||
|                                        LEXY_MOV(*sub_context.value)); | ||||
|     } | ||||
| }; | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| namespace lexy | ||||
| { | ||||
| constexpr void* no_parse_state = nullptr; | ||||
|  | ||||
| template <typename Handler, typename State, typename Production, typename Reader> | ||||
| constexpr auto _do_action(_pc<Handler, State, Production>& context, Reader& reader) | ||||
| { | ||||
|     context.on(parse_events::grammar_start{}, reader.position()); | ||||
|     context.on(parse_events::production_start{}, reader.position()); | ||||
|  | ||||
|     // We parse whitespace, theen the rule, then finish. | ||||
|     using parser = lexy::whitespace_parser< | ||||
|         LEXY_DECAY_DECLTYPE(context), | ||||
|         lexy::parser_for<lexy::production_rule<Production>, _detail::final_parser>>; | ||||
|     auto rule_result = parser::parse(context, reader); | ||||
|  | ||||
|     if (rule_result) | ||||
|     { | ||||
|         context.on(parse_events::production_finish{}, reader.position()); | ||||
|         context.on(parse_events::grammar_finish{}, reader); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         context.on(parse_events::production_cancel{}, reader.position()); | ||||
|         context.on(parse_events::grammar_cancel{}, reader); | ||||
|     } | ||||
|  | ||||
|     return rule_result; | ||||
| } | ||||
|  | ||||
| template <typename Production, template <typename> typename Result, typename Handler, | ||||
|           typename State, typename Reader> | ||||
| constexpr auto do_action(Handler&& handler, State* state, Reader& reader) | ||||
| { | ||||
|     static_assert(!std::is_reference_v<Handler>, "need to move handler in"); | ||||
|  | ||||
|     _detail::parse_context_control_block control_block(LEXY_MOV(handler), state, | ||||
|                                                        max_recursion_depth<Production>()); | ||||
|     _pc<Handler, State, Production>      context(&control_block); | ||||
|  | ||||
|     auto rule_result = _do_action(context, reader); | ||||
|  | ||||
|     using value_type = typename decltype(context)::value_type; | ||||
|     if constexpr (std::is_void_v<value_type>) | ||||
|         return LEXY_MOV(control_block.parse_handler).template get_result<Result<void>>(rule_result); | ||||
|     else if (context.value) | ||||
|         return LEXY_MOV(control_block.parse_handler) | ||||
|             .template get_result<Result<value_type>>(rule_result, LEXY_MOV(*context.value)); | ||||
|     else | ||||
|         return LEXY_MOV(control_block.parse_handler) | ||||
|             .template get_result<Result<value_type>>(rule_result); | ||||
| } | ||||
| } // namespace lexy | ||||
|  | ||||
| //=== value callback ===// | ||||
| namespace lexy::_detail | ||||
| { | ||||
| struct void_value_callback | ||||
| { | ||||
|     constexpr void_value_callback() = default; | ||||
|     template <typename State> | ||||
|     constexpr explicit void_value_callback(State*) | ||||
|     {} | ||||
|  | ||||
|     using return_type = void; | ||||
|  | ||||
|     constexpr auto sink() const | ||||
|     { | ||||
|         return lexy::noop.sink(); | ||||
|     } | ||||
|  | ||||
|     template <typename... Args> | ||||
|     constexpr void operator()(Args&&...) const | ||||
|     {} | ||||
| }; | ||||
| } // namespace lexy::_detail | ||||
|  | ||||
| #endif // LEXY_ACTION_BASE_HPP_INCLUDED | ||||
|  | ||||
							
								
								
									
										90
									
								
								dep/lexy/include/lexy/action/match.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								dep/lexy/include/lexy/action/match.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | ||||
| // Copyright (C) 2020-2025 Jonathan Müller and lexy contributors | ||||
| // SPDX-License-Identifier: BSL-1.0 | ||||
|  | ||||
| #ifndef LEXY_ACTION_MATCH_HPP_INCLUDED | ||||
| #define LEXY_ACTION_MATCH_HPP_INCLUDED | ||||
|  | ||||
| #include <lexy/action/base.hpp> | ||||
|  | ||||
| namespace lexy | ||||
| { | ||||
| class _mh | ||||
| { | ||||
| public: | ||||
|     constexpr _mh() : _failed(false) {} | ||||
|  | ||||
|     class event_handler | ||||
|     { | ||||
|     public: | ||||
|         constexpr event_handler(production_info) {} | ||||
|  | ||||
|         template <typename Error> | ||||
|         constexpr void on(_mh& handler, parse_events::error, Error&&) | ||||
|         { | ||||
|             handler._failed = true; | ||||
|         } | ||||
|  | ||||
|         template <typename Event, typename... Args> | ||||
|         constexpr int on(_mh&, Event, const Args&...) | ||||
|         { | ||||
|             return 0; // operation_chain_start needs to return something | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     template <typename Production, typename State> | ||||
|     using value_callback = _detail::void_value_callback; | ||||
|  | ||||
|     template <typename> | ||||
|     constexpr bool get_result(bool rule_parse_result) && | ||||
|     { | ||||
|         return rule_parse_result && !_failed; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     bool _failed; | ||||
| }; | ||||
|  | ||||
| template <typename State, typename Input> | ||||
| struct match_action | ||||
| { | ||||
|     State* _state = nullptr; | ||||
|  | ||||
|     using handler = _mh; | ||||
|     using state   = State; | ||||
|     using input   = Input; | ||||
|  | ||||
|     template <typename> | ||||
|     using result_type = bool; | ||||
|  | ||||
|     constexpr match_action() = default; | ||||
|     template <typename U = State> | ||||
|     constexpr explicit match_action(U& state) : _state(&state) | ||||
|     {} | ||||
|  | ||||
|     template <typename Production> | ||||
|     constexpr auto operator()(Production, const Input& input) const | ||||
|     { | ||||
|         auto reader = input.reader(); | ||||
|         return lexy::do_action<Production, result_type>(handler(), _state, reader); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| template <typename Production, typename Input> | ||||
| constexpr bool match(const Input& input) | ||||
| { | ||||
|     return match_action<void, Input>()(Production{}, input); | ||||
| } | ||||
| template <typename Production, typename Input, typename State> | ||||
| constexpr bool match(const Input& input, State& state) | ||||
| { | ||||
|     return match_action<State, Input>(state)(Production{}, input); | ||||
| } | ||||
| template <typename Production, typename Input, typename State> | ||||
| constexpr bool match(const Input& input, const State& state) | ||||
| { | ||||
|     return match_action<const State, Input>(state)(Production{}, input); | ||||
| } | ||||
| } // namespace lexy | ||||
|  | ||||
| #endif // LEXY_ACTION_MATCH_HPP_INCLUDED | ||||
|  | ||||
							
								
								
									
										191
									
								
								dep/lexy/include/lexy/action/parse.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								dep/lexy/include/lexy/action/parse.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,191 @@ | ||||
| // Copyright (C) 2020-2025 Jonathan Müller and lexy contributors | ||||
| // SPDX-License-Identifier: BSL-1.0 | ||||
|  | ||||
| #ifndef LEXY_ACTION_PARSE_HPP_INCLUDED | ||||
| #define LEXY_ACTION_PARSE_HPP_INCLUDED | ||||
|  | ||||
| #include <lexy/_detail/invoke.hpp> | ||||
| #include <lexy/action/base.hpp> | ||||
| #include <lexy/action/validate.hpp> | ||||
| #include <lexy/callback/base.hpp> | ||||
| #include <lexy/callback/bind.hpp> | ||||
|  | ||||
| namespace lexy | ||||
| { | ||||
| template <typename T, typename ErrorCallback> | ||||
| class parse_result | ||||
| { | ||||
|     using _impl_t = lexy::validate_result<ErrorCallback>; | ||||
|  | ||||
| public: | ||||
|     using value_type     = T; | ||||
|     using error_callback = ErrorCallback; | ||||
|     using error_type     = typename _impl_t::error_type; | ||||
|  | ||||
|     //=== status ===// | ||||
|     constexpr explicit operator bool() const noexcept | ||||
|     { | ||||
|         return _impl.is_success(); | ||||
|     } | ||||
|  | ||||
|     constexpr bool is_success() const noexcept | ||||
|     { | ||||
|         return _impl.is_success(); | ||||
|     } | ||||
|     constexpr bool is_error() const noexcept | ||||
|     { | ||||
|         return _impl.is_error(); | ||||
|     } | ||||
|     constexpr bool is_recovered_error() const noexcept | ||||
|     { | ||||
|         return _impl.is_recovered_error(); | ||||
|     } | ||||
|     constexpr bool is_fatal_error() const noexcept | ||||
|     { | ||||
|         return _impl.is_fatal_error(); | ||||
|     } | ||||
|  | ||||
|     //=== value ===// | ||||
|     constexpr bool has_value() const noexcept | ||||
|     { | ||||
|         return static_cast<bool>(_value); | ||||
|     } | ||||
|  | ||||
|     constexpr decltype(auto) value() const& noexcept | ||||
|     { | ||||
|         return *_value; | ||||
|     } | ||||
|     constexpr decltype(auto) value() && noexcept | ||||
|     { | ||||
|         return LEXY_MOV(*_value); | ||||
|     } | ||||
|  | ||||
|     //=== error ===// | ||||
|     constexpr std::size_t error_count() const noexcept | ||||
|     { | ||||
|         return _impl.error_count(); | ||||
|     } | ||||
|  | ||||
|     constexpr const auto& errors() const& noexcept | ||||
|     { | ||||
|         return _impl.errors(); | ||||
|     } | ||||
|     constexpr auto&& errors() && noexcept | ||||
|     { | ||||
|         return LEXY_MOV(_impl).errors(); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     constexpr explicit parse_result(_impl_t&& impl) noexcept : _impl(LEXY_MOV(impl)), _value() {} | ||||
|     template <typename U> | ||||
|     constexpr explicit parse_result(_impl_t&& impl, U&& v) noexcept : _impl(LEXY_MOV(impl)) | ||||
|     { | ||||
|         LEXY_PRECONDITION(impl.is_success() || impl.is_recovered_error()); | ||||
|         _value.emplace(LEXY_FWD(v)); | ||||
|     } | ||||
|  | ||||
|     // In principle we could do a space optimization, as we can reconstruct the impl's status from | ||||
|     // the state of _value and error. Feel free to implement it. | ||||
|     _impl_t                     _impl; | ||||
|     lexy::_detail::lazy_init<T> _value; | ||||
|  | ||||
|     template <typename Reader> | ||||
|     friend class _ph; | ||||
| }; | ||||
| } // namespace lexy | ||||
|  | ||||
| namespace lexy | ||||
| { | ||||
| template <typename Reader> | ||||
| class _ph | ||||
| { | ||||
|     using iterator = typename Reader::iterator; | ||||
|  | ||||
| public: | ||||
|     template <typename Input, typename Sink> | ||||
|     constexpr explicit _ph(const _detail::any_holder<const Input*>& input, | ||||
|                            _detail::any_holder<Sink>&               sink) | ||||
|     : _validate(input, sink) | ||||
|     {} | ||||
|  | ||||
|     using event_handler = typename _vh<Reader>::event_handler; | ||||
|  | ||||
|     constexpr operator _vh<Reader>&() | ||||
|     { | ||||
|         return _validate; | ||||
|     } | ||||
|  | ||||
|     template <typename Production, typename State> | ||||
|     using value_callback = production_value_callback<Production, State>; | ||||
|  | ||||
|     template <typename Result, typename T> | ||||
|     constexpr auto get_result(bool rule_parse_result, T&& result) && | ||||
|     { | ||||
|         using validate_result = lexy::validate_result<typename Result::error_callback>; | ||||
|         return Result(LEXY_MOV(_validate).template get_result<validate_result>(rule_parse_result), | ||||
|                       LEXY_MOV(result)); | ||||
|     } | ||||
|     template <typename Result> | ||||
|     constexpr auto get_result(bool rule_parse_result) && | ||||
|     { | ||||
|         using validate_result = lexy::validate_result<typename Result::error_callback>; | ||||
|         return Result(LEXY_MOV(_validate).template get_result<validate_result>(rule_parse_result)); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     _vh<Reader> _validate; | ||||
| }; | ||||
|  | ||||
| template <typename State, typename Input, typename ErrorCallback> | ||||
| struct parse_action | ||||
| { | ||||
|     const ErrorCallback* _callback; | ||||
|     State*               _state = nullptr; | ||||
|  | ||||
|     using handler = _ph<lexy::input_reader<Input>>; | ||||
|     using state   = State; | ||||
|     using input   = Input; | ||||
|  | ||||
|     template <typename T> | ||||
|     using result_type = parse_result<T, ErrorCallback>; | ||||
|  | ||||
|     constexpr explicit parse_action(const ErrorCallback& callback) : _callback(&callback) {} | ||||
|     template <typename U = State> | ||||
|     constexpr explicit parse_action(U& state, const ErrorCallback& callback) | ||||
|     : _callback(&callback), _state(&state) | ||||
|     {} | ||||
|  | ||||
|     template <typename Production> | ||||
|     constexpr auto operator()(Production, const Input& input) const | ||||
|     { | ||||
|         _detail::any_holder input_holder(&input); | ||||
|         _detail::any_holder sink(_get_error_sink(*_callback)); | ||||
|         auto                reader = input.reader(); | ||||
|         return lexy::do_action<Production, result_type>(handler(input_holder, sink), _state, | ||||
|                                                         reader); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| /// Parses the production into a value, invoking the callback on error. | ||||
| template <typename Production, typename Input, typename ErrorCallback> | ||||
| constexpr auto parse(const Input& input, const ErrorCallback& callback) | ||||
| { | ||||
|     return parse_action<void, Input, ErrorCallback>(callback)(Production{}, input); | ||||
| } | ||||
|  | ||||
| /// Parses the production into a value, invoking the callback on error. | ||||
| /// All callbacks gain access to the specified parse state. | ||||
| template <typename Production, typename Input, typename State, typename ErrorCallback> | ||||
| constexpr auto parse(const Input& input, State& state, const ErrorCallback& callback) | ||||
| { | ||||
|     return parse_action<State, Input, ErrorCallback>(state, callback)(Production{}, input); | ||||
| } | ||||
| template <typename Production, typename Input, typename State, typename ErrorCallback> | ||||
| constexpr auto parse(const Input& input, const State& state, const ErrorCallback& callback) | ||||
| { | ||||
|     return parse_action<const State, Input, ErrorCallback>(state, callback)(Production{}, input); | ||||
| } | ||||
| } // namespace lexy | ||||
|  | ||||
| #endif // LEXY_ACTION_PARSE_HPP_INCLUDED | ||||
|  | ||||
							
								
								
									
										217
									
								
								dep/lexy/include/lexy/action/parse_as_tree.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								dep/lexy/include/lexy/action/parse_as_tree.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,217 @@ | ||||
| // Copyright (C) 2020-2025 Jonathan Müller and lexy contributors | ||||
| // SPDX-License-Identifier: BSL-1.0 | ||||
|  | ||||
| #ifndef LEXY_ACTION_PARSE_AS_TREE_HPP_INCLUDED | ||||
| #define LEXY_ACTION_PARSE_AS_TREE_HPP_INCLUDED | ||||
|  | ||||
| #include <lexy/action/base.hpp> | ||||
| #include <lexy/action/validate.hpp> | ||||
| #include <lexy/dsl/any.hpp> | ||||
| #include <lexy/parse_tree.hpp> | ||||
|  | ||||
| namespace lexy | ||||
| { | ||||
| template <typename Tree, typename Reader> | ||||
| class _pth | ||||
| { | ||||
| public: | ||||
|     template <typename Input, typename Sink> | ||||
|     explicit _pth(Tree& tree, const _detail::any_holder<const Input*>& input, | ||||
|                   _detail::any_holder<Sink>& sink) | ||||
|     : _tree(&tree), _depth(0), _validate(input, sink) | ||||
|     {} | ||||
|  | ||||
|     class event_handler | ||||
|     { | ||||
|         using iterator = typename Reader::iterator; | ||||
|  | ||||
|     public: | ||||
|         event_handler(production_info info) : _validate(info) {} | ||||
|  | ||||
|         void on(_pth& handler, parse_events::grammar_start, iterator) | ||||
|         { | ||||
|             LEXY_PRECONDITION(handler._depth == 0); | ||||
|  | ||||
|             handler._builder.emplace(LEXY_MOV(*handler._tree), _validate.get_info()); | ||||
|         } | ||||
|         void on(_pth& handler, parse_events::grammar_finish, Reader& reader) | ||||
|         { | ||||
|             LEXY_PRECONDITION(handler._depth == 0); | ||||
|  | ||||
|             auto begin = reader.position(); | ||||
|             lexy::try_match_token(dsl::any, reader); | ||||
|             auto end = reader.position(); | ||||
|  | ||||
|             *handler._tree = LEXY_MOV(*handler._builder).finish({begin, end}); | ||||
|         } | ||||
|         void on(_pth& handler, parse_events::grammar_cancel, Reader&) | ||||
|         { | ||||
|             LEXY_PRECONDITION(handler._depth == 0); | ||||
|  | ||||
|             handler._tree->clear(); | ||||
|         } | ||||
|  | ||||
|         void on(_pth& handler, parse_events::production_start ev, iterator pos) | ||||
|         { | ||||
|             if (handler._depth++ > 0) | ||||
|                 _marker = handler._builder->start_production(_validate.get_info()); | ||||
|  | ||||
|             _validate.on(handler._validate, ev, pos); | ||||
|         } | ||||
|  | ||||
|         void on(_pth& handler, parse_events::production_finish ev, iterator pos) | ||||
|         { | ||||
|             if (--handler._depth > 0) | ||||
|             { | ||||
|                 if (handler._builder->current_child_count() == 0) | ||||
|                     handler._builder->token(lexy::position_token_kind, _validate.production_begin(), | ||||
|                                             _validate.production_begin()); | ||||
|                 handler._builder->finish_production(LEXY_MOV(_marker)); | ||||
|             } | ||||
|  | ||||
|             _validate.on(handler._validate, ev, pos); | ||||
|         } | ||||
|  | ||||
|         void on(_pth& handler, parse_events::production_cancel ev, iterator pos) | ||||
|         { | ||||
|             if (--handler._depth > 0) | ||||
|             { | ||||
|                 // Cancelling the production removes all nodes from the tree. | ||||
|                 // To ensure that the parse tree remains lossless, we add everything consumed by it | ||||
|                 // as an error token. | ||||
|                 handler._builder->cancel_production(LEXY_MOV(_marker)); | ||||
|                 handler._builder->token(lexy::error_token_kind, _validate.production_begin(), pos); | ||||
|             } | ||||
|  | ||||
|             _validate.on(handler._validate, ev, pos); | ||||
|         } | ||||
|  | ||||
|         auto on(_pth& handler, lexy::parse_events::operation_chain_start, iterator) | ||||
|         { | ||||
|             // As we don't know the production yet (or whether it is actually an operation), | ||||
|             // we create a container node to decide later. | ||||
|             return handler._builder->start_container(); | ||||
|         } | ||||
|         template <typename Operation> | ||||
|         void on(_pth& handler, lexy::parse_events::operation_chain_op, Operation op, iterator) | ||||
|         { | ||||
|             // We set the production of the current container. | ||||
|             // This will do a "left rotation" on the parse tree, making a new container the parent. | ||||
|             handler._builder->set_container_production(op); | ||||
|         } | ||||
|         template <typename Marker> | ||||
|         void on(_pth& handler, lexy::parse_events::operation_chain_finish, Marker&& marker, | ||||
|                 iterator) | ||||
|         { | ||||
|             handler._builder->finish_container(LEXY_MOV(marker)); | ||||
|         } | ||||
|  | ||||
|         template <typename TokenKind> | ||||
|         void on(_pth& handler, parse_events::token, TokenKind kind, iterator begin, iterator end) | ||||
|         { | ||||
|             handler._builder->token(kind, begin, end); | ||||
|         } | ||||
|  | ||||
|         template <typename Error> | ||||
|         void on(_pth& handler, parse_events::error ev, Error&& error) | ||||
|         { | ||||
|             _validate.on(handler._validate, ev, LEXY_FWD(error)); | ||||
|         } | ||||
|  | ||||
|         template <typename Event, typename... Args> | ||||
|         auto on(_pth& handler, Event ev, Args&&... args) | ||||
|         { | ||||
|             return _validate.on(handler._validate, ev, LEXY_FWD(args)...); | ||||
|         } | ||||
|  | ||||
|     private: | ||||
|         typename Tree::builder::marker      _marker; | ||||
|         typename _vh<Reader>::event_handler _validate; | ||||
|     }; | ||||
|  | ||||
|     template <typename Production, typename State> | ||||
|     using value_callback = _detail::void_value_callback; | ||||
|  | ||||
|     template <typename T> | ||||
|     constexpr auto get_result(bool rule_parse_result) && | ||||
|     { | ||||
|         LEXY_PRECONDITION(_depth == 0); | ||||
|         return LEXY_MOV(_validate).template get_result<T>(rule_parse_result); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     lexy::_detail::lazy_init<typename Tree::builder> _builder; | ||||
|     Tree*                                            _tree; | ||||
|     int                                              _depth; | ||||
|  | ||||
|     _vh<Reader> _validate; | ||||
| }; | ||||
|  | ||||
| template <typename State, typename Input, typename ErrorCallback, typename TokenKind = void, | ||||
|           typename MemoryResource = void> | ||||
| struct parse_as_tree_action | ||||
| { | ||||
|     using tree_type = lexy::parse_tree_for<Input, TokenKind, MemoryResource>; | ||||
|  | ||||
|     tree_type*           _tree; | ||||
|     const ErrorCallback* _callback; | ||||
|     State*               _state = nullptr; | ||||
|  | ||||
|     using handler = _pth<tree_type, lexy::input_reader<Input>>; | ||||
|     using state   = State; | ||||
|     using input   = Input; | ||||
|  | ||||
|     template <typename> | ||||
|     using result_type = validate_result<ErrorCallback>; | ||||
|  | ||||
|     constexpr explicit parse_as_tree_action(tree_type& tree, const ErrorCallback& callback) | ||||
|     : _tree(&tree), _callback(&callback) | ||||
|     {} | ||||
|     template <typename U = State> | ||||
|     constexpr explicit parse_as_tree_action(U& state, tree_type& tree, | ||||
|                                             const ErrorCallback& callback) | ||||
|     : _tree(&tree), _callback(&callback), _state(&state) | ||||
|     {} | ||||
|  | ||||
|     template <typename Production> | ||||
|     constexpr auto operator()(Production, const Input& input) const | ||||
|     { | ||||
|         _detail::any_holder input_holder(&input); | ||||
|         _detail::any_holder sink(_get_error_sink(*_callback)); | ||||
|         auto                reader = input.reader(); | ||||
|         return lexy::do_action<Production, result_type>(handler(*_tree, input_holder, sink), _state, | ||||
|                                                         reader); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| template <typename Production, typename TokenKind, typename MemoryResource, typename Input, | ||||
|           typename ErrorCallback> | ||||
| auto parse_as_tree(parse_tree<lexy::input_reader<Input>, TokenKind, MemoryResource>& tree, | ||||
|                    const Input&                                                      input, | ||||
|                    const ErrorCallback& callback) -> validate_result<ErrorCallback> | ||||
| { | ||||
|     return parse_as_tree_action<void, Input, ErrorCallback, TokenKind, | ||||
|                                 MemoryResource>(tree, callback)(Production{}, input); | ||||
| } | ||||
| template <typename Production, typename TokenKind, typename MemoryResource, typename Input, | ||||
|           typename State, typename ErrorCallback> | ||||
| auto parse_as_tree(parse_tree<lexy::input_reader<Input>, TokenKind, MemoryResource>& tree, | ||||
|                    const Input& input, State& state, | ||||
|                    const ErrorCallback& callback) -> validate_result<ErrorCallback> | ||||
| { | ||||
|     return parse_as_tree_action<State, Input, ErrorCallback, TokenKind, | ||||
|                                 MemoryResource>(state, tree, callback)(Production{}, input); | ||||
| } | ||||
| template <typename Production, typename TokenKind, typename MemoryResource, typename Input, | ||||
|           typename State, typename ErrorCallback> | ||||
| auto parse_as_tree(parse_tree<lexy::input_reader<Input>, TokenKind, MemoryResource>& tree, | ||||
|                    const Input& input, const State& state, | ||||
|                    const ErrorCallback& callback) -> validate_result<ErrorCallback> | ||||
| { | ||||
|     return parse_as_tree_action<const State, Input, ErrorCallback, TokenKind, | ||||
|                                 MemoryResource>(state, tree, callback)(Production{}, input); | ||||
| } | ||||
| } // namespace lexy | ||||
|  | ||||
| #endif // LEXY_ACTION_PARSE_AS_TREE_HPP_INCLUDED | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user