Thanks for using Compiler Explorer
Sponsors
Jakt
C++
Ada
Algol68
Analysis
Android Java
Android Kotlin
Assembly
C
C3
Carbon
C with Coccinelle
C++ with Coccinelle
C++ (Circle)
CIRCT
Clean
CMake
CMakeScript
COBOL
C++ for OpenCL
MLIR
Cppx
Cppx-Blue
Cppx-Gold
Cpp2-cppfront
Crystal
C#
CUDA C++
D
Dart
Elixir
Erlang
Fortran
F#
GLSL
Go
Haskell
HLSL
Hook
Hylo
IL
ispc
Java
Julia
Kotlin
LLVM IR
LLVM MIR
Modula-2
Mojo
Nim
Numba
Nix
Objective-C
Objective-C++
OCaml
Odin
OpenCL C
Pascal
Pony
PTX
Python
Racket
Raku
Ruby
Rust
Sail
Snowball
Scala
Slang
Solidity
Spice
SPIR-V
Swift
LLVM TableGen
Toit
Triton
TypeScript Native
V
Vala
Visual Basic
Vyper
WASM
Zig
Javascript
GIMPLE
Ygen
sway
c++ source #1
Output
Compile to binary object
Link to binary
Execute the code
Intel asm syntax
Demangle identifiers
Verbose demangling
Filters
Unused labels
Library functions
Directives
Comments
Horizontal whitespace
Debug intrinsics
Compiler
6502-c++ 11.1.0
ARM GCC 10.2.0
ARM GCC 10.3.0
ARM GCC 10.4.0
ARM GCC 10.5.0
ARM GCC 11.1.0
ARM GCC 11.2.0
ARM GCC 11.3.0
ARM GCC 11.4.0
ARM GCC 12.1.0
ARM GCC 12.2.0
ARM GCC 12.3.0
ARM GCC 12.4.0
ARM GCC 12.5.0
ARM GCC 13.1.0
ARM GCC 13.2.0
ARM GCC 13.2.0 (unknown-eabi)
ARM GCC 13.3.0
ARM GCC 13.3.0 (unknown-eabi)
ARM GCC 13.4.0
ARM GCC 13.4.0 (unknown-eabi)
ARM GCC 14.1.0
ARM GCC 14.1.0 (unknown-eabi)
ARM GCC 14.2.0
ARM GCC 14.2.0 (unknown-eabi)
ARM GCC 14.3.0
ARM GCC 14.3.0 (unknown-eabi)
ARM GCC 15.1.0
ARM GCC 15.1.0 (unknown-eabi)
ARM GCC 15.2.0
ARM GCC 15.2.0 (unknown-eabi)
ARM GCC 4.5.4
ARM GCC 4.6.4
ARM GCC 5.4
ARM GCC 6.3.0
ARM GCC 6.4.0
ARM GCC 7.3.0
ARM GCC 7.5.0
ARM GCC 8.2.0
ARM GCC 8.5.0
ARM GCC 9.3.0
ARM GCC 9.4.0
ARM GCC 9.5.0
ARM GCC trunk
ARM gcc 10.2.1 (none)
ARM gcc 10.3.1 (2021.07 none)
ARM gcc 10.3.1 (2021.10 none)
ARM gcc 11.2.1 (none)
ARM gcc 5.4.1 (none)
ARM gcc 7.2.1 (none)
ARM gcc 8.2 (WinCE)
ARM gcc 8.3.1 (none)
ARM gcc 9.2.1 (none)
ARM msvc v19.0 (ex-WINE)
ARM msvc v19.10 (ex-WINE)
ARM msvc v19.14 (ex-WINE)
ARM64 Morello gcc 10.1 Alpha 2
ARM64 gcc 10.2
ARM64 gcc 10.3
ARM64 gcc 10.4
ARM64 gcc 10.5.0
ARM64 gcc 11.1
ARM64 gcc 11.2
ARM64 gcc 11.3
ARM64 gcc 11.4.0
ARM64 gcc 12.1
ARM64 gcc 12.2.0
ARM64 gcc 12.3.0
ARM64 gcc 12.4.0
ARM64 gcc 12.5.0
ARM64 gcc 13.1.0
ARM64 gcc 13.2.0
ARM64 gcc 13.3.0
ARM64 gcc 13.4.0
ARM64 gcc 14.1.0
ARM64 gcc 14.2.0
ARM64 gcc 14.3.0
ARM64 gcc 15.1.0
ARM64 gcc 15.2.0
ARM64 gcc 4.9.4
ARM64 gcc 5.4
ARM64 gcc 5.5.0
ARM64 gcc 6.3
ARM64 gcc 6.4
ARM64 gcc 7.3
ARM64 gcc 7.5
ARM64 gcc 8.2
ARM64 gcc 8.5
ARM64 gcc 9.3
ARM64 gcc 9.4
ARM64 gcc 9.5
ARM64 gcc trunk
ARM64 msvc v19.14 (ex-WINE)
AVR gcc 10.3.0
AVR gcc 11.1.0
AVR gcc 12.1.0
AVR gcc 12.2.0
AVR gcc 12.3.0
AVR gcc 12.4.0
AVR gcc 12.5.0
AVR gcc 13.1.0
AVR gcc 13.2.0
AVR gcc 13.3.0
AVR gcc 13.4.0
AVR gcc 14.1.0
AVR gcc 14.2.0
AVR gcc 14.3.0
AVR gcc 15.1.0
AVR gcc 15.2.0
AVR gcc 4.5.4
AVR gcc 4.6.4
AVR gcc 5.4.0
AVR gcc 9.2.0
AVR gcc 9.3.0
Arduino Mega (1.8.9)
Arduino Uno (1.8.9)
BPF clang (trunk)
BPF clang 13.0.0
BPF clang 14.0.0
BPF clang 15.0.0
BPF clang 16.0.0
BPF clang 17.0.1
BPF clang 18.1.0
BPF clang 19.1.0
BPF clang 20.1.0
BPF clang 21.1.0
EDG (experimental reflection)
EDG 6.5
EDG 6.5 (GNU mode gcc 13)
EDG 6.6
EDG 6.6 (GNU mode gcc 13)
EDG 6.7
EDG 6.7 (GNU mode gcc 14)
FRC 2019
FRC 2020
FRC 2023
HPPA gcc 14.2.0
HPPA gcc 14.3.0
HPPA gcc 15.1.0
HPPA gcc 15.2.0
KVX ACB 4.1.0 (GCC 7.5.0)
KVX ACB 4.1.0-cd1 (GCC 7.5.0)
KVX ACB 4.10.0 (GCC 10.3.1)
KVX ACB 4.11.1 (GCC 10.3.1)
KVX ACB 4.12.0 (GCC 11.3.0)
KVX ACB 4.2.0 (GCC 7.5.0)
KVX ACB 4.3.0 (GCC 7.5.0)
KVX ACB 4.4.0 (GCC 7.5.0)
KVX ACB 4.6.0 (GCC 9.4.1)
KVX ACB 4.8.0 (GCC 9.4.1)
KVX ACB 4.9.0 (GCC 9.4.1)
KVX ACB 5.0.0 (GCC 12.2.1)
KVX ACB 5.2.0 (GCC 13.2.1)
LoongArch64 clang (trunk)
LoongArch64 clang 17.0.1
LoongArch64 clang 18.1.0
LoongArch64 clang 19.1.0
LoongArch64 clang 20.1.0
LoongArch64 clang 21.1.0
M68K gcc 13.1.0
M68K gcc 13.2.0
M68K gcc 13.3.0
M68K gcc 13.4.0
M68K gcc 14.1.0
M68K gcc 14.2.0
M68K gcc 14.3.0
M68K gcc 15.1.0
M68K gcc 15.2.0
M68k clang (trunk)
MRISC32 gcc (trunk)
MSP430 gcc 4.5.3
MSP430 gcc 5.3.0
MSP430 gcc 6.2.1
MinGW clang 14.0.3
MinGW clang 14.0.6
MinGW clang 15.0.7
MinGW clang 16.0.0
MinGW clang 16.0.2
MinGW gcc 11.3.0
MinGW gcc 12.1.0
MinGW gcc 12.2.0
MinGW gcc 13.1.0
RISC-V (32-bits) gcc (trunk)
RISC-V (32-bits) gcc 10.2.0
RISC-V (32-bits) gcc 10.3.0
RISC-V (32-bits) gcc 11.2.0
RISC-V (32-bits) gcc 11.3.0
RISC-V (32-bits) gcc 11.4.0
RISC-V (32-bits) gcc 12.1.0
RISC-V (32-bits) gcc 12.2.0
RISC-V (32-bits) gcc 12.3.0
RISC-V (32-bits) gcc 12.4.0
RISC-V (32-bits) gcc 12.5.0
RISC-V (32-bits) gcc 13.1.0
RISC-V (32-bits) gcc 13.2.0
RISC-V (32-bits) gcc 13.3.0
RISC-V (32-bits) gcc 13.4.0
RISC-V (32-bits) gcc 14.1.0
RISC-V (32-bits) gcc 14.2.0
RISC-V (32-bits) gcc 14.3.0
RISC-V (32-bits) gcc 15.1.0
RISC-V (32-bits) gcc 15.2.0
RISC-V (32-bits) gcc 8.2.0
RISC-V (32-bits) gcc 8.5.0
RISC-V (32-bits) gcc 9.4.0
RISC-V (64-bits) gcc (trunk)
RISC-V (64-bits) gcc 10.2.0
RISC-V (64-bits) gcc 10.3.0
RISC-V (64-bits) gcc 11.2.0
RISC-V (64-bits) gcc 11.3.0
RISC-V (64-bits) gcc 11.4.0
RISC-V (64-bits) gcc 12.1.0
RISC-V (64-bits) gcc 12.2.0
RISC-V (64-bits) gcc 12.3.0
RISC-V (64-bits) gcc 12.4.0
RISC-V (64-bits) gcc 12.5.0
RISC-V (64-bits) gcc 13.1.0
RISC-V (64-bits) gcc 13.2.0
RISC-V (64-bits) gcc 13.3.0
RISC-V (64-bits) gcc 13.4.0
RISC-V (64-bits) gcc 14.1.0
RISC-V (64-bits) gcc 14.2.0
RISC-V (64-bits) gcc 14.3.0
RISC-V (64-bits) gcc 15.1.0
RISC-V (64-bits) gcc 15.2.0
RISC-V (64-bits) gcc 8.2.0
RISC-V (64-bits) gcc 8.5.0
RISC-V (64-bits) gcc 9.4.0
RISC-V rv32gc clang (trunk)
RISC-V rv32gc clang 10.0.0
RISC-V rv32gc clang 10.0.1
RISC-V rv32gc clang 11.0.0
RISC-V rv32gc clang 11.0.1
RISC-V rv32gc clang 12.0.0
RISC-V rv32gc clang 12.0.1
RISC-V rv32gc clang 13.0.0
RISC-V rv32gc clang 13.0.1
RISC-V rv32gc clang 14.0.0
RISC-V rv32gc clang 15.0.0
RISC-V rv32gc clang 16.0.0
RISC-V rv32gc clang 17.0.1
RISC-V rv32gc clang 18.1.0
RISC-V rv32gc clang 19.1.0
RISC-V rv32gc clang 20.1.0
RISC-V rv32gc clang 21.1.0
RISC-V rv32gc clang 9.0.0
RISC-V rv32gc clang 9.0.1
RISC-V rv64gc clang (trunk)
RISC-V rv64gc clang 10.0.0
RISC-V rv64gc clang 10.0.1
RISC-V rv64gc clang 11.0.0
RISC-V rv64gc clang 11.0.1
RISC-V rv64gc clang 12.0.0
RISC-V rv64gc clang 12.0.1
RISC-V rv64gc clang 13.0.0
RISC-V rv64gc clang 13.0.1
RISC-V rv64gc clang 14.0.0
RISC-V rv64gc clang 15.0.0
RISC-V rv64gc clang 16.0.0
RISC-V rv64gc clang 17.0.1
RISC-V rv64gc clang 18.1.0
RISC-V rv64gc clang 19.1.0
RISC-V rv64gc clang 20.1.0
RISC-V rv64gc clang 21.1.0
RISC-V rv64gc clang 9.0.0
RISC-V rv64gc clang 9.0.1
Raspbian Buster
Raspbian Stretch
SPARC LEON gcc 12.2.0
SPARC LEON gcc 12.3.0
SPARC LEON gcc 12.4.0
SPARC LEON gcc 12.5.0
SPARC LEON gcc 13.1.0
SPARC LEON gcc 13.2.0
SPARC LEON gcc 13.3.0
SPARC LEON gcc 13.4.0
SPARC LEON gcc 14.1.0
SPARC LEON gcc 14.2.0
SPARC LEON gcc 14.3.0
SPARC LEON gcc 15.1.0
SPARC LEON gcc 15.2.0
SPARC gcc 12.2.0
SPARC gcc 12.3.0
SPARC gcc 12.4.0
SPARC gcc 12.5.0
SPARC gcc 13.1.0
SPARC gcc 13.2.0
SPARC gcc 13.3.0
SPARC gcc 13.4.0
SPARC gcc 14.1.0
SPARC gcc 14.2.0
SPARC gcc 14.3.0
SPARC gcc 15.1.0
SPARC gcc 15.2.0
SPARC64 gcc 12.2.0
SPARC64 gcc 12.3.0
SPARC64 gcc 12.4.0
SPARC64 gcc 12.5.0
SPARC64 gcc 13.1.0
SPARC64 gcc 13.2.0
SPARC64 gcc 13.3.0
SPARC64 gcc 13.4.0
SPARC64 gcc 14.1.0
SPARC64 gcc 14.2.0
SPARC64 gcc 14.3.0
SPARC64 gcc 15.1.0
SPARC64 gcc 15.2.0
TI C6x gcc 12.2.0
TI C6x gcc 12.3.0
TI C6x gcc 12.4.0
TI C6x gcc 12.5.0
TI C6x gcc 13.1.0
TI C6x gcc 13.2.0
TI C6x gcc 13.3.0
TI C6x gcc 13.4.0
TI C6x gcc 14.1.0
TI C6x gcc 14.2.0
TI C6x gcc 14.3.0
TI C6x gcc 15.1.0
TI C6x gcc 15.2.0
TI CL430 21.6.1
Tricore gcc 11.3.0 (EEESlab)
VAX gcc NetBSDELF 10.4.0
VAX gcc NetBSDELF 10.5.0 (Nov 15 03:50:22 2023)
VAX gcc NetBSDELF 12.4.0 (Apr 16 05:27 2025)
WebAssembly clang (trunk)
Xtensa ESP32 gcc 11.2.0 (2022r1)
Xtensa ESP32 gcc 12.2.0 (20230208)
Xtensa ESP32 gcc 14.2.0 (20241119)
Xtensa ESP32 gcc 8.2.0 (2019r2)
Xtensa ESP32 gcc 8.2.0 (2020r1)
Xtensa ESP32 gcc 8.2.0 (2020r2)
Xtensa ESP32 gcc 8.4.0 (2020r3)
Xtensa ESP32 gcc 8.4.0 (2021r1)
Xtensa ESP32 gcc 8.4.0 (2021r2)
Xtensa ESP32-S2 gcc 11.2.0 (2022r1)
Xtensa ESP32-S2 gcc 12.2.0 (20230208)
Xtensa ESP32-S2 gcc 14.2.0 (20241119)
Xtensa ESP32-S2 gcc 8.2.0 (2019r2)
Xtensa ESP32-S2 gcc 8.2.0 (2020r1)
Xtensa ESP32-S2 gcc 8.2.0 (2020r2)
Xtensa ESP32-S2 gcc 8.4.0 (2020r3)
Xtensa ESP32-S2 gcc 8.4.0 (2021r1)
Xtensa ESP32-S2 gcc 8.4.0 (2021r2)
Xtensa ESP32-S3 gcc 11.2.0 (2022r1)
Xtensa ESP32-S3 gcc 12.2.0 (20230208)
Xtensa ESP32-S3 gcc 14.2.0 (20241119)
Xtensa ESP32-S3 gcc 8.4.0 (2020r3)
Xtensa ESP32-S3 gcc 8.4.0 (2021r1)
Xtensa ESP32-S3 gcc 8.4.0 (2021r2)
arm64 msvc v19.20 VS16.0
arm64 msvc v19.21 VS16.1
arm64 msvc v19.22 VS16.2
arm64 msvc v19.23 VS16.3
arm64 msvc v19.24 VS16.4
arm64 msvc v19.25 VS16.5
arm64 msvc v19.27 VS16.7
arm64 msvc v19.28 VS16.8
arm64 msvc v19.28 VS16.9
arm64 msvc v19.29 VS16.10
arm64 msvc v19.29 VS16.11
arm64 msvc v19.30 VS17.0
arm64 msvc v19.31 VS17.1
arm64 msvc v19.32 VS17.2
arm64 msvc v19.33 VS17.3
arm64 msvc v19.34 VS17.4
arm64 msvc v19.35 VS17.5
arm64 msvc v19.36 VS17.6
arm64 msvc v19.37 VS17.7
arm64 msvc v19.38 VS17.8
arm64 msvc v19.39 VS17.9
arm64 msvc v19.40 VS17.10
arm64 msvc v19.41 VS17.11
arm64 msvc v19.42 VS17.12
arm64 msvc v19.43 VS17.13
arm64 msvc v19.latest
armv7-a clang (trunk)
armv7-a clang 10.0.0
armv7-a clang 10.0.1
armv7-a clang 11.0.0
armv7-a clang 11.0.1
armv7-a clang 12.0.0
armv7-a clang 12.0.1
armv7-a clang 13.0.0
armv7-a clang 13.0.1
armv7-a clang 14.0.0
armv7-a clang 15.0.0
armv7-a clang 16.0.0
armv7-a clang 17.0.1
armv7-a clang 18.1.0
armv7-a clang 19.1.0
armv7-a clang 20.1.0
armv7-a clang 21.1.0
armv7-a clang 9.0.0
armv7-a clang 9.0.1
armv8-a clang (all architectural features, trunk)
armv8-a clang (trunk)
armv8-a clang 10.0.0
armv8-a clang 10.0.1
armv8-a clang 11.0.0
armv8-a clang 11.0.1
armv8-a clang 12.0.0
armv8-a clang 13.0.0
armv8-a clang 14.0.0
armv8-a clang 15.0.0
armv8-a clang 16.0.0
armv8-a clang 17.0.1
armv8-a clang 18.1.0
armv8-a clang 19.1.0
armv8-a clang 20.1.0
armv8-a clang 21.1.0
armv8-a clang 9.0.0
armv8-a clang 9.0.1
clad trunk (clang 21.1.0)
clad v1.10 (clang 20.1.0)
clad v1.8 (clang 18.1.0)
clad v1.9 (clang 19.1.0)
clad v2.00 (clang 20.1.0)
clang-cl 18.1.0
ellcc 0.1.33
ellcc 0.1.34
ellcc 2017-07-16
ez80-clang 15.0.0
ez80-clang 15.0.7
hexagon-clang 16.0.5
llvm-mos atari2600-3e
llvm-mos atari2600-4k
llvm-mos atari2600-common
llvm-mos atari5200-supercart
llvm-mos atari8-cart-megacart
llvm-mos atari8-cart-std
llvm-mos atari8-cart-xegs
llvm-mos atari8-common
llvm-mos atari8-dos
llvm-mos c128
llvm-mos c64
llvm-mos commodore
llvm-mos cpm65
llvm-mos cx16
llvm-mos dodo
llvm-mos eater
llvm-mos mega65
llvm-mos nes
llvm-mos nes-action53
llvm-mos nes-cnrom
llvm-mos nes-gtrom
llvm-mos nes-mmc1
llvm-mos nes-mmc3
llvm-mos nes-nrom
llvm-mos nes-unrom
llvm-mos nes-unrom-512
llvm-mos osi-c1p
llvm-mos pce
llvm-mos pce-cd
llvm-mos pce-common
llvm-mos pet
llvm-mos rp6502
llvm-mos rpc8e
llvm-mos supervision
llvm-mos vic20
loongarch64 gcc 12.2.0
loongarch64 gcc 12.3.0
loongarch64 gcc 12.4.0
loongarch64 gcc 12.5.0
loongarch64 gcc 13.1.0
loongarch64 gcc 13.2.0
loongarch64 gcc 13.3.0
loongarch64 gcc 13.4.0
loongarch64 gcc 14.1.0
loongarch64 gcc 14.2.0
loongarch64 gcc 14.3.0
loongarch64 gcc 15.1.0
loongarch64 gcc 15.2.0
mips clang 13.0.0
mips clang 14.0.0
mips clang 15.0.0
mips clang 16.0.0
mips clang 17.0.1
mips clang 18.1.0
mips clang 19.1.0
mips clang 20.1.0
mips clang 21.1.0
mips gcc 11.2.0
mips gcc 12.1.0
mips gcc 12.2.0
mips gcc 12.3.0
mips gcc 12.4.0
mips gcc 12.5.0
mips gcc 13.1.0
mips gcc 13.2.0
mips gcc 13.3.0
mips gcc 13.4.0
mips gcc 14.1.0
mips gcc 14.2.0
mips gcc 14.3.0
mips gcc 15.1.0
mips gcc 15.2.0
mips gcc 4.9.4
mips gcc 5.4
mips gcc 5.5.0
mips gcc 9.3.0 (codescape)
mips gcc 9.5.0
mips64 (el) gcc 12.1.0
mips64 (el) gcc 12.2.0
mips64 (el) gcc 12.3.0
mips64 (el) gcc 12.4.0
mips64 (el) gcc 12.5.0
mips64 (el) gcc 13.1.0
mips64 (el) gcc 13.2.0
mips64 (el) gcc 13.3.0
mips64 (el) gcc 13.4.0
mips64 (el) gcc 14.1.0
mips64 (el) gcc 14.2.0
mips64 (el) gcc 14.3.0
mips64 (el) gcc 15.1.0
mips64 (el) gcc 15.2.0
mips64 (el) gcc 4.9.4
mips64 (el) gcc 5.4.0
mips64 (el) gcc 5.5.0
mips64 (el) gcc 9.5.0
mips64 clang 13.0.0
mips64 clang 14.0.0
mips64 clang 15.0.0
mips64 clang 16.0.0
mips64 clang 17.0.1
mips64 clang 18.1.0
mips64 clang 19.1.0
mips64 clang 20.1.0
mips64 clang 21.1.0
mips64 gcc 11.2.0
mips64 gcc 12.1.0
mips64 gcc 12.2.0
mips64 gcc 12.3.0
mips64 gcc 12.4.0
mips64 gcc 12.5.0
mips64 gcc 13.1.0
mips64 gcc 13.2.0
mips64 gcc 13.3.0
mips64 gcc 13.4.0
mips64 gcc 14.1.0
mips64 gcc 14.2.0
mips64 gcc 14.3.0
mips64 gcc 15.1.0
mips64 gcc 15.2.0
mips64 gcc 4.9.4
mips64 gcc 5.4.0
mips64 gcc 5.5.0
mips64 gcc 9.5.0
mips64el clang 13.0.0
mips64el clang 14.0.0
mips64el clang 15.0.0
mips64el clang 16.0.0
mips64el clang 17.0.1
mips64el clang 18.1.0
mips64el clang 19.1.0
mips64el clang 20.1.0
mips64el clang 21.1.0
mipsel clang 13.0.0
mipsel clang 14.0.0
mipsel clang 15.0.0
mipsel clang 16.0.0
mipsel clang 17.0.1
mipsel clang 18.1.0
mipsel clang 19.1.0
mipsel clang 20.1.0
mipsel clang 21.1.0
mipsel gcc 12.1.0
mipsel gcc 12.2.0
mipsel gcc 12.3.0
mipsel gcc 12.4.0
mipsel gcc 12.5.0
mipsel gcc 13.1.0
mipsel gcc 13.2.0
mipsel gcc 13.3.0
mipsel gcc 13.4.0
mipsel gcc 14.1.0
mipsel gcc 14.2.0
mipsel gcc 14.3.0
mipsel gcc 15.1.0
mipsel gcc 15.2.0
mipsel gcc 4.9.4
mipsel gcc 5.4.0
mipsel gcc 5.5.0
mipsel gcc 9.5.0
nanoMIPS gcc 6.3.0 (mtk)
power gcc 11.2.0
power gcc 12.1.0
power gcc 12.2.0
power gcc 12.3.0
power gcc 12.4.0
power gcc 12.5.0
power gcc 13.1.0
power gcc 13.2.0
power gcc 13.3.0
power gcc 13.4.0
power gcc 14.1.0
power gcc 14.2.0
power gcc 14.3.0
power gcc 15.1.0
power gcc 15.2.0
power gcc 4.8.5
power64 AT12.0 (gcc8)
power64 AT13.0 (gcc9)
power64 gcc 11.2.0
power64 gcc 12.1.0
power64 gcc 12.2.0
power64 gcc 12.3.0
power64 gcc 12.4.0
power64 gcc 12.5.0
power64 gcc 13.1.0
power64 gcc 13.2.0
power64 gcc 13.3.0
power64 gcc 13.4.0
power64 gcc 14.1.0
power64 gcc 14.2.0
power64 gcc 14.3.0
power64 gcc 15.1.0
power64 gcc 15.2.0
power64 gcc trunk
power64le AT12.0 (gcc8)
power64le AT13.0 (gcc9)
power64le clang (trunk)
power64le gcc 11.2.0
power64le gcc 12.1.0
power64le gcc 12.2.0
power64le gcc 12.3.0
power64le gcc 12.4.0
power64le gcc 12.5.0
power64le gcc 13.1.0
power64le gcc 13.2.0
power64le gcc 13.3.0
power64le gcc 13.4.0
power64le gcc 14.1.0
power64le gcc 14.2.0
power64le gcc 14.3.0
power64le gcc 15.1.0
power64le gcc 15.2.0
power64le gcc 6.3.0
power64le gcc trunk
powerpc64 clang (trunk)
qnx 8.0.0
s390x gcc 11.2.0
s390x gcc 12.1.0
s390x gcc 12.2.0
s390x gcc 12.3.0
s390x gcc 12.4.0
s390x gcc 12.5.0
s390x gcc 13.1.0
s390x gcc 13.2.0
s390x gcc 13.3.0
s390x gcc 13.4.0
s390x gcc 14.1.0
s390x gcc 14.2.0
s390x gcc 14.3.0
s390x gcc 15.1.0
s390x gcc 15.2.0
sh gcc 12.2.0
sh gcc 12.3.0
sh gcc 12.4.0
sh gcc 12.5.0
sh gcc 13.1.0
sh gcc 13.2.0
sh gcc 13.3.0
sh gcc 13.4.0
sh gcc 14.1.0
sh gcc 14.2.0
sh gcc 14.3.0
sh gcc 15.1.0
sh gcc 15.2.0
sh gcc 4.9.4
sh gcc 9.5.0
vast (trunk)
x64 msvc v19.0 (ex-WINE)
x64 msvc v19.10 (ex-WINE)
x64 msvc v19.14 (ex-WINE)
x64 msvc v19.20 VS16.0
x64 msvc v19.21 VS16.1
x64 msvc v19.22 VS16.2
x64 msvc v19.23 VS16.3
x64 msvc v19.24 VS16.4
x64 msvc v19.25 VS16.5
x64 msvc v19.27 VS16.7
x64 msvc v19.28 VS16.8
x64 msvc v19.28 VS16.9
x64 msvc v19.29 VS16.10
x64 msvc v19.29 VS16.11
x64 msvc v19.30 VS17.0
x64 msvc v19.31 VS17.1
x64 msvc v19.32 VS17.2
x64 msvc v19.33 VS17.3
x64 msvc v19.34 VS17.4
x64 msvc v19.35 VS17.5
x64 msvc v19.36 VS17.6
x64 msvc v19.37 VS17.7
x64 msvc v19.38 VS17.8
x64 msvc v19.39 VS17.9
x64 msvc v19.40 VS17.10
x64 msvc v19.41 VS17.11
x64 msvc v19.42 VS17.12
x64 msvc v19.43 VS17.13
x64 msvc v19.latest
x86 djgpp 4.9.4
x86 djgpp 5.5.0
x86 djgpp 6.4.0
x86 djgpp 7.2.0
x86 msvc v19.0 (ex-WINE)
x86 msvc v19.10 (ex-WINE)
x86 msvc v19.14 (ex-WINE)
x86 msvc v19.20 VS16.0
x86 msvc v19.21 VS16.1
x86 msvc v19.22 VS16.2
x86 msvc v19.23 VS16.3
x86 msvc v19.24 VS16.4
x86 msvc v19.25 VS16.5
x86 msvc v19.27 VS16.7
x86 msvc v19.28 VS16.8
x86 msvc v19.28 VS16.9
x86 msvc v19.29 VS16.10
x86 msvc v19.29 VS16.11
x86 msvc v19.30 VS17.0
x86 msvc v19.31 VS17.1
x86 msvc v19.32 VS17.2
x86 msvc v19.33 VS17.3
x86 msvc v19.34 VS17.4
x86 msvc v19.35 VS17.5
x86 msvc v19.36 VS17.6
x86 msvc v19.37 VS17.7
x86 msvc v19.38 VS17.8
x86 msvc v19.39 VS17.9
x86 msvc v19.40 VS17.10
x86 msvc v19.41 VS17.11
x86 msvc v19.42 VS17.12
x86 msvc v19.43 VS17.13
x86 msvc v19.latest
x86 nvc++ 22.11
x86 nvc++ 22.7
x86 nvc++ 22.9
x86 nvc++ 23.1
x86 nvc++ 23.11
x86 nvc++ 23.3
x86 nvc++ 23.5
x86 nvc++ 23.7
x86 nvc++ 23.9
x86 nvc++ 24.1
x86 nvc++ 24.11
x86 nvc++ 24.3
x86 nvc++ 24.5
x86 nvc++ 24.7
x86 nvc++ 24.9
x86 nvc++ 25.1
x86 nvc++ 25.3
x86 nvc++ 25.5
x86 nvc++ 25.7
x86-64 Zapcc 190308
x86-64 clang (-fimplicit-constexpr)
x86-64 clang (Chris Bazley N3089)
x86-64 clang (EricWF contracts)
x86-64 clang (amd-staging)
x86-64 clang (assertions trunk)
x86-64 clang (clangir)
x86-64 clang (experimental -Wlifetime)
x86-64 clang (experimental P1061)
x86-64 clang (experimental P1144)
x86-64 clang (experimental P1221)
x86-64 clang (experimental P2998)
x86-64 clang (experimental P3068)
x86-64 clang (experimental P3309)
x86-64 clang (experimental P3367)
x86-64 clang (experimental P3372)
x86-64 clang (experimental P3385)
x86-64 clang (experimental P3776)
x86-64 clang (experimental metaprogramming - P2632)
x86-64 clang (old concepts branch)
x86-64 clang (p1974)
x86-64 clang (pattern matching - P2688)
x86-64 clang (reflection - C++26)
x86-64 clang (reflection - TS)
x86-64 clang (resugar)
x86-64 clang (string interpolation - P3412)
x86-64 clang (thephd.dev)
x86-64 clang (trunk)
x86-64 clang (variadic friends - P2893)
x86-64 clang (widberg)
x86-64 clang 10.0.0
x86-64 clang 10.0.0 (assertions)
x86-64 clang 10.0.1
x86-64 clang 11.0.0
x86-64 clang 11.0.0 (assertions)
x86-64 clang 11.0.1
x86-64 clang 12.0.0
x86-64 clang 12.0.0 (assertions)
x86-64 clang 12.0.1
x86-64 clang 13.0.0
x86-64 clang 13.0.0 (assertions)
x86-64 clang 13.0.1
x86-64 clang 14.0.0
x86-64 clang 14.0.0 (assertions)
x86-64 clang 15.0.0
x86-64 clang 15.0.0 (assertions)
x86-64 clang 16.0.0
x86-64 clang 16.0.0 (assertions)
x86-64 clang 17.0.1
x86-64 clang 17.0.1 (assertions)
x86-64 clang 18.1.0
x86-64 clang 18.1.0 (assertions)
x86-64 clang 19.1.0
x86-64 clang 19.1.0 (assertions)
x86-64 clang 2.6.0 (assertions)
x86-64 clang 2.7.0 (assertions)
x86-64 clang 2.8.0 (assertions)
x86-64 clang 2.9.0 (assertions)
x86-64 clang 20.1.0
x86-64 clang 20.1.0 (assertions)
x86-64 clang 21.1.0
x86-64 clang 21.1.0 (assertions)
x86-64 clang 3.0.0
x86-64 clang 3.0.0 (assertions)
x86-64 clang 3.1
x86-64 clang 3.1 (assertions)
x86-64 clang 3.2
x86-64 clang 3.2 (assertions)
x86-64 clang 3.3
x86-64 clang 3.3 (assertions)
x86-64 clang 3.4 (assertions)
x86-64 clang 3.4.1
x86-64 clang 3.5
x86-64 clang 3.5 (assertions)
x86-64 clang 3.5.1
x86-64 clang 3.5.2
x86-64 clang 3.6
x86-64 clang 3.6 (assertions)
x86-64 clang 3.7
x86-64 clang 3.7 (assertions)
x86-64 clang 3.7.1
x86-64 clang 3.8
x86-64 clang 3.8 (assertions)
x86-64 clang 3.8.1
x86-64 clang 3.9.0
x86-64 clang 3.9.0 (assertions)
x86-64 clang 3.9.1
x86-64 clang 4.0.0
x86-64 clang 4.0.0 (assertions)
x86-64 clang 4.0.1
x86-64 clang 5.0.0
x86-64 clang 5.0.0 (assertions)
x86-64 clang 5.0.1
x86-64 clang 5.0.2
x86-64 clang 6.0.0
x86-64 clang 6.0.0 (assertions)
x86-64 clang 6.0.1
x86-64 clang 7.0.0
x86-64 clang 7.0.0 (assertions)
x86-64 clang 7.0.1
x86-64 clang 7.1.0
x86-64 clang 8.0.0
x86-64 clang 8.0.0 (assertions)
x86-64 clang 8.0.1
x86-64 clang 9.0.0
x86-64 clang 9.0.0 (assertions)
x86-64 clang 9.0.1
x86-64 clang rocm-4.5.2
x86-64 clang rocm-5.0.2
x86-64 clang rocm-5.1.3
x86-64 clang rocm-5.2.3
x86-64 clang rocm-5.3.3
x86-64 clang rocm-5.7.0
x86-64 clang rocm-6.0.2
x86-64 clang rocm-6.1.2
x86-64 clang rocm-6.2.4
x86-64 clang rocm-6.3.3
x86-64 clang rocm-6.4.0
x86-64 gcc (P2034 lambdas)
x86-64 gcc (contract labels)
x86-64 gcc (contracts natural syntax)
x86-64 gcc (contracts)
x86-64 gcc (coroutines)
x86-64 gcc (modules)
x86-64 gcc (trunk)
x86-64 gcc 10.1
x86-64 gcc 10.2
x86-64 gcc 10.3
x86-64 gcc 10.3 (assertions)
x86-64 gcc 10.4
x86-64 gcc 10.4 (assertions)
x86-64 gcc 10.5
x86-64 gcc 10.5 (assertions)
x86-64 gcc 11.1
x86-64 gcc 11.1 (assertions)
x86-64 gcc 11.2
x86-64 gcc 11.2 (assertions)
x86-64 gcc 11.3
x86-64 gcc 11.3 (assertions)
x86-64 gcc 11.4
x86-64 gcc 11.4 (assertions)
x86-64 gcc 12.1
x86-64 gcc 12.1 (assertions)
x86-64 gcc 12.2
x86-64 gcc 12.2 (assertions)
x86-64 gcc 12.3
x86-64 gcc 12.3 (assertions)
x86-64 gcc 12.4
x86-64 gcc 12.4 (assertions)
x86-64 gcc 12.5
x86-64 gcc 12.5 (assertions)
x86-64 gcc 13.1
x86-64 gcc 13.1 (assertions)
x86-64 gcc 13.2
x86-64 gcc 13.2 (assertions)
x86-64 gcc 13.3
x86-64 gcc 13.3 (assertions)
x86-64 gcc 13.4
x86-64 gcc 13.4 (assertions)
x86-64 gcc 14.1
x86-64 gcc 14.1 (assertions)
x86-64 gcc 14.2
x86-64 gcc 14.2 (assertions)
x86-64 gcc 14.3
x86-64 gcc 14.3 (assertions)
x86-64 gcc 15.1
x86-64 gcc 15.1 (assertions)
x86-64 gcc 15.2
x86-64 gcc 15.2 (assertions)
x86-64 gcc 3.4.6
x86-64 gcc 4.0.4
x86-64 gcc 4.1.2
x86-64 gcc 4.4.7
x86-64 gcc 4.5.3
x86-64 gcc 4.6.4
x86-64 gcc 4.7.1
x86-64 gcc 4.7.2
x86-64 gcc 4.7.3
x86-64 gcc 4.7.4
x86-64 gcc 4.8.1
x86-64 gcc 4.8.2
x86-64 gcc 4.8.3
x86-64 gcc 4.8.4
x86-64 gcc 4.8.5
x86-64 gcc 4.9.0
x86-64 gcc 4.9.1
x86-64 gcc 4.9.2
x86-64 gcc 4.9.3
x86-64 gcc 4.9.4
x86-64 gcc 5.1
x86-64 gcc 5.2
x86-64 gcc 5.3
x86-64 gcc 5.4
x86-64 gcc 5.5
x86-64 gcc 6.1
x86-64 gcc 6.2
x86-64 gcc 6.3
x86-64 gcc 6.4
x86-64 gcc 6.5
x86-64 gcc 7.1
x86-64 gcc 7.2
x86-64 gcc 7.3
x86-64 gcc 7.4
x86-64 gcc 7.5
x86-64 gcc 8.1
x86-64 gcc 8.2
x86-64 gcc 8.3
x86-64 gcc 8.4
x86-64 gcc 8.5
x86-64 gcc 9.1
x86-64 gcc 9.2
x86-64 gcc 9.3
x86-64 gcc 9.4
x86-64 gcc 9.5
x86-64 icc 13.0.1
x86-64 icc 16.0.3
x86-64 icc 17.0.0
x86-64 icc 18.0.0
x86-64 icc 19.0.0
x86-64 icc 19.0.1
x86-64 icc 2021.1.2
x86-64 icc 2021.10.0
x86-64 icc 2021.2.0
x86-64 icc 2021.3.0
x86-64 icc 2021.4.0
x86-64 icc 2021.5.0
x86-64 icc 2021.6.0
x86-64 icc 2021.7.0
x86-64 icc 2021.7.1
x86-64 icc 2021.8.0
x86-64 icc 2021.9.0
x86-64 icx 2021.1.2
x86-64 icx 2021.2.0
x86-64 icx 2021.3.0
x86-64 icx 2021.4.0
x86-64 icx 2022.0.0
x86-64 icx 2022.1.0
x86-64 icx 2022.2.0
x86-64 icx 2022.2.1
x86-64 icx 2023.0.0
x86-64 icx 2023.1.0
x86-64 icx 2023.2.1
x86-64 icx 2024.0.0
x86-64 icx 2024.1.0
x86-64 icx 2024.2.0
x86-64 icx 2024.2.1
x86-64 icx 2025.0.0
x86-64 icx 2025.0.1
x86-64 icx 2025.0.3
x86-64 icx 2025.0.4
x86-64 icx 2025.1.0
x86-64 icx 2025.1.1
x86-64 icx 2025.2.0
x86-64 icx 2025.2.1
x86-64 icx 2025.2.1
z180-clang 15.0.0
z180-clang 15.0.7
z80-clang 15.0.0
z80-clang 15.0.7
zig c++ 0.10.0
zig c++ 0.11.0
zig c++ 0.12.0
zig c++ 0.12.1
zig c++ 0.13.0
zig c++ 0.14.0
zig c++ 0.14.1
zig c++ 0.15.1
zig c++ 0.6.0
zig c++ 0.7.0
zig c++ 0.7.1
zig c++ 0.8.0
zig c++ 0.9.0
zig c++ trunk
Options
Source code
#include <algorithm> #include <array> #include <initializer_list> #include <sstream> #include <string> #include <string_view> #include <tuple> #include <type_traits> #include <utility> /** * This represents a set of characters which can be queried * to find out if a character is in the set or not. */ template <std::size_t N = 1> class charset_t { static_assert( N > 0, "A charset with zero elements doesn't makes sense to construct."); template <typename Tpl, typename Callable, std::size_t... I> constexpr void do_this_for_that(std::index_sequence<I...>, Tpl const& tpl, Callable callback) noexcept { (callback(std::get<I>(tpl)), ...); } template <typename Tpl, typename Callable> constexpr void for_each_tuple(Tpl const& tpl, Callable callback) noexcept { constexpr auto tpl_size = std::tuple_size<Tpl>::value; do_this_for_that(std::make_index_sequence<tpl_size>(), tpl, callback); } public: std::array<char, N> chars; // Lifecycle management public: ~charset_t() noexcept = default; charset_t(const charset_t&) = default; charset_t(charset_t&&) noexcept = default; charset_t& operator=(const charset_t&) = default; charset_t& operator=(charset_t&&) noexcept = default; // Methods public: /** * This is the default constructor. */ constexpr charset_t() noexcept : chars{} {} /** * This constructs a character set that contains * just the given character. * * @param[in] c * This is the only character to put in the set. */ constexpr explicit charset_t(char c) noexcept : chars{c} {} template <typename... Char, typename = typename std::enable_if< (true && ... && std::is_same_v<Char, char>), void>::type> constexpr charset_t(Char... t) noexcept : chars{t...} {} /** * This constructs a character set that contains all the * characters in all the other given character sets. * * @param[in] characterSets * These are the character sets to include. */ template <std::size_t... NN> constexpr charset_t(const charset_t<NN>&... csets) noexcept : chars{} { // static_assert(((0 + ... + NN) > N), "Sum of csets is greater than // charset's container"); auto csets_tupled = std::make_tuple(csets...); for_each_tuple(csets_tupled, [&, i = 0u](auto const& t) mutable { for (auto const& c : t.chars) { if (!contains(c)) chars[i++] = c; } }); } constexpr explicit charset_t(decltype(chars) const& _chars) noexcept : chars(_chars) {} constexpr explicit charset_t(decltype(chars)&& _chars) noexcept : chars(std::move(_chars)) {} /** * This method checks to see if the given character * is in the character set. * * @param[in] c * This is the character to check. * * @return * An indication of whether or not the given character * is in the character set is returned. */ constexpr bool contains(char c) const noexcept { for (auto const& cc : chars) if (cc == c) return true; return false; } /** * @brief checks if all the chars in the _cs is in the chars list or not * @param _cs * @return */ constexpr bool contains(std::string_view _cs) const noexcept { for (auto const& c : _cs) if (!contains(c)) return false; return true; } constexpr auto size() const noexcept { return chars.size(); } constexpr std::string_view string_view() const noexcept { return std::string_view(chars.data(), N); } }; /** * Constructing a charset with chars * God C++ really needs C++20's concepts; WTF * @return charset_t */ template <typename... Char, typename = typename std::enable_if< (true && ... && std::is_same_v<Char, char>), void>::type> constexpr auto charset(Char... chars) noexcept { return charset_t<sizeof...(chars)>{chars...}; } template <std::size_t... N> constexpr auto charset(charset_t<N>... csets) noexcept { return charset_t<(0 + ... + N)>{csets...}; } template <std::size_t N, std::size_t... I> constexpr auto charset_impl(charset_t<N> const& cset, std::index_sequence<I...>) noexcept { return charset<N>({cset.chars[I]...}); // turning it into a sequence of chars } template <std::size_t N, typename Indeces = std::make_index_sequence<N>> constexpr auto charset(charset_t<N> const& cset) noexcept { return charset_impl(cset, Indeces{}); } template <std::size_t N, char... I> constexpr auto charset(char first, std::integer_sequence<char, I...>) noexcept { return charset_t<N>{static_cast<char>(first + I)...}; } /** * This constructs a character set that contains all the * characters between the given "first" and "last" * characters, inclusive. * * @param[in] first * This is the first of the range of characters * to put in the set. * * @param[in] last * This is the last of the range of characters * to put in the set. */ template <char First, char Last> constexpr auto charset() noexcept { constexpr auto the_size = static_cast<std::size_t>(Last) - static_cast<std::size_t>(First) + 1; return charset<the_size>(First, std::make_integer_sequence<char, the_size>{}); } constexpr auto LOWER_ALPHA = charset<'a', 'z'>(); constexpr auto UPPER_ALPHA = charset<'A', 'Z'>(); /** * This is the character set containing just the alphabetic characters * from the ASCII character set. */ constexpr auto ALPHA = charset(LOWER_ALPHA, UPPER_ALPHA); /** * This is the character set containing just numbers. */ constexpr auto DIGIT = charset<'0', '9'>(); /** * This is the character set containing just the characters allowed * in a hexadecimal digit. */ constexpr auto HEXDIG = charset(DIGIT, charset<'A', 'F'>(), charset<'a', 'f'>()); template <typename T, bool is_signed = true> constexpr inline T to(std::string_view const& str) noexcept { T ret = 0; // todo: minus is not used!! if constexpr (is_signed) { bool minus = false; for (auto const& i : str) { if (i == '-') { minus = true; continue; } ret *= 10; ret += static_cast<T>(i - '0'); } } else { for (auto const& i : str) { ret *= 10; ret += static_cast<T>(i - '0'); } } return ret; } constexpr auto to_uint(std::string_view const& str) noexcept { return to<unsigned int, true>(str); } constexpr auto to_uint8(std::string_view const& str) noexcept { return to<uint8_t, false>(str); } namespace is { /** * @brief check if the specified character is a whitespace * @param c the character to check * @return true if c is a whitespace */ [[nodiscard]] constexpr bool whitespace(char const& c) noexcept { return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\f' || c == '\v'; // TODO: consider using std::isspace } /** * @brief check if str is right trimmed * @param str * @return true if there's no whitespaces in the right side of input */ [[nodiscard]] constexpr bool rtrimmed(std::string_view const& str) noexcept { return !whitespace(*str.rbegin()); } /** * @brief check if str is left trimmed * @param str * @return true if there's no whitespaces in the left side of input */ [[nodiscard]] constexpr bool ltrimmed(std::string_view const& str) noexcept { return !whitespace(str[0]); } /** * @brief check if str is right and left trimmed * @param str * @return true if there's no whitespaces in the right and left side of * input */ [[nodiscard]] constexpr bool trimmed(std::string_view const& str) noexcept { return ltrimmed(str) && rtrimmed(str); } /** * @brief check if the specified character is a valid number or not * @param character * @return true if the specified input is an integer */ [[nodiscard]] constexpr bool digit(char const& c) noexcept { return c >= '0' && c <= '9'; } /** * @brief is all the characters in the specified string digits * @param str * @return true/false */ [[nodiscard]] constexpr bool digit(std::string_view const& str) noexcept { for (auto const& c : str) if (!digit(c)) return false; return !str.empty(); } /** * @brief is a number or a dot * @param c * @return */ [[nodiscard]] constexpr bool number(char const& c) noexcept { return digit(c) || c == '.'; } /** * @brief check if the specified string is a number (including floating * points) * @param str * @return true if the specified string is a number */ [[nodiscard]] constexpr bool number(std::string_view const& str) noexcept { bool is_first = true; for (auto const& c : str) { if (!digit(c)) { if (is_first && c == '.') { is_first = false; continue; } return false; } } return !str.empty(); } /** * @brief check if a character is lowercase * @param c * @return */ [[nodiscard]] constexpr bool lowercase(char const& c) noexcept { return c >= 'a' && c <= 'z'; } /** * @brief checks if a string is completely lowercase or not * @param str * @return */ [[nodiscard]] constexpr bool lowercase(std::string_view const& str) noexcept { for (auto const& c : str) if (!lowercase(c)) return false; return true; } /** * @brief checks if a character is uppercase or not * @param c * @return */ [[nodiscard]] constexpr bool uppercase(char const& c) noexcept { return c >= 'A' && c <= 'Z'; } /** * @brief checks if a string is uppercase or not * @param str * @return */ [[nodiscard]] constexpr bool uppercase(std::string_view const& str) noexcept { for (auto const& c : str) if (!uppercase(c)) return false; return true; } /** * @brief the same as digit function * @param str * @return true if the specified string is an integer */ [[nodiscard]] constexpr bool integer(char const& str) noexcept { return digit(str); } /** * @brief the same as digit function * @param str * @return true if the specified string is an integer */ [[nodiscard]] constexpr bool integer(std::string_view const& str) noexcept { return digit(str); } /** * Check if the specifed string is an integer and can be hold inside * uint8_t; which means it's between 0 and 255 * @param str * @return bool */ [[nodiscard]] constexpr bool uint8(std::string_view const& str) noexcept { return !str.empty() && str.size() <= 3 && digit(str) && to_uint(str) <= 255; } /** * Check if the char is a hexadecimal character * @param char * @return bool */ [[nodiscard]] constexpr bool hex(char const& t) noexcept { return (t >= '0' && t <= '9') || (t >= 'a' && t <= 'f') || (t >= 'A' && t <= 'F'); } /** * check if all of the characters in the string is a hexadecimal * character * @param str * @return bool */ [[nodiscard]] constexpr bool hex(std::string_view const& str) noexcept { for (auto const& c : str) if (!hex(c)) return false; return !str.empty(); } /** * Check if the specified Integer is an octet of a subnet mask * @tparam Integer * @param o * @return */ template <typename Integer> [[nodiscard]] constexpr bool subnet_octet(Integer o) noexcept { constexpr auto mask = static_cast<Integer>(1) << ((sizeof(Integer) * 8) - 1); while ((o & mask) == mask) o <<= 1; return o == 0; } /** * @brief checks if the specified str is an ipv4 * @param str * @return true if str is a valid ipv4 */ [[nodiscard]] constexpr bool ipv4(std::string_view str) noexcept { std::size_t next_dot = 0; for (uint8_t octet_index = 0; octet_index != 4; octet_index++) { next_dot = str.find('.'); auto octet_str = str.substr(0, next_dot); if (octet_str.size() > 3 || !is::digit(octet_str) || to_uint(octet_str) > 255) return false; str.remove_prefix(octet_str.size() + (octet_index != 3)); } return str.empty(); } /** * Check if the specified string is a valid ipv4 subnet mask or not * @param str * @return bool an indication weather or not the specified string is a * valid ipv4 subnet mask or not */ [[nodiscard]] constexpr bool subnet(std::string_view str) noexcept { std::size_t next_dot = 0; for (uint8_t octet_index = 0; octet_index != 4; octet_index++) { next_dot = str.find('.'); auto octet_str = str.substr(0, next_dot); if (octet_str.size() > 3 || !is::digit(octet_str)) { return false; } if (auto octet_int = to_uint(octet_str); octet_int > 255 || subnet_octet(octet_int)) return false; str.remove_prefix(octet_str.size() + (octet_index != 3)); } return str.empty(); } /** * Check if the specified input is a valid subnet ipv4 mask or not * @param octets * @return bool an indication weather or not the specified input is a * valid ipv4 subnet mask or not */ [[nodiscard]] constexpr bool subnet( std::array<uint8_t, 4> const& octets) noexcept { for (auto const& octet : octets) if (!subnet_octet(octet)) return false; return true; } /** * @brief this function template will check if the ipv4 with it's prefix * is valid or not. * @example 192.168.0.1/24, 192.168.0.1:24 */ template <std::size_t N> [[nodiscard]] constexpr bool ipv4_prefix( std::string_view const& str, charset_t<N> const& devider_chars) noexcept { if (auto found = std::find_if( std::rbegin(str), std::rend(str), [&](const auto& c) { return devider_chars.contains(c); }); found != std::rend(str)) { auto index = std::distance(std::begin(str), found.base()) - 1; if (!ipv4(str.substr(0, index))) return false; if (auto prefix = str.substr(index + 1); is::digit(prefix)) { auto _prefix = to_uint(prefix); return _prefix >= 0 && _prefix <= 32; } return false; } return false; } /** * Check if the specified string is a ipv4 plus prefix or not * @param str * @return */ [[nodiscard]] constexpr bool ipv4_prefix(std::string_view const& str) noexcept { return ipv4_prefix(str, charset_t<2>{':', '/'}); } /** * This function checks to make sure the given address * is a valid IPv6 address according to the rules in * RFC 3986 (https://tools.ietf.org/html/rfc3986). * * @param[in] address * This is the IPv6 address to validate. * * @return * An indication of whether or not the given address * is a valid IPv6 address is returned. */ [[nodiscard]] constexpr bool ipv6(std::string_view address) noexcept { bool encountered_double_colons = false; std::size_t index = 0; if (address.starts_with('[')) { if (address.ends_with(']')) { address.remove_suffix(1); address.remove_prefix(1); } else { return false; } } while (index < 8u && !address.empty()) { auto next_colon = address.find(':'); auto octet = address.substr(0, next_colon); if (octet.empty()) { // ip cannon have two double colon semantics (the first one // and the last one is ok) if (!address.empty() && encountered_double_colons) return false; if (index == 0) { if (!address.starts_with("::")) { return false; } address.remove_prefix(1); } encountered_double_colons = true; } else if (octet.size() > 4) { if (ipv4(octet)) { // ipv4 inside ipv6 address should be the last octet return octet.size() == address.size() && ((!encountered_double_colons && index == 8u) || encountered_double_colons); } else return false; } else if (!is::hex(octet)) { return false; } if (next_colon != std::string_view::npos) address.remove_prefix(next_colon + 1); else address.remove_prefix(octet.size()); index++; } return address.empty() && ((!encountered_double_colons && index == 8u) || encountered_double_colons); } template <std::size_t N = 1> [[nodiscard]] constexpr bool ipv6_prefix( std::string_view const& str, charset_t<N> const& devider_chars = charset_t<1>('/')) noexcept { if (auto found = std::find_if( std::rbegin(str), std::rend(str), [&](const auto& c) { return devider_chars.contains(c); }); found != std::rend(str)) { auto index = std::distance(std::begin(str), found.base()) - 1; if (auto prefix = str.substr(index + 1); is::digit(prefix)) { int _prefix = to_uint(prefix); if (!(_prefix >= 0 && _prefix <= 128)) return false; } else { return false; } if (ipv6(str.substr(0, index))) return true; } return false; } /** * @brief check if the specified string is an ipv4 or ipv6 * @param str * @return true if str is ipv4 or ipv6 * TODO: start supporting IPvF (IP version Future) */ [[nodiscard]] constexpr bool ip(std::string_view const& str) noexcept { return ipv4(str) || ipv6(str); } } // namespace is /** * @brief considers this ip as a subnet and converts it into a int * prefix */ constexpr uint8_t to_prefix(uint32_t octets) noexcept { uint8_t prefix = 0u; for (uint32_t mask = 0x80'00'00'00u; mask != 0u; mask >>= 1u) if ((octets & mask) == mask) prefix++; else return prefix; return prefix; } constexpr uint8_t to_prefix(std::array<uint8_t, 4> octets) noexcept { uint8_t prefix = 0u; for (auto const& octet : octets) for (uint8_t mask = 0b1000'0000; mask != 0u; mask >>= 1u) if ((octet & mask) == mask) prefix++; else return prefix; return prefix; } /** * Convert string to prefix * @param octets */ constexpr uint8_t to_prefix(std::string_view const& _data) noexcept { if (_data.size() > 15 || _data.size() < 7) { return 0u; } std::size_t first_dot = 0u; std::size_t len = _data.size(); while (_data[first_dot] != '.' && first_dot != len) first_dot++; auto octet_1 = _data.substr(0u, first_dot); if (first_dot == len || octet_1.empty() || octet_1.size() > 3 || !is::digit(octet_1) || (octet_1.starts_with('0') && octet_1 != "0")) { return 0u; } std::size_t second_dot = first_dot + 1; while (_data[second_dot] != '.' && second_dot != len) second_dot++; auto octet_2 = _data.substr(first_dot + 1u, second_dot - (first_dot + 1)); if (second_dot == len || octet_2.empty() || octet_2.size() > 3 || !is::digit(octet_2) || (octet_2.starts_with('0') && octet_2 != "0")) { return 0u; } std::size_t third_dot = second_dot + 1; while (_data[third_dot] != '.' && third_dot != len) third_dot++; auto octet_3 = _data.substr(second_dot + 1u, third_dot - (second_dot + 1)); if (first_dot == len || octet_3.empty() || octet_3.size() > 3 || !is::digit(octet_3) || (octet_3.starts_with('0') && octet_3 != "0")) { return 0u; // parsing failed. } auto octet_4 = _data.substr(third_dot + 1u); if (octet_4.empty() || octet_4.size() > 3 || !is::digit(octet_4) || (octet_4.starts_with('0') && octet_4 != "0")) { return 0u; } return to_prefix({to_uint8(octet_1), to_uint8(octet_2), to_uint8(octet_3), to_uint8(octet_4)}); } /** * Convert a prefix to a subnet * @param prefix * @return bool */ constexpr uint32_t to_subnet(uint8_t prefix) noexcept { return 0xFF'FF'FF'FFu << (32u - prefix); } /** * Convert a prefix to a subnet * @param prefix * @return bool */ constexpr std::array<uint8_t, 4> to_subnet_array(uint8_t prefix) noexcept { auto subnet = to_subnet(prefix); return {static_cast<uint8_t>(subnet >> 24u & 0xFFu), static_cast<uint8_t>(subnet >> 16u & 0xFFu), static_cast<uint8_t>(subnet >> 8u & 0xFFu), static_cast<uint8_t>(subnet & 0xFFu)}; } class ipv4 { private: mutable uint32_t data = 0u; // all bits are used // 255 means that the ip doesn't have a prefix // 254 means the ip is not valid // 253 means the prefix was not valid mutable uint8_t _prefix = 255u; constexpr void parse(std::string_view const& _data) const noexcept { if (_data.size() > 15 || _data.size() < 7) { _prefix = 254u; // the ip is not valid return; } std::size_t first_dot = 0u; std::size_t len = _data.size(); while (_data[first_dot] != '.' && first_dot != len) first_dot++; auto octet_1 = _data.substr(0u, first_dot); if (first_dot == len || octet_1.empty() || octet_1.size() > 3 || !is::digit(octet_1) || (octet_1.starts_with('0') && octet_1 != "0")) { _prefix = 254u; // the ip is not valid return; } std::size_t second_dot = first_dot + 1; while (_data[second_dot] != '.' && second_dot != len) second_dot++; auto octet_2 = _data.substr(first_dot + 1u, second_dot - (first_dot + 1)); if (second_dot == len || octet_2.empty() || octet_2.size() > 3 || !is::digit(octet_2) || (octet_2.starts_with('0') && octet_2 != "0")) { _prefix = 254u; // the ip is not valid return; } std::size_t third_dot = second_dot + 1; while (_data[third_dot] != '.' && third_dot != len) third_dot++; auto octet_3 = _data.substr(second_dot + 1u, third_dot - (second_dot + 1)); if (third_dot == len || octet_3.empty() || octet_3.size() > 3 || !is::digit(octet_3) || (octet_3.starts_with('0') && octet_3 != "0")) { _prefix = 254u; // the ip is not valid return; // parsing failed. } std::size_t slash = third_dot + 1; while (_data[slash] != '/' && slash != len) slash++; auto octet_4 = _data.substr(third_dot + 1u, slash - (third_dot + 1)); if (octet_4.empty() || octet_4.size() > 3 || !is::digit(octet_4) || (octet_4.starts_with('0') && octet_4 != "0")) { _prefix = 254u; // the ip is not valid return; } if (slash != len) { auto prefix_str = _data.substr(slash + 1); if (prefix_str.empty() || (prefix_str.starts_with('0') && prefix_str != "0") || !is::digit(prefix_str)) { _prefix = 254u; // the ip is not valid return; } auto __prefix = to_uint(prefix_str); if (__prefix > 32) { _prefix = 254; // the ip is not valid return; } _prefix = static_cast<uint8_t>(__prefix); } auto oc1 = to_uint(octet_1); auto oc2 = to_uint(octet_2); auto oc3 = to_uint(octet_3); auto oc4 = to_uint(octet_4); if (oc1 > 255 || oc2 > 255 || oc3 > 255 || oc4 > 255) { _prefix = 254u; // the ip is not valid return; } data = parse({static_cast<uint8_t>(oc1), static_cast<uint8_t>(oc2), static_cast<uint8_t>(oc3), static_cast<uint8_t>(oc4)}); if (_prefix == 254u) _prefix = 255u; // the ip is valid } constexpr uint32_t parse(std::array<uint8_t, 4u> const& ip) const noexcept { return static_cast<uint32_t>(ip[0] << 24u) | static_cast<uint32_t>(ip[1] << 16u) | static_cast<uint32_t>(ip[2] << 8u) | static_cast<uint32_t>(ip[3]); } public: constexpr ipv4(ipv4 const& ip) = default; constexpr ipv4(ipv4&& ip) = default; constexpr explicit ipv4(std::string_view const& ip) noexcept : _prefix(255) { parse(ip); } constexpr explicit ipv4(char const* const ip) noexcept : _prefix(255) { parse(ip); } constexpr ipv4(std::string_view const& ip, std::string_view const& subnet) noexcept : _prefix(is::subnet(subnet) ? to_prefix(subnet) : 253u) { parse(ip); } constexpr ipv4(std::string_view const& ip, std::array<uint8_t, 4> const& subnet) noexcept : _prefix(is::subnet(subnet) ? to_prefix(subnet) : 253u) { parse(ip); } constexpr ipv4(std::string_view const& ip, uint8_t __prefix) noexcept : _prefix(__prefix > 32 && __prefix != 255u ? 253u : __prefix) { parse(ip); } constexpr ipv4(uint8_t octet1, uint8_t octet2, uint8_t octet3, uint8_t octet4, uint8_t prefix = 255) noexcept : data(parse({octet1, octet2, octet3, octet4})), _prefix(prefix > 32 && prefix != 255u ? 253u : prefix) {} constexpr ipv4(uint8_t octet1, uint8_t octet2, uint8_t octet3, uint8_t octet4, std::string_view const& subnet) noexcept : data(parse({octet1, octet2, octet3, octet4})), _prefix(is::subnet(subnet) ? to_prefix(subnet) : 253u) {} constexpr explicit ipv4(uint32_t const& ip, uint8_t prefix = 255) noexcept : data(ip), _prefix(prefix > 32 && prefix != 255u ? 253u : prefix) {} constexpr explicit ipv4(uint32_t const& ip, std::string_view subnet) noexcept : data(ip), _prefix(is::subnet(subnet) ? to_prefix(subnet) : 253u) {} constexpr ipv4(std::array<uint8_t, 4> const& ip, uint8_t prefix = 255) noexcept : data(parse(ip)), _prefix(prefix > 32 && prefix != 255u ? 253u : prefix) {} constexpr ipv4(std::array<uint8_t, 4> const& ip, std::string_view const& subnet) noexcept : data(parse(ip)), _prefix(is::subnet(subnet) ? to_prefix(subnet) : 253u) {} constexpr ipv4(std::array<uint8_t, 4> const& ip, std::array<uint8_t, 4> const& subnet) noexcept : data(parse(ip)), _prefix(is::subnet(subnet) ? to_prefix(subnet) : 253u) {} explicit operator std::string() { return str(); } explicit operator const char*() { return str().c_str(); } explicit operator uint32_t() { return integer(); } ipv4& operator=(ipv4 const& ip) = default; ipv4& operator=(ipv4&& ip) = default; ipv4& operator=(std::string_view const& ip) noexcept { parse(ip); _prefix = 255u; return *this; } ipv4& operator=(uint32_t ip) noexcept { data = ip; _prefix = 255u; return *this; } constexpr bool operator==( std::array<uint8_t, 4> const& other) const noexcept { return data == parse(other); } constexpr bool operator!=( std::array<uint8_t, 4> const& other) const noexcept { return data != parse(other); } constexpr bool operator<(std::array<uint8_t, 4> const& other) const noexcept { return data < parse(other); } constexpr bool operator>(std::array<uint8_t, 4> const& other) const noexcept { return data > parse(other); } constexpr bool operator<=( std::array<uint8_t, 4> const& other) const noexcept { return data <= parse(other); } constexpr bool operator>=( std::array<uint8_t, 4> const& other) const noexcept { return data >= parse(other); } constexpr bool operator==(ipv4 const& other) const noexcept { return data == other.data && _prefix == other._prefix; } constexpr bool operator!=(ipv4 const& other) const noexcept { return !operator==(other); } constexpr bool operator<(ipv4 const& other) const noexcept { return data < other.data; } constexpr bool operator>(ipv4 const& other) const noexcept { return data > other.data; } constexpr bool operator>=(ipv4 const& other) const noexcept { return data >= other.data; } constexpr bool operator<=(ipv4 const& other) const noexcept { return data <= other.data; } constexpr bool operator!=(std::string_view ip) const noexcept { return operator!=(ipv4(ip)); } constexpr bool operator==(std::string_view ip) const noexcept { return operator==(ipv4(ip)); } constexpr bool operator<(std::string_view ip) const noexcept { return operator<(ipv4(ip)); } constexpr bool operator>(std::string_view ip) const noexcept { return operator>(ipv4(ip)); } constexpr bool operator<=(std::string_view ip) const noexcept { return operator<=(ipv4(ip)); } constexpr bool operator>=(std::string_view ip) const noexcept { return operator>=(ipv4(ip)); } constexpr bool operator==(uint32_t const& ip) const noexcept { return integer() == ip; } constexpr bool operator!=(uint32_t const& ip) const noexcept { return !operator==(ip); } constexpr bool operator<(uint32_t const& ip) const noexcept { return integer() < ip; } constexpr bool operator>(uint32_t const& ip) const noexcept { return integer() > ip; } constexpr bool operator<=(uint32_t const& ip) const noexcept { return integer() <= ip; } constexpr bool operator>=(uint32_t const& ip) const noexcept { return integer() >= ip; } friend std::ostream& operator<<(std::ostream& stream, ipv4 const& ip) { stream << ip.str(); return stream; } friend std::istream& operator>>(std::istream& stream, ipv4& ip) { std::string str; stream >> str; ip = str; return stream; } /** * @brief get string representation of the ip * @return */ [[nodiscard]] std::string str() const noexcept { auto _octets = octets(); std::ostringstream s; s << static_cast<unsigned int>(_octets[0]) << '.' << static_cast<unsigned int>(_octets[1]) << '.' << static_cast<unsigned int>(_octets[2]) << '.' << static_cast<unsigned int>(_octets[3]); return s.str(); } /** * @brief get the integer representation of the ip address * @return */ [[nodiscard]] constexpr uint32_t integer() const noexcept { return data; } /** * @brief get the 4 octets of the ip address * @return */ [[nodiscard]] constexpr std::array<uint8_t, 4u> octets() const noexcept { uint32_t _data = integer(); return std::array<uint8_t, 4u>({static_cast<uint8_t>(_data >> 24u), static_cast<uint8_t>(_data >> 16u & 0x0FFu), static_cast<uint8_t>(_data >> 8u & 0x0FFu), static_cast<uint8_t>(_data & 0x0FFu)}); } /** * @brief check if the ip is in the specified range or not * @param start * @param finish * @return */ [[nodiscard]] constexpr bool in_range(ipv4 const& start, ipv4 const& finish) const noexcept { return *this >= start && *this <= finish; } /** * Get the prefix you specified in the constructor * @return */ [[nodiscard]] constexpr auto prefix() const noexcept { return _prefix; } /** * Change the prefix of the ip * @param __prefix */ ipv4& prefix(uint8_t __prefix) noexcept { _prefix = __prefix > 32 && __prefix != 255u ? 253u : __prefix; return *this; } /** * Set prefix with a subnet string * @param _subnet */ ipv4& prefix(std::string_view const& _subnet) noexcept { return prefix(to_prefix(_subnet)); } /** * Set prefix with a subnet array * @param _subnet */ ipv4& prefix(std::array<uint8_t, 4> const& _subnet) noexcept { return prefix(to_prefix(_subnet)); } /** * Remove prefix from the ip address */ ipv4& clear_prefix() noexcept { return prefix(255u); } /** * Check if the ip contains a prefix or not * @return bool an indication on weather or not the ip contains a prefix * or not */ [[nodiscard]] constexpr bool has_prefix() const noexcept { return _prefix <= 32; } /** * Check if the specified subnet or prefix was valid or not * @return bool */ [[nodiscard]] constexpr bool has_valid_prefix() const noexcept { return _prefix != 253u; } /** * @brief checks if the ip in this class is in the specified subnet or * not regardless of the the prefix that is specified in the ctor * @param ip * @param prefix * @return bool */ [[nodiscard]] constexpr bool is_in_subnet(ipv4 const& ip) const noexcept { auto uint_val = integer(); auto uint_ip = ip.integer(); uint_val &= 0xFFFFFFFFu << (32u - ip.prefix()); uint_ip &= 0xFFFFFFFFu << (32u - ip.prefix()); return uint_val == uint_ip; } /** * @brief checks if the ip is in private range or not regardless of the * prefix * @return */ [[nodiscard]] constexpr bool is_private() const noexcept { constexpr ipv4 class_C(std::array<uint8_t, 4u>{192, 168, 0, 0}, 16); constexpr ipv4 class_B_start(std::array<uint8_t, 4u>{172, 16, 0, 0}); constexpr ipv4 class_B_finish(std::array<uint8_t, 4u>{172, 31, 255, 255}); constexpr ipv4 class_A(std::array<uint8_t, 4u>{10, 0, 0, 0}, 8); return is_in_subnet(class_C) || in_range(class_B_start, class_B_finish) || is_in_subnet(class_A); } /** * @brief checks if the ip address is in public range or not * @return */ [[nodiscard]] constexpr bool is_public() const noexcept { return !is_private(); } /** * @brief check if all the octets are zero or not * @return true if all the octets are zero */ [[nodiscard]] constexpr bool is_zero() const noexcept { return data == 0; } /** * Check if the ip you specified is valid or not (the ctor will not * throw an error if the specified string is not a valid ipv4 address) * @return bool */ [[nodiscard]] constexpr bool is_valid() const noexcept { return _prefix != 254u; } /** * Get the ip in reversed order * @return */ [[nodiscard]] constexpr ipv4 reversed() const noexcept { return std::array<uint8_t, 4>{static_cast<uint8_t>(data & 0xFFu), static_cast<uint8_t>(data >> 8u & 0xFFu), static_cast<uint8_t>(data >> 16u & 0xFFu), static_cast<uint8_t>(data >> 24u & 0xFFu)}; } /** * TODO: implement this thing * @brief get the geographical location of the ip address based on * predefined rules * @return coordinates or string location */ [[nodiscard]] std::string geographic_location() const noexcept; }; constexpr bool operator==(uint32_t const& one, ipv4 const& two) { return two == one; } constexpr bool operator!=(uint32_t const& one, ipv4 const& two) { return two == one; } constexpr bool operator<(uint32_t const& one, ipv4 const& two) { return one < two.integer(); } constexpr bool operator>(uint32_t const& one, ipv4 const& two) { return one > two.integer(); } constexpr bool operator<=(uint32_t const& one, ipv4 const& two) { return one <= two.integer(); } constexpr bool operator>=(uint32_t const& one, ipv4 const& two) { return one >= two.integer(); } constexpr bool operator==(std::string_view const& one, ipv4 const& two) { return two == one; } constexpr bool operator!=(std::string_view const& one, ipv4 const& two) { return two != one; } constexpr bool operator<(std::string_view const& one, ipv4 const& two) { return ipv4(one) < two; } constexpr bool operator>(std::string_view const& one, ipv4 const& two) { return ipv4(one) > two; } constexpr bool operator<=(std::string_view const& one, ipv4 const& two) { return ipv4(one) <= two; } constexpr bool operator>=(std::string_view const& one, ipv4 const& two) { return ipv4(one) >= two; } constexpr bool operator==(std::array<uint8_t, 4> const& one, ipv4 const& two) { return two == one; } constexpr bool operator!=(std::array<uint8_t, 4> const& one, ipv4 const& two) { return two != one; } constexpr bool operator<(std::array<uint8_t, 4> const& one, ipv4 const& two) { return ipv4(one) < two; } constexpr bool operator>(std::array<uint8_t, 4> const& one, ipv4 const& two) { return ipv4(one) > two; } constexpr bool operator<=(std::array<uint8_t, 4> const& one, ipv4 const& two) { return ipv4(one) <= two; } constexpr bool operator>=(std::array<uint8_t, 4> const& one, ipv4 const& two) { return ipv4(one) >= two; } class ipv6 { public: static constexpr auto IPV6_ADDR_SIZE = 16u; // Bytes using octets8_t = std::array<uint8_t, 16u>; using octets16_t = std::array<uint16_t, 8u>; using octets32_t = std::array<uint32_t, 4u>; using octets64_t = std::array<uint64_t, 2u>; using octets_t = octets8_t; /** * IPv6 Address Scopes */ enum class scope { node_local = 0u, // Node-Local scope interface_local = 1u, // Interface-Local scope link_local = 2u, // Link-Local scope realm_local = 3u, // Realm-Local scope admin_local = 4u, // Admin-Local scope site_local = 5u, // Site-Local scope org_local = 8u, // Organization-Local scope global = 14u, // Global scope }; private: static constexpr auto interface_identifier_offset = 8u; // Interface Identifier offset in bytes static constexpr auto interface_identifier_size = 8u; // Interface Identifier size in bytes // I didn't go with a union because in OpenThread project they did and // they had to deal with endianness of their data. I rather use shifts // and host's byte order instead of getting my hands dirty with host's // byte order. Network's byte order is big endian btw, but here we just // have to worry about the host's byte order because we are not sending // these data over the network. mutable octets_t data = {}; // filled with zeros // 255 means it's doesn't have prefix // 254 means the ip is not valid // 253 means the prefix is not valid (it's not being used for now) mutable uint8_t _prefix = 255u; /** * converts 16/32/64/... bit arrays to 8bit * @tparam OCTET * @param _octets * @return octets8_t so I could put it in the "data" */ template <typename OCTET> [[nodiscard]] static constexpr octets_t to_octets_t( OCTET const& _octets) noexcept { octets_t _data = {}; auto _octets_it = _octets.cbegin(); auto _data_it = _data.begin(); auto each_octet_size = _data.size() / _octets.size(); for (; _octets_it != _octets.cend(); ++_octets_it) { auto _octet = *_octets_it; for (std::size_t i = 0u; i < each_octet_size; i++) { _octet >>= i * 8u; _octet |= 0xFFu; *_data_it++ = static_cast<uint8_t>(*_octets_it); } } return _data; } /** * parses the string_view to the uint8 structure */ constexpr void parse(std::string_view ipv6_data) const noexcept { constexpr auto hexes = HEXDIG.string_view(); if (ipv6_data.empty()) { _prefix = 254u; return; } data = {}; // all zero auto it = data.begin(); auto double_colon_point = data.end(); do { if (it == data.cend() && !ipv6_data.starts_with('/')) { _prefix = 254u; // the ip has too many octets return; } auto colon = ipv6_data.find_first_not_of(hexes); if (colon == std::string_view::npos || ipv6_data[colon] == ':' || (colon != 0 && ipv6_data[colon] == '/')) { // it's an octet switch (colon == std::string_view::npos ? ipv6_data.size() : colon) { case 4: *(it++) = std::stoul(std::string(ipv6_data.substr(0, 2)), nullptr, 16); *(it++) = std::stoul(std::string(ipv6_data.substr(2, 2)), nullptr, 16); break; case 3: *(it++) = std::stoul(std::string(ipv6_data.substr(0, 1)), nullptr, 16); *(it++) = std::stoul(std::string(ipv6_data.substr(1, 2)), nullptr, 16); break; case 2: case 1: *((++it)++) = std::stoul(std::string(ipv6_data.substr(0, colon)), nullptr, 16); break; case 0: // we've reached the double colon rule if (double_colon_point != data.end()) { _prefix = 254u; // we can't have two double colons return; } else { double_colon_point = it; if (it == data.begin()) ipv6_data.remove_prefix(1); } break; default: _prefix = 254u; // the ip is invalid return; } if (ipv6_data[colon] == '/') colon--; } else if (ipv6_data[colon] == '.') { // we found an ipv4 address ipv4 ip(ipv6_data); if (!ip.is_valid()) { _prefix = 254u; // the ip is not valid return; } for (auto const& octet : ip.octets()) *(it++) = octet; break; } else if (ipv6_data[colon] == '/') { // we have a prefix auto prefix_str = ipv6_data.substr(colon + 1); if (!is::digit(prefix_str)) { _prefix = 253u; // the prefix is invalid break; // let's not go all crazy just yet } auto __prefix = std::stoul(std::string(prefix_str)); // if (prefix_str.starts_with('0') && __prefix != 0) { // // there can't be a leading zero in the prefix string // _prefix = 253u; // return; // } _prefix = __prefix > 128u ? 253u : static_cast<decltype(_prefix)>(__prefix); ipv6_data.remove_prefix(prefix_str.size() + 1); if (!ipv6_data.empty()) { _prefix = 254u; // there can't be more stuff in the ip // from now on. return; } break; // there's nothing in the loop for us anymore } else { _prefix = 254u; // the ip is not valid return; } if (colon != std::string_view::npos) ipv6_data.remove_prefix(colon + 1); else break; } while (!ipv6_data.empty()); if (double_colon_point != data.end() && it != data.end()) { // XXXX XXXX XXXX XXXX YYYY YYYY 0000 0000 // ^ ^ // double colon it // shift the values to the last std::rotate(double_colon_point, it, data.end()); } else if (it != data.end()) { _prefix = 254u; // the string does not contain the whole ip return; } } public: constexpr explicit ipv6(std::string_view const& str, uint8_t __prefix = 255u) noexcept : _prefix(__prefix > 128u && __prefix != 255u ? 253u : __prefix) { parse(str); } constexpr explicit ipv6(octets8_t const& _octets, uint8_t __prefix = 255u) noexcept : data(_octets), _prefix(__prefix > 128u && __prefix != 255u ? 253u : __prefix) {} constexpr explicit ipv6(octets16_t const& _octets, uint8_t __prefix = 255u) noexcept : data{to_octets_t(_octets)}, _prefix(__prefix > 128u && __prefix != 255u ? 253u : __prefix) {} constexpr explicit ipv6(octets32_t const& _octets, uint8_t __prefix = 255u) noexcept : data{to_octets_t(_octets)}, _prefix(__prefix > 128u && __prefix != 255u ? 253u : __prefix) {} constexpr explicit ipv6(octets64_t const& _octets, uint8_t __prefix = 255u) noexcept : data{to_octets_t(_octets)}, _prefix(__prefix > 128u && __prefix != 255u ? 253u : __prefix) {} constexpr ipv6(ipv6 const& ip) noexcept = default; constexpr ipv6(ipv6&& ip) noexcept = default; ipv6& operator=(ipv6 const& ip) noexcept = default; ipv6& operator=(std::string_view const& str) noexcept { parse(str); _prefix = 255u; return *this; } ipv6& operator=(octets8_t const& _octets) noexcept { data = _octets; _prefix = 255u; return *this; } ipv6& operator=(octets16_t const& _octets) noexcept { data = to_octets_t(_octets); _prefix = 255u; return *this; } ipv6& operator=(octets32_t const& _octets) noexcept { data = to_octets_t(_octets); _prefix = 255u; return *this; } ipv6& operator=(octets64_t const& _octets) noexcept { data = to_octets_t(_octets); _prefix = 255u; return *this; } bool operator==(ipv6 const& other) const noexcept { return data == other.data && _prefix == other._prefix; } bool operator!=(ipv6 const& other) const noexcept { return !operator==(other); } // TODO: add other operators explicit operator octets8_t() { return octets8(); } explicit operator octets16_t() { return octets16(); } explicit operator octets32_t() { return octets32(); } explicit operator octets64_t() { return octets64(); } explicit operator const char*() { return short_str().c_str(); } explicit operator std::string() { return short_str(); } /** * @brief get the octets in 8bit format * @return the octets in 8bit format */ [[nodiscard]] constexpr octets8_t octets8() const noexcept { return data; } /** * @brief get all the octets in 8bit format * @details same as octets8 method */ [[nodiscard]] constexpr octets_t octets() const noexcept { return octets8(); } /** * @brief return all the octets in 16bit format */ [[nodiscard]] constexpr octets16_t octets16() const noexcept { // IP: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX // 08: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 // 16: --0-- --1-- --2-- --3-- --4-- --5-- --6-- --7-- // 32: -----0----- -----1----- -----2----- -----3----- // 64: -----------0----------- -----------1----------- auto _octets = octets8(); octets16_t ndata = {}; constexpr std::size_t len = ndata.size(); using t = uint16_t; for (std::size_t i = 0; i < len; i++) { ndata[i] = (static_cast<t>(_octets[i * 2u + 0u]) << (16u - 8u * 1u)) | (static_cast<t>(_octets[i * 2u + 1u]) << (16u - 8u * 2u)); } return ndata; } /** * @brief return all octets in 32bit format */ [[nodiscard]] constexpr octets32_t octets32() const noexcept { // IP: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX // 08: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 // 16: --0-- --1-- --2-- --3-- --4-- --5-- --6-- --7-- // 32: -----0----- -----1----- -----2----- -----3----- // 64: -----------0----------- -----------1----------- auto _octets = octets8(); octets32_t ndata = {}; constexpr std::size_t len = ndata.size(); using t = uint32_t; for (std::size_t i = 0; i < len; i++) { ndata[i] = (static_cast<t>(_octets[i * 2u + 0u]) << (32u - 8u * 1u)) | (static_cast<t>(_octets[i * 2u + 1u]) << (32u - 8u * 2u)) | (static_cast<t>(_octets[i * 2u + 2u]) << (32u - 8u * 3u)) | (static_cast<t>(_octets[i * 2u + 3u]) << (32u - 8u * 4u)); } return ndata; } /** * @brief return all octets in 64bit format */ [[nodiscard]] constexpr octets64_t octets64() const noexcept { // IP: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX // 08: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 // 16: --0-- --1-- --2-- --3-- --4-- --5-- --6-- --7-- // 32: -----0----- -----1----- -----2----- -----3----- // 64: -----------0----------- -----------1----------- auto _octets = octets8(); octets64_t ndata = {}; constexpr std::size_t len = ndata.size(); using t = uint64_t; for (std::size_t i = 0; i < len; i++) { ndata[i] = (static_cast<t>(_octets[i * 2u + 0u]) << (64u - 8u * 1u)) | (static_cast<t>(_octets[i * 2u + 1u]) << (64u - 8u * 2u)) | (static_cast<t>(_octets[i * 2u + 2u]) << (64u - 8u * 3u)) | (static_cast<t>(_octets[i * 2u + 3u]) << (64u - 8u * 4u)) | (static_cast<t>(_octets[i * 2u + 4u]) << (64u - 8u * 5u)) | (static_cast<t>(_octets[i * 2u + 5u]) << (64u - 8u * 6u)) | (static_cast<t>(_octets[i * 2u + 6u]) << (64u - 8u * 7u)) | (static_cast<t>(_octets[i * 2u + 7u]) << (64u - 8u * 8u)); } return ndata; } /** * This method returns the IPv6 address scope. * @returns The IPv6 address scope. */ [[nodiscard]] constexpr uint8_t scope() const noexcept { if (is_multicast()) { return octets8()[1] & 0xfu; } else if (is_link_local()) { return static_cast<uint8_t>(scope::link_local); } else if (is_loopback()) { return static_cast<uint8_t>(scope::node_local); } return static_cast<uint8_t>(scope::global); } /** * This method indicates whether or not the IPv6 address is the * Unspecified Address. * Unspecified IPv6 Address == ::0 * * @retval TRUE If the IPv6 address is the Unspecified Address. * @retval FALSE If the IPv6 address is not the Unspecified Address. * */ [[nodiscard]] constexpr bool is_unspecified() const noexcept { auto _octets = octets8(); return (_octets[0] == 0) && (_octets[1] == 0) && (_octets[2] == 0) && (_octets[3] == 0) && (_octets[4] == 0) && (_octets[5] == 0) && (_octets[6] == 0) && (_octets[7] == 0) && (_octets[8] == 0) && (_octets[9] == 0) && (_octets[10] == 0) && (_octets[11] == 0) && (_octets[12] == 0) && (_octets[13] == 0) && (_octets[14] == 0) && (_octets[15] == 0); } /** * This method indicates whether or not the IPv6 address is the Loopback * Address. * * @retval TRUE If the IPv6 address is the Loopback Address. * @retval FALSE If the IPv6 address is not the Loopback Address. * */ [[nodiscard]] constexpr bool is_loopback() const noexcept { auto _octets = octets8(); return (_octets[0] == 0) && (_octets[1] == 0) && (_octets[2] == 0) && (_octets[3] == 0) && (_octets[4] == 0) && (_octets[5] == 0) && (_octets[6] == 0) && (_octets[7] == 0) && (_octets[8] == 0) && (_octets[9] == 0) && (_octets[10] == 0) && (_octets[11] == 0) && (_octets[12] == 0) && (_octets[13] == 0) && (_octets[14] == 0) && (_octets[15] == 1); } /** * This method indicates whether or not the IPv6 address scope is * Interface-Local. * * @retval TRUE If the IPv6 address scope is Interface-Local. * @retval FALSE If the IPv6 address scope is not Interface-Local. * */ [[nodiscard]] constexpr bool is_link_local() const noexcept { auto _octets = octets8(); return (_octets[0] == 0xfeu) && ((_octets[1] & 0xc0u) == 0x80u); } /** * This method indicates whether or not the IPv6 address is multicast * address. * * @retval TRUE If the IPv6 address is a multicast address. * @retval FALSE If the IPv6 address scope is not a multicast address. * */ [[nodiscard]] constexpr bool is_multicast() const noexcept { auto _octets = octets8(); return _octets[0] == 0xffu; } /** * Determine whether the address is a global multicast address * @return bool */ [[nodiscard]] constexpr bool is_multicast_global() const noexcept { auto _octets = octets8(); return ((_octets[0] == 0xffu) && ((_octets[1] & 0x0fu) == 0x0eu)); } /** * Determine whether the address is a link-local multicast address * @return bool */ [[nodiscard]] constexpr bool is_multicast_link_local() const noexcept { auto _octets = octets8(); return ((_octets[0] == 0xffu) && ((_octets[1] & 0x0fu) == 0x02u)); } /** * Determine whether the address is a node-local multicast address * @return bool */ [[nodiscard]] constexpr bool is_multicast_node_local() const noexcept { auto _octets = octets8(); return ((_octets[0] == 0xffu) && ((_octets[1] & 0x0fu) == 0x01u)); } /** * Determine whether the address is a org-local multicast address * @return bool */ [[nodiscard]] constexpr bool is_multicast_org_local() const noexcept { auto _octets = octets8(); return ((_octets[0] == 0xffu) && ((_octets[1] & 0x0fu) == 0x08u)); } /** * Determine whether the address is a site-local multicast address * @return bool */ [[nodiscard]] constexpr bool is_multicast_site_local() const noexcept { auto _octets = octets8(); return ((_octets[0] == 0xffu) && ((_octets[1] & 0x0fu) == 0x05u)); } /** * This method indicates whether or not the IPv6 address scope is * Interface-Local. * * @retval TRUE If the IPv6 address scope is Interface-Local. * @retval FALSE If the IPv6 address scope is not Interface-Local. * */ [[nodiscard]] constexpr bool IsInterfaceLocal() const noexcept; /** * Determine whether the address is site local * @return bool */ [[nodiscard]] constexpr bool is_site_local() const noexcept { auto _octets = octets8(); return (_octets[0] == 0xfeu) && ((_octets[1] & 0xc0u) == 0xc0u); } /** * Determine whether the address is a mapped IPv4 address * @return bool */ [[nodiscard]] constexpr bool is_v4_mapped() const noexcept { auto _octets = octets8(); return (_octets[0] == 0) && (_octets[1] == 0) && (_octets[2] == 0) && (_octets[3] == 0) && (_octets[4] == 0) && (_octets[5] == 0) && (_octets[6] == 0) && (_octets[7] == 0) && (_octets[8] == 0) && (_octets[9] == 0) && (_octets[10] == 0xff) && (_octets[11] == 0xff); } /** * This method indicates whether or not the IPv6 address is a link-local * multicast address. * * @retval TRUE If the IPv6 address is a link-local multicast address. * @retval FALSE If the IPv6 address scope is not a link-local * multicast address. * */ [[nodiscard]] constexpr bool is_link_local_multicast() const noexcept { return is_multicast() && scope() == static_cast<uint8_t>(scope::link_local); } /** * This method indicates whether or not the IPv6 address is a link-local * all nodes multicast address. * * @retval TRUE If the IPv6 address is a link-local all nodes * multicast address. * @retval FALSE If the IPv6 address is not a link-local all nodes * multicast address. * */ [[nodiscard]] constexpr bool is_link_local_all_nodes_multicast() const noexcept { auto _octets = octets8(); return _octets[0] == 0xFFu && _octets[1] == 0x02u && (_octets[2] == 0) && (_octets[3] == 0) && (_octets[4] == 0) && (_octets[5] == 0) && (_octets[6] == 0) && (_octets[7] == 0) && (_octets[8] == 0) && (_octets[9] == 0) && (_octets[10] == 0) && (_octets[11] == 0) && (_octets[12] == 0) && (_octets[13] == 0) && (_octets[14] == 0) && (_octets[15] == 0x01u); } /** * This method indicates whether or not the IPv6 address is a link-local * all routers multicast address. * * @retval TRUE If the IPv6 address is a link-local all routers * multicast address. * @retval FALSE If the IPv6 address is not a link-local all routers * multicast address. * */ [[nodiscard]] constexpr bool is_link_local_all_routers_multicast() const noexcept { auto _octets = octets(); return _octets[0] == 0xFFu && _octets[1] == 0x02u && (_octets[2] == 0) && (_octets[3] == 0) && (_octets[4] == 0) && (_octets[5] == 0) && (_octets[6] == 0) && (_octets[7] == 0) && (_octets[8] == 0) && (_octets[9] == 0) && (_octets[10] == 0) && (_octets[11] == 0) && (_octets[12] == 0) && (_octets[13] == 0) && (_octets[14] == 0) && (_octets[15] == 0x02u); } /** * This method indicates whether or not the IPv6 address is a * realm-local multicast address. * * @retval TRUE If the IPv6 address is a realm-local multicast * address. * @retval FALSE If the IPv6 address scope is not a realm-local * multicast address. * */ [[nodiscard]] constexpr bool is_realm_local_multicast() const noexcept { return is_multicast() && (scope() == static_cast<uint8_t>(scope::realm_local)); } /** * This method indicates whether or not the IPv6 address is a * realm-local all nodes multicast address. * * @retval TRUE If the IPv6 address is a realm-local all nodes * multicast address. * @retval FALSE If the IPv6 address is not a realm-local all nodes * multicast address. * */ [[nodiscard]] constexpr bool is_realm_local_all_nodes_multicast() const noexcept { return is_multicast() && scope() == static_cast<uint8_t>(scope::realm_local); } /** * This method indicates whether or not the IPv6 address is a * realm-local all routers multicast address. * * @retval TRUE If the IPv6 address is a realm-local all routers * multicast address. * @retval FALSE If the IPv6 address is not a realm-local all routers * multicast address. * */ [[nodiscard]] constexpr bool is_realm_local_all_routers_multicast() const noexcept { auto _octets = octets(); return _octets[0] == 0xFFu && _octets[1] == 0x03u && (_octets[2] == 0) && (_octets[3] == 0) && (_octets[4] == 0) && (_octets[5] == 0) && (_octets[6] == 0) && (_octets[7] == 0) && (_octets[8] == 0) && (_octets[9] == 0) && (_octets[10] == 0) && (_octets[11] == 0) && (_octets[12] == 0) && (_octets[13] == 0) && (_octets[14] == 0) && (_octets[15] == 0x02u); } /** * This method indicates whether or not the IPv6 address is a * realm-local all MPL forwarders address. * * @retval TRUE If the IPv6 address is a realm-local all MPL * forwarders address. * @retval FALSE If the IPv6 address is not a realm-local all MPL * forwarders address. * */ [[nodiscard]] constexpr bool is_realm_local_all_mpl_forwarders() const noexcept { auto _octets = octets8(); return _octets[0] == 0xFFu && _octets[1] == 0x03u && (_octets[2] == 0) && (_octets[3] == 0) && (_octets[4] == 0) && (_octets[5] == 0) && (_octets[6] == 0) && (_octets[7] == 0) && (_octets[8] == 0) && (_octets[9] == 0) && (_octets[10] == 0) && (_octets[11] == 0) && (_octets[12] == 0) && (_octets[13] == 0) && (_octets[14] == 0) && (_octets[15] == 0xfcu); } /** * This method indicates whether or not the IPv6 address is multicast * larger than realm local. * * @retval TRUE If the IPv6 address is multicast larger than realm * local. * @retval FALSE If the IPv6 address is not multicast or the scope is * not larger than realm local. * */ [[nodiscard]] constexpr bool is_multicast_larger_than_realm_local() const noexcept { return is_multicast() && scope() > static_cast<uint8_t>(scope::realm_local); } /** * This method indicates whether or not the IPv6 address is a RLOC * address. * * @retval TRUE If the IPv6 address is a RLOC address. * @retval FALSE If the IPv6 address is not a RLOC address. * */ [[nodiscard]] constexpr bool is_routing_locator() const noexcept { constexpr auto aloc_16_mask = 0xFCu; // The mask for Aloc16 constexpr auto rloc16_reserved_bit_mask = 0x02u; // The mask for the reserved bit of Rloc16 auto _octets = octets(); // XX XX XX XX XX XX XX XX 00 00 00 FF FE 00 YY YY // 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 // --0-- --1-- --2-- --3-- --4-- --5-- --6-- --7-- return _octets[8] == 0 && _octets[9] == 0 && _octets[10] == 0 && _octets[11] == 0xFF && _octets[12] == 0xFE && _octets[13] == 0 && (_octets[14] < aloc_16_mask) && ((_octets[14] & rloc16_reserved_bit_mask) == 0); } /** * This method indicates whether or not the IPv6 address is an Anycast * RLOC address. * * @retval TRUE If the IPv6 address is an Anycast RLOC address. * @retval FALSE If the IPv6 address is not an Anycast RLOC address. * */ [[nodiscard]] constexpr bool is_anycast_routing_locator() const noexcept { constexpr auto aloc_16_mask = 0xFC; // The mask for Aloc16 auto _octets = octets(); // XX XX XX XX XX XX XX XX 00 00 00 FF FE 00 FC XX // 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 // --0-- --1-- --2-- --3-- --4-- --5-- --6-- --7-- return _octets[8] == 0 && _octets[9] == 0 && _octets[10] == 0 && _octets[11] == 0xFF && _octets[12] == 0xFE && _octets[13] == 0 && _octets[14] == aloc_16_mask; } /** * This method indicates whether or not the IPv6 address is an Anycast * Service Locator. * * @retval TRUE If the IPv6 address is an Anycast Service Locator. * @retval FALSE If the IPv6 address is not an Anycast Service Locator. * */ [[nodiscard]] constexpr bool is_anycast_service_locator() const noexcept { constexpr auto aloc8_service_start = 0x10; constexpr auto aloc8_service_end = 0x2f; auto _octets = octets(); return is_anycast_routing_locator() && (_octets[IPV6_ADDR_SIZE - 2] == 0xfc) && (_octets[IPV6_ADDR_SIZE - 1] >= aloc8_service_start) && (_octets[IPV6_ADDR_SIZE - 1] <= aloc8_service_end); } /** * This method indicates whether or not the IPv6 address is * Subnet-Router Anycast (RFC 4291), * * @retval TRUE If the IPv6 address is a Subnet-Router Anycast * address. * @retval FALSE If the IPv6 address is not a Subnet-Router Anycast * address. * */ [[nodiscard]] constexpr bool is_subnet_router_anycast() const noexcept { // IP: XX XX XX XX XX XX XX XX 00 00 00 00 00 00 00 00 // 08: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 // 16: --0-- --1-- --2-- --3-- --4-- --5-- --6-- --7-- // 32: -----0----- -----1----- -----2----- -----3----- // 64: -----------0----------- -----------1----------- auto _octets = octets(); return (_octets[8] == 0) && (_octets[9] == 0) && (_octets[10] == 0) && (_octets[11] == 0) && (_octets[12] == 0) && (_octets[13] == 0) && (_octets[14] == 0) && (_octets[15] == 0); } /** * This method indicates whether or not the IPv6 address is Reserved * Subnet Anycast (RFC 2526), * * @retval TRUE If the IPv6 address is a Reserved Subnet Anycast * address. * @retval FALSE If the IPv6 address is not a Reserved Subnet Anycast * address. * */ [[nodiscard]] constexpr bool is_reserved_subnet_anycast() const noexcept { // IP: XX XX XX XX XX XX XX XX FD FF FF FF FF FF FF 80 // 08: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 // 16: --0-- --1-- --2-- --3-- --4-- --5-- --6-- --7-- // 32: -----0----- -----1----- -----2----- -----3----- // 64: -----------0----------- -----------1----------- auto _octets = octets8(); return _octets[8] == 0xFD && _octets[15] == 0x80 && (_octets[9] == 0xFFu) && (_octets[10] == 0xFFu) && (_octets[11] == 0xFFu) && (_octets[12] == 0xFFu) && (_octets[13] == 0xFFu) && (_octets[14] == 0xFFu); } /** * This method indicates whether or not the IPv6 address contains * Reserved IPv6 IID (RFC 5453), * * @retval TRUE If the IPv6 address contains a reserved IPv6 IID. * @retval FALSE If the IPv6 address does not contain a reserved IPv6 * IID. * */ [[nodiscard]] constexpr bool is_iid_reserved() const noexcept { return is_subnet_router_anycast() || is_reserved_subnet_anycast() || is_anycast_routing_locator(); } /** * This method returns a pointer to the Interface Identifier. * @returns A pointer to the Interface Identifier. */ octets8_t::iterator iid() noexcept { return octets8().begin() + interface_identifier_offset; } /** * This method returns a pointer to the Interface Identifier. * @returns A pointer to the Interface Identifier. */ const octets8_t::const_iterator iid() const noexcept { return octets8().cbegin() + interface_identifier_offset; } /** * This method sets the Interface Identifier. * @param piid A reference to the Interface Identifier. */ void iid(const uint8_t* piid) noexcept { auto _end = piid + interface_identifier_size; auto _iid = iid(); for (auto it = piid; it != _end; it++) { *_iid++ = *it; } } /** * This method sets the Interface Identifier. * @param A reference to the Interface Identifier. */ void iid(const octets8_t::const_iterator& piid) noexcept { auto _iid = iid(); auto _end = _iid + interface_identifier_size; auto pit = piid; for (auto it = _iid; it != _end; it++) { *it = *pit++; } } // /** // * This method sets the Interface Identifier. // * // * @param[in] aExtAddress A reference to the extended // address. // * // */ // void SetIid(const Mac::ExtAddress& aExtAddress) { // // TODO // } // // /** // * This method converts the IPv6 Interface Identifier to an // IEEE // * 802.15.4 Extended Address. // * @param[out] aExtAddress A reference to the extended // address. // */ // void ToExtAddress(Mac::ExtAddress& aExtAddress) const { // // TODO // } // // /** // * This method converts the IPv6 Interface Identifier to an // IEEE // * 802.15.4 MAC Address. // * @param[out] aMacAddress A reference to the MAC address. // */ // void ToExtAddress(Mac::Address& aMacAddress) const { // // TODO // } // // /** // * This method returns the number of IPv6 prefix bits that // match. // * @param[in] aOther The IPv6 address to match against. // * @returns The number of IPv6 prefix bits that match. // */ // uint8_t PrefixMatch(const otIp6Address& aOther) const { // // TODO // } /** * @brief checks if the specified ip is valid or not * @return true if it is an unspecified ip address. */ [[nodiscard]] constexpr bool is_valid() const noexcept { return _prefix != 254 && _prefix != 253; } /** * @brief long string representation of the ip */ std::string str() const noexcept { char buffer[40] = {}; auto _octets = octets16(); sprintf(buffer, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", _octets[0], _octets[1], _octets[2], _octets[3], _octets[4], _octets[5], _octets[6], _octets[7]); buffer[39] = '\0'; return std::string(buffer); } /** * @brief return the short string representation of ip version 6 * TODO: all zero ip prints in a wrong format */ std::string short_str() const noexcept { auto _octets = octets16(); // finding all of the ranges that are zero filled decltype(_octets)::const_iterator range_start = _octets.cend(), range_end = _octets.cend(), start, finish = _octets.cbegin(); do { start = std::find(finish, _octets.cend(), 0u); if (start == _octets.cend()) { break; } finish = std::find_if(start, _octets.cend(), [](auto const& a) { return a != 0; }); if (range_start == _octets.cend() || std::distance(start, finish) > std::distance(range_start, range_end)) { range_start = start; range_end = finish; } } while (finish != _octets.cend()); // generating short string representation of the ip version 6 std::ostringstream ostr; ostr << std::hex; auto it = _octets.cbegin(); // [0, range_start) while (it != range_start) { ostr << *it; if (++it != range_start) ostr << ':'; } // [range_start, range_end) if (it != range_end) { ostr << "::"; it = range_end; } // [range_end, end) while (it != _octets.cend()) { ostr << *it; if (++it != _octets.cend()) ostr << ':'; } return ostr.str(); } /** * Get the prefix if exists or 255 otherwise */ [[nodiscard]] constexpr uint8_t prefix() const noexcept { return _prefix; } /** * Check if the ip has a prefix or not * @return bool an indication of weather or not the ip has a prefix or * not */ [[nodiscard]] constexpr bool has_prefix() const noexcept { return _prefix <= 128; } /** * Set prefix for this ip address * @param prefix */ ipv6& prefix(uint8_t __prefix) noexcept { if (__prefix == 254u) { data = {}; // reset the ip if it was not valid } _prefix = __prefix > 128u && __prefix != 255u ? 253u : __prefix; return *this; } /** * Clears the prefix from this ip */ ipv6& clear_prefix() noexcept { return prefix(255u); } ipv6 reversed() const noexcept { return ipv6 {octets_t { data[14], data[15], data[12], data[13], data[10], data[11], data[8], data[9], data[6], data[7], data[4], data[5], data[2], data[3], data[0], data[1] }, _prefix}; } }; #include <iostream> int main (int, char**argv) { using namespace std; ipv6 ip(argv[1]); cout << ip.short_str() << endl; cout << ip.reversed().short_str() << endl; }
Become a Patron
Sponsor on GitHub
Donate via PayPal
Compiler Explorer Shop
Source on GitHub
Mailing list
Installed libraries
Wiki
Report an issue
How it works
Contact the author
CE on Mastodon
CE on Bluesky
Statistics
Changelog
Version tree