diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 7bca445..f295fda 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -76,7 +76,7 @@ jobs: _cxx="${_cc/#gcc/g++}" _dir="build/dbg_$_cc" mkdir -p "$_dir" - make -j$ncpu -C src O="../$_dir" debug=1 CC="$_cc" CXX="$_cxx" + make V=1 -j$ncpu -C src O="../$_dir" debug=1 CC="$_cc" CXX="$_cxx" (( $? == 0 )) || touch "$_dir/failed" done @@ -90,7 +90,7 @@ jobs: _cxx="${_cc/#clang/clang++}" _dir="build/dbg_$_cc" mkdir -p "$_dir" - make -j$ncpu -C src O="../$_dir" debug=1 CC="$_cc" CXX="$_cxx" + make V=1 -j$ncpu -C src O="../$_dir" debug=1 CC="$_cc" CXX="$_cxx" ret=$? if (( ret == 139 )); then fmt='\n\e[1m\e[33mIgnoring SIGSEGV during %s build.\e[m\n\n' @@ -115,7 +115,7 @@ jobs: _cxx="${_cc/#gcc/g++}" _dir="build/rel_$_cc" mkdir -p "$_dir" - make -j$ncpu -C src O="../$_dir" CC="$_cc" CXX="$_cxx" + make V=1 -j$ncpu -C src O="../$_dir" CC="$_cc" CXX="$_cxx" (( $? == 0 )) || touch "$_dir/failed" done @@ -129,7 +129,7 @@ jobs: _cxx="${_cc/#clang/clang++}" _dir="build/rel_$_cc" mkdir -p "$_dir" - make -j$ncpu -C src O="../$_dir" CC="$_cc" CXX="$_cxx" + make V=1 -j$ncpu -C src O="../$_dir" CC="$_cc" CXX="$_cxx" ret=$? if (( ret == 139 )); then fmt='\n\e[1m\e[33mIgnoring SIGSEGV during %s build.\e[m\n\n' @@ -253,7 +253,7 @@ jobs: _cxx="${_cc/#gcc/g++}" _dir="build/dbg_$_cc" mkdir -p "$_dir" - make -j$ncpu -C src O="../$_dir" debug=1 CC="$_cc" CXX="$_cxx" + make V=1 -j$ncpu -C src O="../$_dir" debug=1 CC="$_cc" CXX="$_cxx" (( $? == 0 )) || touch "$_dir/failed" done @@ -267,7 +267,7 @@ jobs: _cxx="${_cc/#clang/clang++}" _dir="build/dbg_$_cc" mkdir -p "$_dir" - make -j$ncpu -C src O="../$_dir" debug=1 CC="$_cc" CXX="$_cxx" + make V=1 -j$ncpu -C src O="../$_dir" debug=1 CC="$_cc" CXX="$_cxx" ret=$? if (( ret == 139 )); then fmt='\n\e[1m\e[33mIgnoring SIGSEGV during %s build.\e[m\n\n' @@ -292,7 +292,7 @@ jobs: _cxx="${_cc/#gcc/g++}" _dir="build/rel_$_cc" mkdir -p "$_dir" - make -j$ncpu -C src O="../$_dir" CC="$_cc" CXX="$_cxx" + make V=1 -j$ncpu -C src O="../$_dir" CC="$_cc" CXX="$_cxx" (( $? == 0 )) || touch "$_dir/failed" done @@ -306,7 +306,7 @@ jobs: _cxx="${_cc/#clang/clang++}" _dir="build/rel_$_cc" mkdir -p "$_dir" - make -j$ncpu -C src O="../$_dir" CC="$_cc" CXX="$_cxx" + make V=1 -j$ncpu -C src O="../$_dir" CC="$_cc" CXX="$_cxx" ret=$? if (( ret == 139 )); then fmt='\n\e[1m\e[33mIgnoring SIGSEGV during %s build.\e[m\n\n' @@ -430,7 +430,7 @@ jobs: _cxx="${_cc/#gcc/g++}" _dir="build/dbg_$_cc" mkdir -p "$_dir" - make -j$ncpu -C src O="../$_dir" debug=1 CC="$_cc" CXX="$_cxx" + make V=1 -j$ncpu -C src O="../$_dir" debug=1 CC="$_cc" CXX="$_cxx" (( $? == 0 )) || touch "$_dir/failed" done @@ -444,7 +444,7 @@ jobs: _cxx="${_cc/#clang/clang++}" _dir="build/dbg_$_cc" mkdir -p "$_dir" - make -j$ncpu -C src O="../$_dir" debug=1 CC="$_cc" CXX="$_cxx" + make V=1 -j$ncpu -C src O="../$_dir" debug=1 CC="$_cc" CXX="$_cxx" (( $? == 0 )) || touch "$_dir/failed" done @@ -458,7 +458,7 @@ jobs: _cxx="${_cc/#gcc/g++}" _dir="build/rel_$_cc" mkdir -p "$_dir" - make -j$ncpu -C src O="../$_dir" CC="$_cc" CXX="$_cxx" + make V=1 -j$ncpu -C src O="../$_dir" CC="$_cc" CXX="$_cxx" (( $? == 0 )) || touch "$_dir/failed" done @@ -472,7 +472,7 @@ jobs: _cxx="${_cc/#clang/clang++}" _dir="build/rel_$_cc" mkdir -p "$_dir" - make -j$ncpu -C src O="../$_dir" CC="$_cc" CXX="$_cxx" + make V=1 -j$ncpu -C src O="../$_dir" CC="$_cc" CXX="$_cxx" (( $? == 0 )) || touch "$_dir/failed" done diff --git a/.gitignore b/.gitignore index 538c0ec..134f0a3 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,7 @@ /.qtc_clangd /.vscode /build +/src/stochar /src/test +/src/test-json /src/test-utf8 diff --git a/src/Makefile b/src/Makefile index 86753af..0a9ef73 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,6 +1,6 @@ override THIS_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST)))) -override TARGETS := test test-utf8 +override TARGETS := stochar test test-json test-rev test-utf8 all:| $(TARGETS) clean:| $(TARGETS:%=clean-%) @@ -18,11 +18,19 @@ ifneq (,$(filter $(PUBLISH),$(or $(MAKECMDGOALS),all))) include $(THIS_DIR)../common.mk +override SRC_stochar := letopt.c stochar.c utf8.c +override DBG_stochar := dbg.c + override SRC_test := cc.c cxx.cpp dstr.c file.c test.c override DBG_test := dbg.c override LIBS_test = $(CJSON_LIBS) override CFLAGS_test.c = $(CJSON_CFLAGS) +override SRC_test-rev := parse.c test-rev.c + +override SRC_test-json := json.c test-json.c +override DBG_test-json := dbg.c + override SRC_test-utf8 := letopt.c test-utf8.c utf8.c utf8_graph.c override DBG_test-utf8 := dbg.c diff --git a/src/b25.c b/src/b25.c new file mode 100644 index 0000000..705a964 --- /dev/null +++ b/src/b25.c @@ -0,0 +1,527 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/** @file b25.c + * @brief 32-bit binary De Bruijn sequences + * @author Juuso Alasuutari + */ +#include +#include +#include +#include + +#include "b25.h" + +static force_inline void +b25_div_add (uint64_t *const div, + uint5 const len) +{ + unsigned const i = b25_div_offset(len); + uint64_t const m = b25_div_mask(len) << i; + uint64_t const n = *div & m; + if (n < m) { + uint64_t const add_total = (*div & 0x0fU) < 0x0fU; + uint64_t const add_unique = !n && (*div & 0xf0U) < 0xf0U; + *div += UINT64_C(1) << i | add_unique << 4U | add_total; + } +} + +static force_inline bool +b25_info_add_cycle (struct b25_info *dst, + uint32_t msk) +{ + uint32_t off = 0U; + uint32_t prd = 0U; + + for (uint32_t m = msk; m; m >>= 1U) { + prd += m & 1U; + off += !prd; + } + + if (!prd) + return false; + + const unsigned i = b25_info_get_num_cycles(dst); + if (i >= 9U) + return false; + + uint5 n = uint5(prd - 1U); + b25_div_add(&dst->div, n); + if (prd > 1U) { + u10x3_append(&dst->loc[i / 3U], uint5(off), n); + dst->mask[i] = msk; + } + + return b25_div_get_unique(dst->div) < 8U; +} + +b25_loc +b25_info_get_cycle (struct b25_info const *const src, + unsigned const i) +{ + if (i < b25_info_get_num_cycles(src)) { + unsigned const n = i % 3U; + uint32_t x = src->loc[i / 3U]; + if (n < u10x3_count(x)) { + x >>= n * 10U + 2U; + return (b25_loc){ + .offset = (u16bit)(x & 31U), + .period = (u16bit)((x >> 5U & 31U) + 1U) + }; + } + } + return (b25_loc){0}; +} + +const uint32_t B25[2048] = { + 0x4653adf,0x4653b5f,0x4653eb7,0x4653ed7,0x46569df,0x46569f7,0x4656e9f,0x4656fa7,0x465769f,0x4657da7,0x465a9df,0x465a9f7,0x465ba9f,0x465bea7,0x465da9f,0x465f6a7, + 0x4674adf,0x46752df,0x467695f,0x4676a5f,0x467d2b7,0x467d4b7,0x467da57,0x467da97,0x46959df,0x46959f7,0x4695cfb,0x4695d9f,0x4695f3b,0x4695f67,0x469cafb,0x469d95f, + 0x469df2b,0x469f2bb,0x469f657,0x469f72b,0x46a59df,0x46a59f7,0x46a5cfb,0x46a5d9f,0x46a5f3b,0x46a5f67,0x46a72fb,0x46a765f,0x46a77cb,0x46a7cbb,0x46a7d97,0x46a7dcb, + 0x46b29df,0x46b29f7,0x46b94fb,0x46bb29f,0x46be53b,0x46beca7,0x46ca75f,0x46ca7d7,0x46cae9f,0x46cafa7,0x46cba9f,0x46cbea7,0x46ce95f,0x46cea5f,0x46cfa57,0x46cfa97, + 0x46e53eb,0x46e7d2b,0x46e7d4b,0x46e959f,0x46e9f2b,0x46ea59f,0x46ea7cb,0x46eb29f,0x46f94eb,0x46f9d2b,0x46f9d4b,0x46fa567,0x46fa72b,0x46fa967,0x46fa9cb,0x46faca7, + 0x4729afb,0x4729beb,0x472b7d3,0x472be9b,0x472bed3,0x472df53,0x472fa9b,0x472fb53,0x4734afb,0x47352fb,0x4737d2b,0x4737d4b,0x474acdf,0x474adf3,0x474af9b,0x474cadf, + 0x474d95f,0x474df2b,0x4752cdf,0x4752df3,0x4752f9b,0x47532df,0x475365f,0x47537cb,0x47594df,0x475be53,0x475f29b,0x476535f,0x476695f,0x4766a5f,0x47695f3,0x476995f, + 0x476a5f3,0x476a65f,0x476be53,0x477ca6b,0x477cad3,0x477cb53,0x477cd2b,0x477cd4b,0x47ca6bb,0x47ca6eb,0x47cadd3,0x47cae9b,0x47caed3,0x47cb753,0x47cba9b,0x47cbb53, + 0x47cd2bb,0x47cd4bb,0x47cdd2b,0x47cdd4b,0x47d2b37,0x47d2b73,0x47d2b9b,0x47d32b7,0x47d3657,0x47d372b,0x47d4b37,0x47d4b73,0x47d4b9b,0x47d4cb7,0x47d4d97,0x47d4dcb, + 0x47d6537,0x47d6e53,0x47d729b,0x47d94d7,0x47d9a57,0x47d9a97,0x47da573,0x47da657,0x47da973,0x47da997,0x47dae53,0x47dca6b,0x47dcad3,0x47dcb53,0x47dcd2b,0x47dcd4b, + 0x4a33adf,0x4a33b5f,0x4a33eb7,0x4a33ed7,0x4a359df,0x4a359f7,0x4a35cfb,0x4a35d9f,0x4a35f3b,0x4a35f67,0x4a3675f,0x4a367d7,0x4a373eb,0x4a3759f,0x4a37ceb,0x4a37d67, + 0x4a39afb,0x4a39beb,0x4a3acdf,0x4a3adf3,0x4a3af9b,0x4a3b35f,0x4a3b5f3,0x4a3be6b,0x4a3e6bb,0x4a3e6eb,0x4a3eb37,0x4a3eb73,0x4a3eb9b,0x4a3ecd7,0x4a3ed73,0x4a3ee6b, + 0x4ac6e7d,0x4ac6f9d,0x4ac737d,0x4ac77cd,0x4ac7cdd,0x4ac7dcd,0x4acd1df,0x4acd1f7,0x4acdc7d,0x4acdd1f,0x4acdf1d,0x4acdf47,0x4ace37d,0x4ace8df,0x4acef8d,0x4acf8dd, + 0x4acfa37,0x4acfb8d,0x4ad19df,0x4ad19f7,0x4ad1df3,0x4ad1f73,0x4adc67d,0x4adcc7d,0x4adcfa3,0x4add19f,0x4add1f3,0x4adf19d,0x4adf31d,0x4adf3a3,0x4adf467,0x4adf473, + 0x4ae33ed,0x4ae367d,0x4ae3ecd,0x4ae63ed,0x4ae68fb,0x4ae6c7d,0x4ae7d1b,0x4ae7d8d,0x4ae7da3,0x4ae8d9f,0x4ae8f9b,0x4aec7cd,0x4aecd1f,0x4aecf8d,0x4aed19f,0x4aed1f3, + 0x4af8ced,0x4af8d9d,0x4af8ecd,0x4af98ed,0x4af9a3b,0x4af9b1d,0x4af9d1b,0x4af9d8d,0x4af9da3,0x4afa367,0x4afa39b,0x4afb1cd,0x4afb347,0x4afb38d,0x4afb467,0x4afb473, + 0x4b1b9f5,0x4b1be75,0x4b1cdf5,0x4b1df35,0x4b1f375,0x4b1f735,0x4b351df,0x4b351f7,0x4b371f5,0x4b3751f,0x4b37c75,0x4b37d47,0x4b38df5,0x4b3a8df,0x4b3be35,0x4b3e375, + 0x4b3ea37,0x4b3ee35,0x4b519df,0x4b519f7,0x4b51df3,0x4b51f73,0x4b719f5,0x4b731f5,0x4b73ea3,0x4b7519f,0x4b751f3,0x4b7c675,0x4b7cc75,0x4b7cea3,0x4b7d467,0x4b7d473, + 0x4b8cfb5,0x4b8d9f5,0x4b8fb35,0x4b98fb5,0x4b9a8fb,0x4b9b1f5,0x4b9f51b,0x4b9f635,0x4b9f6a3,0x4ba8d9f,0x4ba8f9b,0x4bb1f35,0x4bb351f,0x4bb3e35,0x4bb519f,0x4bb51f3, + 0x4be33b5,0x4be3675,0x4be3b35,0x4be63b5,0x4be6a3b,0x4be6c75,0x4be751b,0x4be7635,0x4be76a3,0x4bea367,0x4bea39b,0x4bec735,0x4becd47,0x4bece35,0x4bed467,0x4bed473, + 0x4c72b7d,0x4c72bed,0x4c72df5,0x4c72fb5,0x4c75be5,0x4c76be5,0x4c77cad,0x4c77cb5,0x4c7cadd,0x4c7caed,0x4c7cb75,0x4c7cbb5,0x4c7d6e5,0x4c7dae5,0x4c7dcad,0x4c7dcb5, + 0x4ca3adf,0x4ca3b5f,0x4ca3eb7,0x4ca3ed7,0x4cad1df,0x4cad1f7,0x4cadc7d,0x4cadd1f,0x4cadf1d,0x4cadf47,0x4cae3ed,0x4caed1f,0x4caf8ed,0x4cafb47,0x4cb51df,0x4cb51f7, + 0x4cb71f5,0x4cb751f,0x4cb7c75,0x4cb7d47,0x4cb8fb5,0x4cbb51f,0x4cbe3b5,0x4cbed47,0x4d1cafb,0x4d1d95f,0x4d1df2b,0x4d1f2bb,0x4d1f657,0x4d1f72b,0x4d472fb,0x4d4765f, + 0x4d477cb,0x4d47cbb,0x4d47d97,0x4d47dcb,0x4d63be5,0x4d63ee5,0x4d651df,0x4d651f7,0x4d71f65,0x4d728fb,0x4d763e5,0x4d7651f,0x4d7c765,0x4d7ca3b,0x4d7d8e5,0x4d7d947, + 0x4d8e57d,0x4d8e5f5,0x4d8ebe5,0x4d8f95d,0x4d8f975,0x4d8fae5,0x4d9475f,0x4d947d7,0x4d95c7d,0x4d95d1f,0x4d95f1d,0x4d95f47,0x4d971f5,0x4d9751f,0x4d97c75,0x4d97d47, + 0x4dc7d65,0x4dca3eb,0x4dcac7d,0x4dcb1f5,0x4dd1f2b,0x4dd47cb,0x4dd63e5,0x4dd651f,0x4df1d65,0x4df28eb,0x4df2b1d,0x4df2c75,0x4df472b,0x4df51cb,0x4df58e5,0x4df5947, + 0x4e32b7d,0x4e32bed,0x4e32df5,0x4e32fb5,0x4e35f65,0x4e3657d,0x4e365f5,0x4e37d65,0x4e51afb,0x4e51beb,0x4e5637d,0x4e56fa3,0x4e57d1b,0x4e57d8d,0x4e57da3,0x4e58df5, + 0x4e5bea3,0x4e5f51b,0x4e5f635,0x4e5f6a3,0x4e8cadf,0x4e8d95f,0x4e8df2b,0x4ea32df,0x4ea365f,0x4ea37cb,0x4eb1be5,0x4eb28df,0x4eb7c65,0x4eb7ca3,0x4ebe365,0x4ebe51b, + 0x4ec6be5,0x4eca35f,0x4ecaf8d,0x4ecbe35,0x4ed195f,0x4ed465f,0x4ed7c65,0x4ed7ca3,0x4ef8cad,0x4ef8cb5,0x4ef8d65,0x4ef946b,0x4ef958d,0x4ef95a3,0x4ef9635,0x4ef96a3, + 0x4f8cadd,0x4f8caed,0x4f8cb75,0x4f8cbb5,0x4f8d765,0x4f8d95d,0x4f8d975,0x4f8dd65,0x4f946bb,0x4f946eb,0x4f958dd,0x4f95ba3,0x4f95d1b,0x4f95d8d,0x4f95da3,0x4f96375, + 0x4f96ea3,0x4f9751b,0x4f97635,0x4f976a3,0x4fa32b7,0x4fa3657,0x4fa372b,0x4fa8cb7,0x4fa8d97,0x4fa8dcb,0x4fac6e5,0x4faca37,0x4fadc65,0x4fadca3,0x4fae365,0x4fae51b, + 0x4fb1ae5,0x4fb28d7,0x4fb2b8d,0x4fb2e35,0x4fb4657,0x4fb5197,0x4fb5c65,0x4fb5ca3,0x4fb8cad,0x4fb8cb5,0x4fb8d65,0x4fb946b,0x4fb958d,0x4fb95a3,0x4fb9635,0x4fb96a3, + 0x5193adf,0x5193b5f,0x5193eb7,0x5193ed7,0x51ac9df,0x51ac9f7,0x51ae4fb,0x51aec9f,0x51af93b,0x51afb27,0x51b275f,0x51b27d7,0x51b93eb,0x51bac9f,0x51be4eb,0x51beb27, + 0x51c9afb,0x51c9beb,0x51d64df,0x51d6f93,0x51d7c9b,0x51d935f,0x51daf93,0x51df26b,0x51f26bb,0x51f26eb,0x51f5937,0x51f5b93,0x51f5c9b,0x51f64d7,0x51f6b93,0x51f726b, + 0x5233adf,0x5233b5f,0x5233eb7,0x5233ed7,0x52359df,0x52359f7,0x5235cfb,0x5235d9f,0x5235f3b,0x5235f67,0x523675f,0x52367d7,0x52373eb,0x523759f,0x5237ceb,0x5237d67, + 0x5239afb,0x5239beb,0x523acdf,0x523adf3,0x523af9b,0x523b35f,0x523b5f3,0x523be6b,0x523e6bb,0x523e6eb,0x523eb37,0x523eb73,0x523eb9b,0x523ecd7,0x523ed73,0x523ee6b, + 0x531d6f9,0x531daf9,0x531f5b9,0x531f6b9,0x5323adf,0x5323b5f,0x5323eb7,0x5323ed7,0x5358ef9,0x5358fb9,0x53591df,0x53591f7,0x535c7d9,0x535c8fb,0x535d8f9,0x535d91f, + 0x535f1d9,0x535f23b,0x535f639,0x535f647,0x5363af9,0x5363eb9,0x536475f,0x53647d7,0x5371f59,0x53723eb,0x53758f9,0x537591f,0x537c759,0x537c8eb,0x537d639,0x537d647, + 0x538d7d9,0x538df59,0x5391afb,0x5391beb,0x53ac6f9,0x53ac8df,0x53adf19,0x53adf23,0x53af8d9,0x53af91b,0x53b1af9,0x53b235f,0x53b5f19,0x53b5f23,0x53be359,0x53be46b, + 0x53e35d9,0x53e3759,0x53e46bb,0x53e46eb,0x53eb1b9,0x53eb237,0x53eb719,0x53eb723,0x53eb8d9,0x53eb91b,0x53ec6b9,0x53ec8d7,0x53ed719,0x53ed723,0x53ee359,0x53ee46b, + 0x5634ef9,0x5634fb9,0x563727d,0x56373e9,0x56374f9,0x5637c9d,0x5637ce9,0x5637d39,0x563937d,0x5639be9,0x563a6f9,0x563be4d,0x563be69,0x563e4dd,0x563e6e9,0x563e9b9, + 0x563ee4d,0x563ee69,0x56469df,0x56469f7,0x5646e9f,0x5646fa7,0x56474df,0x5647d37,0x564d1df,0x564d1f7,0x564dc7d,0x564dd1f,0x564df1d,0x564df47,0x564e37d,0x564e8df, + 0x564ef8d,0x564f8dd,0x564fa37,0x564fb8d,0x56691df,0x56691f7,0x566e3e9,0x566e91f,0x566f8e9,0x566fa47,0x5671be9,0x56748df,0x5677c69,0x567c6e9,0x567d237,0x567dc69, + 0x568c9df,0x568c9f7,0x568ef93,0x568fb93,0x56919df,0x56919f7,0x5691df3,0x5691f73,0x5698ef9,0x5698fb9,0x56991df,0x56991f7,0x569df19,0x569df23,0x569f719,0x569f723, + 0x56e327d,0x56e33e9,0x56e3e99,0x56e47d3,0x56e4c7d,0x56e4fa3,0x56e63e9,0x56e7d23,0x56e8c9f,0x56e8f93,0x56e919f,0x56e91f3,0x56e98f9,0x56e991f,0x56e9f19,0x56e9f23, + 0x56f8c9d,0x56f8ce9,0x56f8e99,0x56f91d3,0x56f931d,0x56f93a3,0x56f98e9,0x56f9d23,0x56fa327,0x56fa393,0x56fa467,0x56fa473,0x56fa639,0x56fa647,0x56fa719,0x56fa723, + 0x57193ed,0x5719f69,0x571a7d9,0x571b27d,0x571b3e9,0x571f4d9,0x571f64d,0x571f669,0x571f699,0x57234fb,0x5723e9b,0x5723ed3,0x57263ed,0x57268fb,0x5726c7d,0x5727d1b, + 0x5727d8d,0x5727da3,0x5731f69,0x57348fb,0x57363e9,0x573e91b,0x573ec69,0x573ed23,0x5746c9f,0x5747c9b,0x5748d9f,0x5748f9b,0x574d8f9,0x574d91f,0x574f8d9,0x574f91b, + 0x57634f9,0x5763e4d,0x5763e69,0x576469f,0x5764d1f,0x5764f8d,0x576691f,0x5767c69,0x5768c9f,0x5768f93,0x576919f,0x57691f3,0x57698f9,0x576991f,0x5769f19,0x5769f23, + 0x57c64ed,0x57c6769,0x57c69d9,0x57c6c9d,0x57c6ce9,0x57c74d9,0x57c764d,0x57c7669,0x57c7699,0x57c8d3b,0x57c8e9b,0x57c8ed3,0x57c98ed,0x57c9a3b,0x57c9b1d,0x57c9d1b, + 0x57c9d8d,0x57c9da3,0x57cc769,0x57cd23b,0x57cd8e9,0x57ce91b,0x57cec69,0x57ced23,0x57d1b27,0x57d1c9b,0x57d2367,0x57d239b,0x57d3639,0x57d3647,0x57d38d9,0x57d391b, + 0x57d8d39,0x57d8e4d,0x57d8e69,0x57d91a7,0x57d9347,0x57d938d,0x57d9a47,0x57d9c69,0x57da327,0x57da393,0x57da467,0x57da473,0x57da639,0x57da647,0x57da719,0x57da723, + 0x58d4ef9,0x58d4fb9,0x58dc9f5,0x58dcfa9,0x58dd4f9,0x58df275,0x58df3a9,0x58df539,0x58e4df5,0x58e6fa9,0x58ea6f9,0x58ef935,0x58ef9a9,0x58f9375,0x58f9ba9,0x58fa9b9, + 0x58fb935,0x58fb9a9,0x591a9df,0x591a9f7,0x591ba9f,0x591bea7,0x591d4df,0x591f537,0x59351df,0x59351f7,0x59371f5,0x593751f,0x5937c75,0x5937d47,0x5938df5,0x593a8df, + 0x593be35,0x593e375,0x593ea37,0x593ee35,0x59a91df,0x59a91f7,0x59b8fa9,0x59ba91f,0x59be3a9,0x59bea47,0x59c6fa9,0x59d48df,0x59df1a9,0x59f1ba9,0x59f5237,0x59f71a9, + 0x5a8c9df,0x5a8c9f7,0x5a8ef93,0x5a8fb93,0x5a919df,0x5a919f7,0x5a91df3,0x5a91f73,0x5a98ef9,0x5a98fb9,0x5a991df,0x5a991f7,0x5a9df19,0x5a9df23,0x5a9f719,0x5a9f723, + 0x5b8c9f5,0x5b8cfa9,0x5b8fa99,0x5b91f53,0x5b931f5,0x5b93ea3,0x5b98fa9,0x5b9f523,0x5ba8c9f,0x5ba8f93,0x5ba919f,0x5ba91f3,0x5ba98f9,0x5ba991f,0x5ba9f19,0x5ba9f23, + 0x5be3275,0x5be33a9,0x5be3a99,0x5be4753,0x5be4c75,0x5be4ea3,0x5be63a9,0x5be7523,0x5bea327,0x5bea393,0x5bea467,0x5bea473,0x5bea639,0x5bea647,0x5bea719,0x5bea723, + 0x5c64fb5,0x5c67da9,0x5c6a7d9,0x5c6c9f5,0x5c6cfa9,0x5c7d4d9,0x5c7d935,0x5c7d9a9,0x5c7da99,0x5c8d4fb,0x5c8fa9b,0x5c8fb53,0x5c98fb5,0x5c9a8fb,0x5c9b1f5,0x5c9f51b, + 0x5c9f635,0x5c9f6a3,0x5cc7da9,0x5cd48fb,0x5cd8fa9,0x5cfa91b,0x5cfb1a9,0x5cfb523,0x5d46c9f,0x5d47c9b,0x5d48d9f,0x5d48f9b,0x5d4d8f9,0x5d4d91f,0x5d4f8d9,0x5d4f91b, + 0x5d8d4f9,0x5d8f935,0x5d8f9a9,0x5d91a9f,0x5d9351f,0x5d93e35,0x5d9a91f,0x5d9f1a9,0x5da8c9f,0x5da8f93,0x5da919f,0x5da91f3,0x5da98f9,0x5da991f,0x5da9f19,0x5da9f23, + 0x5f193b5,0x5f19da9,0x5f1a9d9,0x5f1b275,0x5f1b3a9,0x5f1d4d9,0x5f1d935,0x5f1d9a9,0x5f1da99,0x5f2353b,0x5f23a9b,0x5f23b53,0x5f263b5,0x5f26a3b,0x5f26c75,0x5f2751b, + 0x5f27635,0x5f276a3,0x5f31da9,0x5f3523b,0x5f363a9,0x5f3a91b,0x5f3b1a9,0x5f3b523,0x5f51b27,0x5f51c9b,0x5f52367,0x5f5239b,0x5f53639,0x5f53647,0x5f538d9,0x5f5391b, + 0x5f63539,0x5f63935,0x5f639a9,0x5f646a7,0x5f64d47,0x5f64e35,0x5f66a47,0x5f671a9,0x5f6a327,0x5f6a393,0x5f6a467,0x5f6a473,0x5f6a639,0x5f6a647,0x5f6a719,0x5f6a723, + 0x6256e7d,0x6256f9d,0x62573ed,0x6257ced,0x625b9f5,0x625be75,0x625cfb5,0x625f3b5,0x6272b7d,0x6272bed,0x6272df5,0x6272fb5,0x6275be5,0x6276be5,0x6277cad,0x6277cb5, + 0x627cadd,0x627caed,0x627cb75,0x627cbb5,0x627d6e5,0x627dae5,0x627dcad,0x627dcb5,0x629d6f9,0x629daf9,0x629f5b9,0x629f6b9,0x62b4ef9,0x62b4fb9,0x62b727d,0x62b73e9, + 0x62b74f9,0x62b7c9d,0x62b7ce9,0x62b7d39,0x62b93ed,0x62b9f69,0x62bb4f9,0x62be4ed,0x62be769,0x62bed39,0x62d4ef9,0x62d4fb9,0x62dc9f5,0x62dcfa9,0x62dd4f9,0x62df275, + 0x62df3a9,0x62df539,0x62e4fb5,0x62e7da9,0x62ed4f9,0x62f93b5,0x62f9da9,0x62fb539,0x6453adf,0x6453b5f,0x6453eb7,0x6453ed7,0x64569df,0x64569f7,0x6456e9f,0x6456fa7, + 0x645769f,0x6457da7,0x645a9df,0x645a9f7,0x645ba9f,0x645bea7,0x645da9f,0x645f6a7,0x64e2b7d,0x64e2bed,0x64e2df5,0x64e2fb5,0x64e8adf,0x64ea2df,0x64eb7c5,0x64ed15f, + 0x64ed45f,0x64ed7c5,0x64ef8ad,0x64ef8b5,0x64f8add,0x64f8aed,0x64f8b75,0x64f8bb5,0x64fa2b7,0x64fa8b7,0x64fadc5,0x64fb457,0x64fb517,0x64fb5c5,0x64fb8ad,0x64fb8b5, + 0x6513adf,0x6513b5f,0x6513eb7,0x6513ed7,0x653adf1,0x653b5f1,0x653eb71,0x653ed71,0x65689df,0x65689f7,0x6569df1,0x6569f71,0x656e27d,0x656e89f,0x656e9f1,0x656f89d, + 0x656fa27,0x656fa71,0x65713ed,0x657689f,0x65769f1,0x657c4ed,0x657da27,0x657da71,0x65a89df,0x65a89f7,0x65a9df1,0x65a9f71,0x65b89f5,0x65ba89f,0x65ba9f1,0x65be275, + 0x65bea27,0x65bea71,0x65c4fb5,0x65da89f,0x65da9f1,0x65f13b5,0x65f6a27,0x65f6a71,0x6712b7d,0x6712bed,0x6712df5,0x6712fb5,0x6715be9,0x6715f69,0x6716fa9,0x6717da9, + 0x6744adf,0x6748adf,0x674adf1,0x67512df,0x67522df,0x6752df1,0x675be25,0x675be29,0x676895f,0x676915f,0x67695f1,0x676a25f,0x676a45f,0x676a5f1,0x676be25,0x676be29, + 0x677c4ad,0x677c4b5,0x677c569,0x677c5a9,0x67c4add,0x67c4aed,0x67c4b75,0x67c4bb5,0x67c56e9,0x67c5769,0x67c5ba9,0x67c5da9,0x67d12b7,0x67d22b7,0x67d2b71,0x67d44b7, + 0x67d48b7,0x67d4b71,0x67d6e25,0x67d6e29,0x67da257,0x67da457,0x67da571,0x67da897,0x67da917,0x67da971,0x67dae25,0x67dae29,0x67dc4ad,0x67dc4b5,0x67dc569,0x67dc5a9, + 0x68959df,0x68959f7,0x6895cfb,0x6895d9f,0x6895f3b,0x6895f67,0x689cafb,0x689d95f,0x689df2b,0x689f2bb,0x689f657,0x689f72b,0x68ac9df,0x68ac9f7,0x68ae4fb,0x68aec9f, + 0x68af93b,0x68afb27,0x69159df,0x69159f7,0x6915cfb,0x6915d9f,0x6915f3b,0x6915f67,0x6959df1,0x6959f71,0x695cfb1,0x695d9f1,0x695f3b1,0x695f671,0x69c57d9,0x69c8afb, + 0x69cafb1,0x69d8af9,0x69d915f,0x69d95f1,0x69df159,0x69df22b,0x69df2b1,0x69f15d9,0x69f22bb,0x69f2bb1,0x69f62b9,0x69f6457,0x69f6571,0x69f7159,0x69f722b,0x69f72b1, + 0x6a259df,0x6a259f7,0x6a25cfb,0x6a25d9f,0x6a25f3b,0x6a25f67,0x6a272fb,0x6a2765f,0x6a277cb,0x6a27cbb,0x6a27d97,0x6a27dcb,0x6a2c9df,0x6a2c9f7,0x6a2e4fb,0x6a2ec9f, + 0x6a2f93b,0x6a2fb27,0x6a459df,0x6a459f7,0x6a45cfb,0x6a45d9f,0x6a45f3b,0x6a45f67,0x6a59df1,0x6a59f71,0x6a5cfb1,0x6a5d9f1,0x6a5f3b1,0x6a5f671,0x6a717d9,0x6a722fb, + 0x6a72fb1,0x6a762f9,0x6a7645f,0x6a765f1,0x6a77c59,0x6a77c8b,0x6a77cb1,0x6a7c5d9,0x6a7c8bb,0x6a7cbb1,0x6a7d8b9,0x6a7d917,0x6a7d971,0x6a7dc59,0x6a7dc8b,0x6a7dcb1, + 0x6b13be5,0x6b13ee5,0x6b14ef9,0x6b14fb9,0x6b229df,0x6b229f7,0x6b277c5,0x6b27dc5,0x6b289df,0x6b289f7,0x6b29df1,0x6b29f71,0x6b3be25,0x6b3be29,0x6b3ee25,0x6b3ee29, + 0x6b89f65,0x6b8a7d9,0x6b914fb,0x6b93ec5,0x6b944fb,0x6b94fb1,0x6b9f625,0x6b9f629,0x6bb13e5,0x6bb14f9,0x6bb229f,0x6bb27c5,0x6bb289f,0x6bb29f1,0x6bb3e25,0x6bb3e29, + 0x6be2765,0x6be29d9,0x6be453b,0x6be4ec5,0x6be513b,0x6be53b1,0x6be7625,0x6be7629,0x6bec4e5,0x6bec539,0x6bec8a7,0x6bec9c5,0x6beca27,0x6beca71,0x6bece25,0x6bece29, + 0x6c4ae7d,0x6c4af9d,0x6c4b9f5,0x6c4be75,0x6c4e57d,0x6c4e5f5,0x6c4ebe5,0x6c4f95d,0x6c4f975,0x6c4fae5,0x6c53af9,0x6c53eb9,0x6c5727d,0x6c573e9,0x6c574f9,0x6c57c9d, + 0x6c57ce9,0x6c57d39,0x6c5c9f5,0x6c5cfa9,0x6c5d4f9,0x6c5f275,0x6c5f3a9,0x6c5f539,0x6c8a75f,0x6c8a7d7,0x6c8ae9f,0x6c8afa7,0x6c8ba9f,0x6c8bea7,0x6c9c57d,0x6c9c5f5, + 0x6c9d15f,0x6c9d45f,0x6c9d7c5,0x6c9f15d,0x6c9f175,0x6c9f457,0x6c9f517,0x6c9f5c5,0x6ca275f,0x6ca27d7,0x6ca75f1,0x6ca7d71,0x6cae27d,0x6cae89f,0x6cae9f1,0x6caf89d, + 0x6cafa27,0x6cafa71,0x6cb89f5,0x6cba89f,0x6cba9f1,0x6cbe275,0x6cbea27,0x6cbea71,0x6ce257d,0x6ce25f5,0x6ce2be9,0x6ce2fa9,0x6ce895f,0x6ce915f,0x6ce95f1,0x6cea25f, + 0x6cea45f,0x6cea5f1,0x6cebe25,0x6cebe29,0x6cf895d,0x6cf8975,0x6cf8ae9,0x6cf8ba9,0x6cfa257,0x6cfa457,0x6cfa571,0x6cfa897,0x6cfa917,0x6cfa971,0x6cfae25,0x6cfae29, + 0x6e2567d,0x6e259f5,0x6e27d65,0x6e29f59,0x6e2b27d,0x6e2b3e9,0x6e2c9f5,0x6e2cfa9,0x6e453eb,0x6e4fa2b,0x6e4fa8b,0x6e4fac5,0x6e513eb,0x6e53eb1,0x6e5627d,0x6e589f5, + 0x6e7d12b,0x6e7d22b,0x6e7d2b1,0x6e7d44b,0x6e7d48b,0x6e7d4b1,0x6e7d625,0x6e7d629,0x6e8959f,0x6e89f2b,0x6e8ac9f,0x6e9159f,0x6e959f1,0x6e9f159,0x6e9f22b,0x6e9f2b1, + 0x6ea259f,0x6ea27cb,0x6ea2c9f,0x6ea459f,0x6ea59f1,0x6ea7c59,0x6ea7c8b,0x6ea7cb1,0x6eb13e5,0x6eb14f9,0x6eb229f,0x6eb27c5,0x6eb289f,0x6eb29f1,0x6eb3e25,0x6eb3e29, + 0x6f8959d,0x6f89675,0x6f89d65,0x6f8a759,0x6f8ac9d,0x6f8ace9,0x6f8b275,0x6f8b3a9,0x6f914eb,0x6f93a2b,0x6f93a8b,0x6f93ac5,0x6f944eb,0x6f94eb1,0x6f9589d,0x6f96275, + 0x6f9d12b,0x6f9d22b,0x6f9d2b1,0x6f9d44b,0x6f9d48b,0x6f9d4b1,0x6f9d625,0x6f9d629,0x6fa2567,0x6fa272b,0x6fa2b27,0x6fa4567,0x6fa5671,0x6fa7159,0x6fa722b,0x6fa72b1, + 0x6fa8967,0x6fa89cb,0x6fa8b27,0x6fa9167,0x6fa9671,0x6fa9c59,0x6fa9c8b,0x6fa9cb1,0x6fac4e5,0x6fac539,0x6fac8a7,0x6fac9c5,0x6faca27,0x6faca71,0x6face25,0x6face29, + 0x712b37d,0x712becd,0x712cdf5,0x712fb35,0x7132b7d,0x7132bed,0x7132df5,0x7132fb5,0x7135f65,0x713657d,0x71365f5,0x7137d65,0x714d7d9,0x714df59,0x715937d,0x7159be9, + 0x715be99,0x715f4d9,0x715f64d,0x715f669,0x715f699,0x7164df5,0x7166fa9,0x716fa99,0x717d4d9,0x717d935,0x717d9a9,0x717da99,0x7229afb,0x7229beb,0x722b7d3,0x722be9b, + 0x722bed3,0x722df53,0x722fa9b,0x722fb53,0x7262b7d,0x7262bed,0x7262df5,0x7262fb5,0x7268afb,0x726a2fb,0x726bec5,0x726c57d,0x726c5f5,0x726fa2b,0x726fa8b,0x726fac5, + 0x7289afb,0x7289beb,0x729afb1,0x729beb1,0x72b137d,0x72b7d13,0x72b7d31,0x72be89b,0x72be9b1,0x72bec4d,0x72bed13,0x72bed31,0x72c4df5,0x72df513,0x72df531,0x72fa89b, + 0x72fa9b1,0x72fb135,0x72fb513,0x72fb531,0x7312b7d,0x7312bed,0x7312df5,0x7312fb5,0x7315be9,0x7315f69,0x7316fa9,0x7317da9,0x7344afb,0x7348afb,0x734afb1,0x73512fb, + 0x73522fb,0x7352fb1,0x735f625,0x735f629,0x736257d,0x73625f5,0x7362be9,0x7362fa9,0x737d12b,0x737d22b,0x737d2b1,0x737d44b,0x737d48b,0x737d4b1,0x737d625,0x737d629, + 0x744acdf,0x744adf3,0x744af9b,0x744cadf,0x744d95f,0x744df2b,0x74564df,0x7456f93,0x7457c9b,0x748acdf,0x748adf3,0x748af9b,0x74acdf1,0x74adf31,0x74af9b1,0x74c56f9, + 0x74c8adf,0x74cadf1,0x74d8af9,0x74d915f,0x74d95f1,0x74df159,0x74df22b,0x74df2b1,0x7512cdf,0x7512df3,0x7512f9b,0x75132df,0x751365f,0x75137cb,0x75164df,0x7516f93, + 0x7517c9b,0x7522cdf,0x7522df3,0x7522f9b,0x752cdf1,0x752df31,0x752f9b1,0x75316f9,0x75322df,0x7532df1,0x75362f9,0x753645f,0x75365f1,0x7537c59,0x7537c8b,0x7537cb1, + 0x7589be5,0x758a6f9,0x75914df,0x75937c5,0x75944df,0x7594df1,0x759be25,0x759be29,0x75be265,0x75be299,0x75be453,0x75be4c5,0x75be513,0x75be531,0x75be625,0x75be629, + 0x75f1365,0x75f14d9,0x75f229b,0x75f26c5,0x75f289b,0x75f29b1,0x75f3625,0x75f3629,0x76257cd,0x7625f35,0x7626be5,0x7629af9,0x762be4d,0x762be69,0x762f935,0x762f9a9, + 0x764535f,0x764d15f,0x764d45f,0x764d7c5,0x765135f,0x76535f1,0x7657c4d,0x765f135,0x766895f,0x766915f,0x76695f1,0x766a25f,0x766a45f,0x766a5f1,0x766be25,0x766be29, + 0x76895f3,0x768995f,0x768af93,0x76915f3,0x7695f31,0x7698af9,0x769915f,0x76995f1,0x76a25f3,0x76a265f,0x76a2f93,0x76a45f3,0x76a5f31,0x76a62f9,0x76a645f,0x76a65f1, + 0x76be265,0x76be299,0x76be453,0x76be4c5,0x76be513,0x76be531,0x76be625,0x76be629,0x77c4acd,0x77c4b35,0x77c4cad,0x77c4cb5,0x77c4d65,0x77c5359,0x77c564d,0x77c5669, + 0x77c5699,0x77c5935,0x77c59a9,0x77c5a99,0x77c8a6b,0x77c8ad3,0x77c8b53,0x77c98ad,0x77c98b5,0x77c9a2b,0x77c9a8b,0x77c9ac5,0x77ca26b,0x77ca6b1,0x77cac4d,0x77cad13, + 0x77cad31,0x77cb135,0x77cb513,0x77cb531,0x77cc4ad,0x77cc4b5,0x77cc569,0x77cc5a9,0x77cd12b,0x77cd22b,0x77cd2b1,0x77cd44b,0x77cd48b,0x77cd4b1,0x77cd625,0x77cd629, + 0x7c4acdd,0x7c4aecd,0x7c4b375,0x7c4bb35,0x7c4cadd,0x7c4caed,0x7c4cb75,0x7c4cbb5,0x7c4d765,0x7c4d95d,0x7c4d975,0x7c4dd65,0x7c535d9,0x7c53759,0x7c564dd,0x7c566e9, + 0x7c56e99,0x7c574d9,0x7c5764d,0x7c57669,0x7c57699,0x7c59375,0x7c59ba9,0x7c5ba99,0x7c5d4d9,0x7c5d935,0x7c5d9a9,0x7c5da99,0x7c8a6bb,0x7c8a6eb,0x7c8add3,0x7c8ae9b, + 0x7c8aed3,0x7c8b753,0x7c8ba9b,0x7c8bb53,0x7c98add,0x7c98aed,0x7c98b75,0x7c98bb5,0x7c9a2bb,0x7c9a8bb,0x7c9aec5,0x7c9b15d,0x7c9b175,0x7c9ba2b,0x7c9ba8b,0x7c9bac5, + 0x7ca26bb,0x7ca26eb,0x7ca6bb1,0x7ca6eb1,0x7cac4dd,0x7cadd13,0x7cadd31,0x7cae89b,0x7cae9b1,0x7caec4d,0x7caed13,0x7caed31,0x7cb1375,0x7cb7513,0x7cb7531,0x7cba89b, + 0x7cba9b1,0x7cbb135,0x7cbb513,0x7cbb531,0x7cc4add,0x7cc4aed,0x7cc4b75,0x7cc4bb5,0x7cc56e9,0x7cc5769,0x7cc5ba9,0x7cc5da9,0x7cd12bb,0x7cd22bb,0x7cd2bb1,0x7cd44bb, + 0x7cd48bb,0x7cd4bb1,0x7cd7625,0x7cd7629,0x7cd895d,0x7cd8975,0x7cd8ae9,0x7cd8ba9,0x7cdd12b,0x7cdd22b,0x7cdd2b1,0x7cdd44b,0x7cdd48b,0x7cdd4b1,0x7cdd625,0x7cdd629, + 0x7d12b37,0x7d12b73,0x7d12b9b,0x7d132b7,0x7d13657,0x7d1372b,0x7d15937,0x7d15b93,0x7d15c9b,0x7d22b37,0x7d22b73,0x7d22b9b,0x7d2b371,0x7d2b731,0x7d2b9b1,0x7d315b9, + 0x7d322b7,0x7d32b71,0x7d362b9,0x7d36457,0x7d36571,0x7d37159,0x7d3722b,0x7d372b1,0x7d44b37,0x7d44b73,0x7d44b9b,0x7d44cb7,0x7d44d97,0x7d44dcb,0x7d45937,0x7d45b93, + 0x7d45c9b,0x7d48b37,0x7d48b73,0x7d48b9b,0x7d4b371,0x7d4b731,0x7d4b9b1,0x7d4c5b9,0x7d4c8b7,0x7d4cb71,0x7d4d8b9,0x7d4d917,0x7d4d971,0x7d4dc59,0x7d4dc8b,0x7d4dcb1, + 0x7d626e5,0x7d629b9,0x7d64537,0x7d64dc5,0x7d65137,0x7d65371,0x7d66e25,0x7d66e29,0x7d6e265,0x7d6e299,0x7d6e453,0x7d6e4c5,0x7d6e513,0x7d6e531,0x7d6e625,0x7d6e629, + 0x7d71365,0x7d714d9,0x7d7229b,0x7d726c5,0x7d7289b,0x7d729b1,0x7d73625,0x7d73629,0x7d895cd,0x7d89735,0x7d89ae5,0x7d8a6b9,0x7d8ae4d,0x7d8ae69,0x7d8b935,0x7d8b9a9, + 0x7d914d7,0x7d93457,0x7d93517,0x7d935c5,0x7d944d7,0x7d94d71,0x7d95c4d,0x7d97135,0x7d9a257,0x7d9a457,0x7d9a571,0x7d9a897,0x7d9a917,0x7d9a971,0x7d9ae25,0x7d9ae29, + 0x7da2573,0x7da2657,0x7da2b93,0x7da4573,0x7da5731,0x7da62b9,0x7da6457,0x7da6571,0x7da8973,0x7da8997,0x7da8b93,0x7da9173,0x7da9731,0x7da98b9,0x7da9917,0x7da9971, + 0x7dae265,0x7dae299,0x7dae453,0x7dae4c5,0x7dae513,0x7dae531,0x7dae625,0x7dae629,0x7dc4acd,0x7dc4b35,0x7dc4cad,0x7dc4cb5,0x7dc4d65,0x7dc5359,0x7dc564d,0x7dc5669, + 0x7dc5699,0x7dc5935,0x7dc59a9,0x7dc5a99,0x7dc8a6b,0x7dc8ad3,0x7dc8b53,0x7dc98ad,0x7dc98b5,0x7dc9a2b,0x7dc9a8b,0x7dc9ac5,0x7dca26b,0x7dca6b1,0x7dcac4d,0x7dcad13, + 0x7dcad31,0x7dcb135,0x7dcb513,0x7dcb531,0x7dcc4ad,0x7dcc4b5,0x7dcc569,0x7dcc5a9,0x7dcd12b,0x7dcd22b,0x7dcd2b1,0x7dcd44b,0x7dcd48b,0x7dcd4b1,0x7dcd625,0x7dcd629, +}; + +struct b25_info +b25_info (uint32_t seq) +{ + struct b25_info info = {0}; + uint32_t seen = 0; + + do { + unsigned n = b25_info_get_num_cycles(&info); + unsigned off = b25_info_get_cycle(&info, n - 1U).offset; + uint32_t mask = UINT32_C(1) << off; + for (; (seen & mask); ++off, mask <<= 1); + for (unsigned i = off;;) { + i = ror32(seq, i) & 31U; + uint32_t m = mask ^ (UINT32_C(1) << i); + if (m <= mask) { + if (i != off) + return (struct b25_info){0}; + break; + } + mask = m; + } + if ((seen & mask) || !b25_info_add_cycle(&info, mask)) + return (struct b25_info){0}; + seen ^= mask; + } while (seen != UINT32_MAX); + + return info; +} + +void +b25_info_print (struct b25_info const *const info, + struct b25_id const id) +{ + unsigned const rot = id.rot; + unsigned const idx = id.idx; + uint32_t const seq = ror32(B25[idx], rot); + unsigned const num = b25_info_get_num_cycles(info); + + printf("{0x%08" PRIx32 ", 0x%016" PRIx64 ", {%" PRIu16 ",%u,%u}," + " {%u", seq, info->div, b25_id_to_u16(id), idx, rot, num); + for (unsigned n = 1; ++n < 33U; ) { + unsigned c = b25_info_get_num_period_cycles(info, n); + if (c) + printf(",[%u]=%u", n, c); + } + (void)fputs("}, {", stdout); + for (unsigned n = 0; n < num; ++n) { + b25_loc x = b25_info_get_cycle(info, n); + printf(&",{%u,%u}"[!n], (unsigned)x.offset, + (unsigned)x.period); + } + (void)fputs("}, {", stdout); + for (unsigned n = 0; n < num; ++n) + printf(&",0x%" PRIx32[!n], info->mask[n]); + puts("}},"); +} + +const struct b25_id four8[1] = { + { 6,1439}, +}; + +const struct b25_id two16[119] = { + {29, 3}, {29, 13}, {17, 43}, {19, 51}, {30, 53}, {11, 64}, {29, 67}, {19, 76}, + { 7, 79}, {11, 177}, {31, 208}, {21, 298}, {15, 313}, {31, 320}, {13, 329}, {31, 329}, + {30, 339}, {10, 345}, {28, 352}, { 4, 358}, { 2, 362}, {11, 377}, {31, 387}, {10, 401}, + { 6, 407}, {22, 411}, {30, 430}, {13, 443}, {13, 463}, { 1, 476}, {29, 497}, {28, 507}, + {13, 509}, {29, 551}, { 9, 586}, { 8, 609}, {17, 618}, { 4, 660}, {28, 667}, { 3, 669}, + {22, 691}, {14, 727}, {11, 742}, {11, 759}, { 8, 774}, {18, 802}, { 8, 807}, {30, 857}, + {20, 863}, { 9, 897}, { 9, 902}, {21, 943}, { 2,1004}, { 8,1009}, {30,1023}, {31,1037}, + {13,1041}, {14,1098}, {15,1104}, {20,1124}, {13,1140}, {10,1144}, { 3,1174}, { 5,1191}, + { 5,1210}, {30,1261}, { 6,1262}, {13,1267}, {17,1267}, { 1,1270}, {23,1272}, { 7,1281}, + {20,1312}, {30,1369}, {19,1385}, {14,1391}, { 4,1402}, {19,1410}, {28,1426}, { 2,1441}, + { 8,1444}, { 8,1450}, {11,1473}, {29,1481}, {28,1503}, {13,1528}, {31,1536}, {16,1542}, + {31,1580}, {20,1621}, { 4,1622}, {20,1648}, {24,1648}, {30,1649}, { 7,1652}, { 4,1666}, + { 4,1711}, { 7,1749}, {13,1753}, {13,1769}, { 1,1774}, {18,1802}, { 7,1806}, {16,1809}, + { 1,1833}, {29,1839}, {19,1841}, {19,1893}, {16,1901}, { 5,1932}, {11,1932}, {19,1932}, + { 9,1967}, {19,1967}, {31,1978}, { 1,2003}, {28,2009}, {30,2023}, {29,2037}, +}; + +const struct b25_id one32[1936] = { + { 3, 1}, { 3, 2}, { 8, 3}, { 4, 4}, { 4, 6}, { 1, 7}, { 3, 7}, {21, 7}, + { 9, 8}, { 4, 9}, {30, 9}, {29, 10}, { 8, 11}, {16, 11}, {21, 12}, { 0, 14}, + { 6, 14}, {10, 14}, { 1, 15}, {13, 16}, {29, 16}, { 3, 18}, { 1, 21}, {19, 21}, + {13, 23}, {30, 24}, {13, 26}, {22, 29}, {19, 31}, {17, 32}, {22, 38}, { 1, 40}, + {29, 40}, { 3, 41}, {21, 41}, {29, 41}, {16, 43}, {17, 45}, { 3, 47}, {29, 48}, + {13, 50}, {29, 50}, { 4, 51}, {10, 52}, {22, 52}, {13, 53}, {30, 54}, {19, 56}, + {10, 58}, {13, 59}, {21, 59}, { 8, 61}, {28, 61}, {30, 62}, { 7, 63}, {31, 63}, + { 3, 65}, {17, 65}, {22, 66}, {30, 66}, { 4, 67}, {11, 68}, {19, 68}, {17, 69}, + {21, 69}, { 0, 70}, {13, 72}, {31, 74}, { 0, 76}, {17, 78}, {19, 78}, {29, 78}, + { 2, 80}, { 6, 80}, { 2, 84}, { 4, 85}, { 2, 86}, { 7, 87}, { 0, 91}, { 9, 93}, + { 1, 94}, {29, 94}, { 3, 96}, {11, 97}, { 2, 99}, { 2, 100}, {22, 100}, {29, 101}, + { 8, 102}, {22, 102}, { 8, 105}, { 3, 106}, {11, 106}, {10, 107}, { 4, 112}, {10, 116}, + { 3, 117}, {10, 118}, {11, 119}, {10, 121}, {10, 122}, {30, 122}, { 3, 125}, {11, 126}, + { 2, 127}, { 6, 127}, {10, 127}, {29, 128}, {14, 129}, { 3, 131}, { 7, 132}, {14, 133}, + {30, 133}, { 8, 134}, {10, 134}, { 7, 135}, { 0, 136}, { 8, 136}, {28, 136}, { 6, 137}, + { 0, 138}, {28, 138}, {31, 142}, {29, 143}, { 0, 144}, {14, 144}, { 3, 145}, { 7, 145}, + { 9, 145}, { 6, 146}, { 8, 146}, {21, 147}, { 3, 148}, {10, 150}, {14, 150}, {30, 150}, + {29, 151}, { 8, 153}, {10, 153}, {14, 153}, {10, 154}, {28, 154}, { 1, 156}, {10, 157}, + {28, 157}, {10, 159}, {14, 159}, {30, 160}, { 7, 161}, { 7, 162}, {28, 163}, {28, 164}, + {11, 166}, {15, 166}, { 3, 167}, { 7, 167}, {29, 167}, {18, 169}, { 3, 170}, {29, 170}, + {28, 171}, {30, 171}, {28, 172}, { 8, 173}, {15, 174}, {29, 174}, {22, 177}, { 9, 178}, + {29, 178}, {30, 179}, {28, 181}, { 7, 182}, { 9, 182}, {15, 183}, {19, 183}, {18, 186}, + {30, 186}, {15, 187}, { 1, 188}, {21, 189}, { 2, 191}, {22, 192}, {17, 193}, {31, 193}, + {18, 194}, {22, 194}, { 3, 195}, { 9, 196}, { 2, 197}, {22, 197}, {15, 199}, {19, 199}, + {28, 201}, {17, 202}, { 9, 204}, {17, 204}, { 9, 205}, {18, 207}, { 0, 211}, { 9, 212}, + {17, 212}, {17, 214}, {17, 215}, {21, 217}, { 9, 218}, { 8, 219}, { 2, 221}, {22, 222}, + {22, 223}, {18, 224}, {13, 226}, {22, 227}, { 8, 228}, { 3, 230}, {15, 231}, { 3, 232}, + {17, 232}, {22, 233}, {19, 234}, {22, 235}, { 9, 236}, {21, 236}, { 3, 237}, {13, 240}, + {15, 241}, {29, 241}, {15, 244}, {22, 246}, { 6, 247}, {22, 250}, {13, 251}, { 3, 254}, + { 3, 255}, { 9, 255}, {22, 257}, {30, 257}, {22, 260}, {28, 263}, {31, 265}, {10, 267}, + {22, 267}, {28, 268}, {30, 269}, {28, 273}, {30, 274}, {19, 275}, { 0, 276}, {30, 276}, + {11, 277}, {17, 277}, {31, 277}, { 6, 278}, {28, 278}, {28, 279}, { 8, 281}, {10, 282}, + {17, 284}, { 3, 285}, {13, 285}, {17, 285}, {19, 285}, {11, 287}, { 5, 288}, {17, 291}, + {15, 292}, { 6, 294}, { 6, 296}, {13, 297}, {29, 297}, { 6, 298}, { 8, 300}, { 6, 301}, + {11, 303}, { 6, 305}, {15, 306}, {30, 307}, { 6, 313}, {22, 313}, {15, 314}, {17, 314}, + {30, 315}, { 8, 318}, {10, 318}, {10, 319}, {16, 320}, {13, 321}, {28, 323}, {31, 324}, + { 7, 326}, {31, 326}, { 7, 328}, {13, 328}, {17, 328}, {17, 333}, { 4, 334}, {18, 334}, + {22, 334}, {17, 336}, {30, 337}, {22, 338}, {30, 338}, { 7, 339}, {16, 340}, { 4, 342}, + {30, 342}, {22, 343}, {30, 343}, { 7, 344}, {13, 345}, { 2, 348}, { 9, 350}, {29, 350}, + {30, 351}, {22, 355}, { 4, 356}, {28, 356}, {17, 360}, {29, 360}, { 6, 363}, { 8, 363}, + { 8, 364}, {10, 364}, { 2, 365}, { 8, 365}, { 0, 368}, { 4, 368}, {13, 369}, {31, 370}, + { 8, 372}, {31, 373}, {10, 375}, {30, 375}, {10, 377}, {31, 383}, { 9, 388}, {17, 388}, + { 8, 389}, {16, 390}, {30, 390}, {30, 394}, {10, 395}, {22, 395}, { 6, 397}, {30, 397}, + {13, 401}, {13, 402}, {18, 403}, {11, 404}, {10, 409}, {30, 410}, { 9, 411}, {13, 411}, + { 0, 412}, {13, 413}, { 5, 414}, {11, 416}, {16, 418}, {18, 418}, {28, 418}, {30, 418}, + {29, 419}, { 5, 420}, {11, 420}, {29, 422}, {16, 423}, {15, 425}, { 0, 426}, { 1, 428}, + { 7, 429}, {13, 429}, { 1, 430}, { 1, 432}, { 9, 432}, {13, 432}, { 8, 433}, {10, 433}, + {16, 433}, {13, 436}, { 0, 437}, {10, 437}, {28, 438}, {15, 441}, {19, 441}, {18, 442}, + {10, 443}, {30, 443}, { 8, 446}, {30, 446}, { 5, 448}, {13, 448}, {19, 449}, {15, 450}, + {10, 452}, {30, 452}, { 4, 454}, { 8, 454}, { 2, 456}, {29, 457}, {19, 458}, {13, 460}, + {15, 460}, {28, 462}, { 0, 463}, { 2, 463}, { 4, 463}, {16, 464}, {28, 464}, {15, 466}, + {19, 466}, { 8, 468}, {18, 470}, {13, 472}, {19, 472}, {28, 473}, {15, 474}, {11, 475}, + {19, 475}, {10, 476}, { 6, 477}, { 4, 478}, {30, 479}, { 1, 481}, {11, 481}, {31, 483}, + {10, 484}, {18, 484}, {13, 486}, { 0, 488}, { 6, 488}, {10, 488}, { 2, 489}, { 6, 489}, + { 6, 493}, { 6, 495}, { 6, 496}, { 4, 497}, { 8, 498}, {30, 498}, { 9, 499}, {10, 501}, + {16, 501}, {18, 501}, {11, 502}, {16, 505}, {22, 505}, { 4, 509}, { 9, 511}, {17, 512}, + {20, 513}, {22, 513}, { 8, 514}, {14, 514}, {22, 514}, {10, 516}, { 7, 517}, {11, 519}, + {20, 521}, { 8, 523}, {10, 523}, { 8, 525}, { 1, 528}, {15, 528}, { 7, 531}, {29, 531}, + {22, 532}, { 9, 533}, {17, 533}, {17, 535}, {31, 535}, { 6, 536}, { 7, 538}, { 4, 539}, + {22, 539}, {15, 540}, {29, 540}, { 6, 541}, {31, 544}, {30, 545}, { 8, 546}, {14, 546}, + { 4, 549}, { 8, 550}, { 8, 551}, {30, 551}, {29, 552}, { 3, 553}, { 4, 554}, {28, 554}, + {30, 554}, { 3, 556}, {22, 560}, { 3, 561}, { 4, 562}, {30, 562}, { 9, 563}, {29, 565}, + {31, 565}, { 4, 566}, {20, 566}, {30, 566}, { 1, 568}, { 3, 568}, {21, 568}, {30, 569}, + { 7, 570}, {28, 571}, { 6, 573}, {18, 573}, { 1, 574}, {16, 576}, {29, 577}, {17, 578}, + {10, 579}, {10, 580}, {21, 581}, {31, 581}, { 0, 583}, {16, 583}, {20, 583}, { 9, 584}, + {29, 584}, { 0, 586}, {16, 586}, {11, 587}, {29, 587}, { 3, 591}, { 9, 591}, {20, 592}, + {22, 592}, {11, 594}, {16, 595}, { 6, 596}, {30, 596}, {31, 597}, {17, 598}, {31, 598}, + {20, 600}, {28, 600}, {30, 601}, { 6, 603}, { 0, 608}, {18, 608}, { 3, 609}, { 0, 611}, + {22, 611}, {17, 612}, {29, 612}, {15, 613}, {17, 613}, { 6, 614}, {30, 614}, { 6, 615}, + { 6, 617}, { 8, 617}, { 0, 618}, { 6, 618}, {22, 618}, {22, 619}, { 3, 620}, {29, 620}, + { 7, 621}, { 0, 622}, {10, 622}, { 0, 625}, { 6, 625}, {20, 625}, {31, 627}, {10, 628}, + {30, 632}, {11, 634}, {17, 634}, { 4, 636}, {16, 636}, {20, 636}, { 0, 637}, { 4, 637}, + { 6, 637}, {17, 638}, { 6, 640}, {15, 641}, { 2, 642}, { 4, 642}, { 4, 643}, {20, 643}, + {22, 643}, { 6, 644}, {16, 644}, {31, 646}, { 3, 647}, {29, 648}, { 6, 650}, { 4, 651}, + {14, 651}, {18, 651}, {22, 651}, {31, 652}, {11, 655}, {15, 655}, { 3, 656}, { 4, 657}, + {16, 657}, {14, 659}, { 9, 660}, {11, 660}, {16, 661}, { 0, 663}, {14, 663}, {16, 663}, + { 3, 667}, { 8, 669}, {22, 670}, {15, 674}, {20, 678}, {31, 680}, {31, 681}, {15, 682}, + {28, 683}, { 0, 684}, { 6, 684}, {20, 684}, {18, 685}, {15, 688}, {21, 688}, { 7, 691}, + {16, 693}, {14, 695}, { 0, 698}, {16, 698}, {11, 699}, {15, 699}, { 3, 702}, {15, 702}, + {31, 702}, {17, 704}, { 0, 706}, { 0, 708}, {22, 708}, { 4, 709}, { 3, 710}, {11, 710}, + {14, 711}, {22, 713}, { 7, 715}, {29, 715}, {22, 718}, {14, 719}, {28, 719}, {18, 721}, + { 5, 722}, {31, 722}, { 0, 723}, {28, 723}, { 9, 724}, {31, 725}, {20, 726}, { 7, 727}, + {22, 728}, { 7, 729}, {29, 729}, {20, 730}, {22, 730}, { 7, 733}, { 3, 734}, { 5, 734}, + { 3, 735}, {21, 735}, {15, 736}, { 0, 737}, {16, 737}, {18, 737}, {20, 737}, {28, 738}, + { 4, 740}, {20, 740}, {20, 742}, {11, 744}, { 2, 745}, {14, 745}, {17, 747}, {28, 749}, + { 7, 750}, {15, 751}, {21, 751}, { 7, 753}, { 0, 754}, {18, 754}, { 7, 756}, {17, 757}, + { 7, 758}, { 8, 760}, {17, 762}, {22, 763}, {11, 764}, { 2, 766}, {20, 766}, {11, 768}, + {17, 768}, { 7, 769}, {17, 769}, { 0, 771}, {30, 772}, { 0, 773}, {16, 773}, { 7, 775}, + {17, 775}, {14, 778}, {28, 778}, { 0, 779}, {14, 779}, {11, 781}, {14, 784}, {18, 784}, + {28, 784}, { 3, 785}, { 7, 785}, {11, 785}, { 3, 786}, { 5, 786}, { 9, 787}, { 3, 788}, + {17, 789}, { 3, 790}, { 6, 791}, { 2, 792}, { 6, 792}, {15, 793}, {11, 797}, {15, 797}, + {16, 799}, {30, 801}, { 3, 802}, {11, 802}, {17, 802}, { 2, 803}, {14, 803}, {16, 806}, + { 9, 807}, {21, 807}, {22, 810}, {15, 811}, { 6, 812}, { 9, 813}, {11, 813}, {11, 814}, + { 6, 815}, {16, 815}, {22, 815}, { 2, 816}, {14, 816}, { 6, 817}, { 7, 819}, {21, 819}, + {15, 820}, { 6, 823}, {22, 825}, {17, 827}, { 8, 829}, {16, 829}, {10, 833}, {18, 833}, + {22, 838}, {16, 839}, {18, 840}, {31, 841}, {10, 852}, {11, 853}, {28, 856}, { 2, 858}, + {16, 858}, { 3, 860}, {13, 863}, {17, 863}, { 4, 864}, {22, 864}, {16, 865}, {22, 865}, + {28, 865}, {10, 866}, {22, 866}, {28, 866}, {29, 868}, {14, 869}, {16, 869}, {28, 869}, + { 3, 870}, {15, 870}, {31, 870}, {17, 871}, { 2, 872}, {16, 872}, {29, 875}, { 8, 879}, + {28, 880}, { 1, 882}, {14, 883}, {20, 883}, {22, 883}, {16, 884}, {31, 887}, {15, 889}, + { 3, 892}, {14, 896}, {20, 897}, { 3, 898}, {15, 898}, { 2, 899}, {20, 899}, {30, 899}, + { 9, 901}, {17, 901}, {30, 902}, { 1, 905}, {29, 905}, {11, 909}, { 1, 910}, {15, 910}, + {21, 911}, {13, 912}, {11, 913}, {15, 913}, { 2, 914}, {29, 915}, { 4, 916}, { 4, 917}, + {30, 917}, { 4, 919}, {20, 919}, { 7, 920}, {29, 920}, { 4, 921}, {14, 925}, {20, 928}, + {30, 928}, { 9, 929}, { 5, 930}, { 9, 931}, {17, 931}, { 3, 932}, { 9, 932}, {11, 932}, + {29, 933}, {30, 935}, { 6, 936}, { 1, 937}, {21, 938}, { 4, 939}, {16, 939}, {15, 940}, + {29, 940}, {11, 941}, {15, 941}, {16, 942}, {20, 942}, { 8, 943}, {16, 943}, { 2, 944}, + {22, 944}, { 2, 945}, { 8, 945}, {15, 946}, { 8, 947}, {28, 947}, { 0, 948}, { 4, 948}, + { 6, 949}, { 2, 950}, {13, 951}, { 9, 952}, {14, 954}, {20, 954}, {22, 954}, {21, 955}, + { 0, 956}, { 8, 956}, { 3, 957}, { 9, 957}, { 8, 959}, { 4, 961}, {14, 961}, { 3, 962}, + {21, 964}, { 9, 965}, {13, 965}, {10, 966}, { 6, 967}, {16, 967}, { 9, 968}, {21, 968}, + {10, 969}, {16, 969}, {20, 969}, {11, 971}, {17, 972}, {14, 974}, { 2, 975}, {15, 976}, + {17, 976}, {29, 976}, {20, 977}, { 2, 978}, {30, 978}, {20, 979}, { 2, 980}, {20, 980}, + { 0, 981}, {16, 981}, {28, 981}, {14, 982}, {30, 982}, { 5, 984}, { 9, 984}, { 2, 985}, + { 2, 986}, { 3, 987}, {14, 988}, { 8, 989}, {14, 989}, { 3, 990}, { 9, 990}, { 4, 994}, + {11, 995}, {11, 997}, {15, 997}, { 5, 998}, {11, 998}, {14, 999}, {16,1000}, {20,1000}, + {30,1000}, {15,1001}, {17,1001}, {11,1002}, { 9,1004}, { 0,1006}, { 1,1007}, {17,1007}, + {11,1008}, { 8,1010}, { 4,1011}, {18,1011}, {28,1011}, { 0,1012}, {28,1012}, { 4,1013}, + { 9,1015}, {10,1016}, { 3,1017}, { 7,1017}, {28,1019}, { 4,1020}, { 7,1023}, {13,1023}, + { 7,1024}, {20,1025}, { 8,1027}, {20,1027}, { 6,1028}, {14,1032}, { 5,1036}, {31,1036}, + { 0,1037}, {13,1040}, { 5,1043}, {29,1045}, {31,1047}, { 6,1048}, {20,1048}, {24,1048}, + {17,1049}, {19,1049}, {17,1050}, {31,1050}, {20,1052}, {29,1053}, { 2,1054}, { 2,1055}, + { 6,1056}, { 9,1058}, {19,1059}, { 1,1060}, { 9,1062}, { 7,1064}, { 6,1065}, {10,1065}, + {15,1066}, { 6,1067}, {28,1067}, {17,1068}, {19,1069}, {29,1070}, { 0,1073}, { 2,1073}, + {16,1073}, {16,1074}, {20,1074}, {17,1075}, {31,1075}, {24,1076}, {28,1076}, { 5,1077}, + {29,1077}, {20,1078}, {28,1078}, { 3,1079}, {31,1079}, {17,1080}, {20,1081}, {28,1082}, + {13,1084}, { 4,1087}, { 4,1088}, {10,1088}, {10,1090}, {14,1090}, {10,1092}, {23,1093}, + {31,1093}, { 9,1094}, { 8,1095}, {30,1096}, {23,1098}, {17,1101}, { 0,1102}, { 6,1102}, + {30,1104}, {20,1107}, {24,1109}, {28,1110}, { 5,1111}, {28,1113}, { 5,1114}, { 7,1114}, + { 9,1114}, { 8,1115}, { 0,1117}, {14,1117}, { 0,1120}, { 6,1123}, { 7,1124}, { 2,1125}, + {20,1125}, {10,1128}, { 1,1129}, {19,1129}, {16,1131}, { 8,1133}, {20,1133}, {24,1133}, + { 1,1136}, {10,1137}, {20,1137}, { 4,1140}, {17,1144}, { 0,1145}, { 2,1146}, {16,1146}, + {10,1150}, {14,1150}, {30,1150}, {10,1152}, { 5,1153}, {17,1153}, { 6,1155}, {10,1155}, + {28,1155}, {20,1157}, { 9,1158}, {21,1158}, { 9,1160}, {30,1161}, { 7,1164}, { 7,1167}, + {15,1167}, { 3,1168}, {10,1169}, {16,1169}, { 0,1170}, {16,1170}, {10,1171}, {28,1171}, + { 3,1172}, { 9,1172}, { 9,1175}, { 0,1177}, {28,1177}, { 4,1178}, { 8,1178}, { 8,1179}, + {10,1179}, {14,1179}, {28,1179}, { 3,1182}, { 8,1183}, { 6,1184}, {28,1184}, {28,1186}, + {30,1186}, {16,1188}, {28,1188}, { 3,1189}, {31,1190}, {14,1191}, {28,1191}, {16,1192}, + { 5,1193}, { 3,1194}, { 5,1194}, { 9,1194}, { 7,1198}, {31,1199}, { 8,1200}, {16,1200}, + { 0,1201}, { 8,1201}, {16,1201}, {30,1201}, { 3,1202}, { 9,1202}, { 7,1205}, { 7,1206}, + { 3,1207}, {31,1207}, { 6,1208}, {16,1208}, {28,1208}, {10,1209}, {14,1209}, {16,1209}, + {28,1210}, { 3,1211}, { 5,1211}, { 8,1213}, {14,1213}, { 8,1215}, {14,1216}, {30,1216}, + {11,1218}, {30,1221}, {23,1222}, { 3,1223}, {19,1223}, {23,1223}, { 4,1226}, { 8,1227}, + {20,1227}, {13,1228}, { 4,1229}, {15,1233}, {23,1233}, { 8,1236}, { 4,1237}, { 8,1237}, + {29,1239}, {31,1240}, { 0,1241}, {20,1242}, {14,1243}, { 3,1245}, {14,1246}, { 4,1248}, + { 6,1248}, {15,1249}, {29,1249}, {16,1250}, { 8,1251}, {16,1251}, {30,1251}, { 8,1252}, + { 6,1253}, {24,1254}, {19,1257}, {30,1258}, {19,1259}, {31,1261}, {31,1262}, {31,1263}, + {21,1264}, {24,1266}, {30,1267}, { 6,1270}, {20,1270}, { 6,1272}, {17,1273}, {29,1273}, + { 1,1274}, {19,1277}, { 2,1278}, { 6,1278}, {13,1279}, {17,1279}, { 6,1281}, {20,1281}, + { 4,1282}, {24,1282}, {21,1283}, {23,1283}, {13,1284}, {15,1284}, {21,1285}, {30,1286}, + { 8,1288}, {20,1288}, { 7,1291}, {29,1295}, {19,1299}, {13,1301}, { 5,1302}, { 3,1306}, + {23,1306}, { 6,1309}, {24,1310}, {24,1311}, {20,1314}, {11,1315}, {17,1315}, {19,1318}, + { 4,1319}, {14,1321}, { 8,1322}, { 1,1323}, { 5,1325}, {19,1325}, {31,1325}, { 3,1326}, + { 4,1327}, { 8,1328}, {11,1329}, {15,1329}, { 6,1330}, {14,1330}, {24,1330}, {11,1332}, + {16,1333}, {20,1333}, { 5,1334}, {17,1334}, { 6,1335}, {16,1336}, { 1,1337}, {11,1337}, + {29,1337}, {11,1338}, {31,1338}, {11,1341}, {13,1342}, {17,1342}, {19,1342}, {16,1343}, + {13,1344}, { 2,1345}, {16,1347}, {20,1347}, {16,1348}, {24,1348}, {31,1349}, { 8,1350}, + { 6,1353}, {16,1353}, {29,1355}, {21,1356}, { 6,1358}, { 7,1359}, {29,1359}, { 4,1360}, + {16,1360}, {24,1360}, { 0,1364}, { 2,1364}, {14,1364}, {16,1364}, {13,1365}, { 7,1367}, + { 2,1368}, {23,1369}, { 3,1370}, { 2,1371}, { 4,1372}, { 6,1372}, {24,1372}, {20,1373}, + {30,1373}, {16,1374}, { 9,1375}, { 3,1376}, { 3,1377}, { 3,1379}, {24,1381}, {19,1384}, + { 9,1387}, {11,1387}, { 7,1388}, { 9,1388}, {19,1388}, {29,1388}, { 2,1390}, {30,1390}, + {29,1391}, { 9,1393}, {19,1393}, {23,1393}, {29,1393}, { 6,1398}, {14,1398}, {24,1398}, + {14,1400}, {23,1401}, { 7,1404}, {23,1405}, { 4,1406}, {13,1409}, {14,1410}, {14,1411}, + {20,1411}, {23,1413}, {11,1414}, {21,1414}, {11,1416}, { 6,1417}, {14,1417}, {20,1417}, + {30,1417}, {30,1418}, {11,1419}, {23,1420}, {29,1420}, { 4,1421}, {28,1421}, { 0,1422}, + { 4,1422}, {24,1422}, { 0,1423}, {14,1423}, {13,1424}, {17,1424}, {19,1426}, {16,1427}, + {28,1427}, { 2,1428}, {29,1429}, {29,1430}, { 2,1431}, { 6,1431}, { 7,1434}, {19,1434}, + {15,1435}, {19,1435}, {23,1435}, { 6,1436}, {28,1436}, {24,1437}, { 5,1439}, {19,1441}, + { 2,1443}, { 7,1444}, {17,1445}, {29,1445}, { 8,1446}, {16,1446}, {28,1447}, { 8,1449}, + { 3,1450}, { 8,1451}, { 1,1453}, { 3,1453}, { 7,1454}, {29,1456}, {14,1458}, {28,1462}, + {14,1464}, { 7,1466}, {11,1466}, {23,1466}, { 8,1469}, { 8,1471}, {20,1471}, {14,1473}, + { 0,1475}, { 8,1476}, {23,1477}, {15,1478}, {17,1478}, {19,1478}, {11,1480}, {13,1480}, + {17,1480}, {31,1480}, { 8,1481}, { 0,1482}, {19,1483}, { 9,1484}, {23,1484}, {31,1484}, + { 8,1486}, {29,1487}, {31,1487}, {17,1488}, { 4,1489}, { 6,1489}, { 8,1490}, {19,1491}, + { 4,1492}, { 6,1492}, {19,1494}, { 9,1495}, {15,1495}, {19,1495}, {28,1496}, {16,1498}, + {14,1501}, { 6,1502}, {28,1502}, { 6,1504}, { 1,1505}, {29,1505}, {11,1507}, { 2,1508}, + {16,1509}, {19,1512}, {14,1513}, {24,1515}, { 4,1516}, {16,1516}, {13,1518}, {23,1518}, + { 3,1519}, {31,1519}, {16,1520}, {28,1520}, {15,1521}, {21,1522}, {23,1522}, {15,1523}, + {19,1523}, {16,1526}, {16,1527}, { 6,1528}, {15,1529}, {13,1530}, {19,1530}, {21,1530}, + {16,1532}, { 7,1534}, { 9,1534}, {13,1534}, {19,1534}, { 8,1535}, {31,1537}, {18,1540}, + {30,1540}, {31,1541}, {23,1542}, {16,1543}, {24,1543}, {20,1544}, {24,1546}, { 5,1548}, + {11,1548}, { 8,1549}, {14,1549}, {14,1551}, {18,1552}, {24,1553}, {24,1554}, { 1,1555}, + { 5,1555}, {11,1556}, {18,1557}, {20,1557}, {11,1558}, {19,1561}, {23,1561}, { 2,1562}, + {10,1563}, { 1,1564}, {29,1564}, {30,1566}, {19,1568}, {29,1568}, { 1,1570}, {13,1570}, + {19,1570}, {21,1570}, { 4,1571}, { 8,1571}, {20,1574}, {23,1575}, { 3,1577}, { 8,1580}, + { 6,1582}, { 1,1585}, {19,1586}, { 4,1587}, { 6,1587}, {30,1587}, {19,1588}, { 3,1591}, + {13,1591}, {19,1591}, { 4,1592}, { 1,1595}, { 3,1595}, { 7,1595}, {23,1595}, {18,1596}, + { 4,1597}, {24,1599}, {31,1601}, { 3,1602}, {11,1602}, { 0,1603}, {10,1603}, {20,1603}, + {14,1605}, {30,1605}, {24,1606}, { 3,1607}, { 5,1607}, {11,1608}, {30,1609}, { 5,1611}, + {19,1611}, { 3,1613}, {15,1613}, {29,1613}, { 7,1614}, { 3,1615}, {29,1615}, {10,1616}, + {16,1617}, {30,1617}, {31,1621}, {16,1623}, {20,1623}, {30,1623}, { 3,1624}, { 4,1626}, + { 8,1626}, {10,1626}, {18,1626}, {20,1626}, {30,1626}, {31,1631}, {16,1632}, {24,1632}, + {21,1633}, { 3,1634}, {13,1634}, {28,1635}, { 3,1636}, { 6,1639}, {10,1639}, {20,1642}, + {28,1645}, { 2,1646}, { 4,1646}, {14,1646}, {30,1646}, { 3,1648}, {21,1648}, {29,1648}, + {23,1649}, {23,1650}, { 8,1651}, {14,1652}, { 6,1654}, { 3,1656}, {29,1656}, { 3,1659}, + {19,1659}, {16,1660}, { 0,1662}, {16,1662}, {28,1662}, {23,1663}, {10,1664}, { 1,1667}, + { 3,1667}, {13,1667}, {23,1667}, {29,1667}, { 4,1668}, { 5,1670}, { 6,1671}, {10,1672}, + {14,1672}, {28,1672}, { 0,1673}, {18,1673}, {28,1674}, {19,1675}, {15,1678}, {23,1679}, + { 3,1681}, {19,1681}, {21,1684}, { 4,1685}, {14,1685}, { 3,1686}, {13,1686}, {29,1686}, + { 7,1690}, {11,1690}, {16,1691}, { 2,1692}, {16,1692}, { 7,1693}, {16,1694}, { 7,1695}, + {19,1695}, {10,1697}, {14,1697}, {19,1699}, {23,1699}, {19,1700}, { 6,1702}, {19,1703}, + {31,1704}, { 4,1705}, {24,1705}, { 3,1706}, {23,1708}, {29,1708}, { 8,1710}, {20,1710}, + {31,1711}, { 3,1712}, {23,1712}, {20,1715}, { 4,1716}, {16,1716}, {28,1716}, {13,1717}, + { 4,1718}, {24,1718}, { 0,1720}, {29,1721}, { 0,1724}, {28,1724}, { 0,1725}, {16,1725}, + {28,1726}, {29,1727}, { 7,1728}, {21,1728}, {23,1728}, {10,1729}, {15,1730}, { 4,1731}, + {20,1732}, {28,1732}, { 0,1733}, {15,1737}, { 4,1738}, {20,1738}, {31,1739}, { 3,1740}, + {13,1740}, {18,1743}, {20,1743}, { 8,1744}, {20,1744}, {10,1746}, { 7,1747}, {20,1749}, + { 4,1751}, { 3,1752}, {13,1752}, {16,1753}, {28,1753}, {16,1755}, {16,1756}, {28,1756}, + {31,1757}, { 2,1762}, {10,1763}, { 1,1764}, { 3,1764}, {23,1764}, {16,1766}, { 2,1767}, + { 5,1768}, {23,1770}, {16,1771}, {31,1773}, {18,1774}, {30,1774}, { 6,1775}, {31,1777}, + { 2,1780}, {14,1784}, { 1,1785}, { 5,1786}, { 1,1787}, {23,1787}, { 6,1788}, {10,1788}, + {10,1789}, {28,1789}, { 5,1790}, { 2,1794}, {18,1794}, { 9,1796}, {31,1796}, {28,1797}, + {14,1798}, {16,1798}, { 5,1799}, {18,1803}, {16,1804}, { 0,1806}, {16,1806}, {18,1806}, + { 7,1807}, { 3,1808}, {31,1808}, { 9,1809}, { 4,1811}, { 4,1812}, { 6,1812}, {30,1812}, + { 6,1814}, {30,1814}, {10,1816}, { 9,1818}, { 3,1819}, { 9,1819}, {19,1821}, { 1,1823}, + {28,1824}, {16,1825}, {20,1825}, {28,1825}, { 7,1827}, { 9,1827}, { 2,1828}, {14,1831}, + {19,1834}, { 4,1835}, {15,1840}, { 4,1841}, {16,1841}, { 2,1842}, { 4,1842}, {28,1842}, + { 1,1843}, { 7,1843}, {31,1843}, { 2,1844}, {18,1844}, { 1,1848}, { 7,1848}, {11,1850}, + { 4,1851}, {31,1852}, {11,1853}, { 2,1854}, {10,1854}, {20,1854}, {13,1855}, { 4,1856}, + {14,1856}, {14,1863}, {13,1868}, { 2,1870}, {30,1870}, {13,1872}, {19,1873}, { 4,1874}, + {28,1874}, { 5,1875}, { 7,1875}, { 2,1877}, {18,1877}, { 1,1881}, {19,1881}, { 1,1882}, + { 5,1882}, { 6,1884}, {14,1884}, {16,1884}, {28,1885}, { 7,1886}, {28,1887}, { 7,1888}, + {15,1888}, {14,1889}, {28,1889}, {14,1890}, {11,1891}, { 0,1892}, {20,1892}, {28,1892}, + {16,1893}, { 7,1895}, {14,1897}, { 1,1898}, { 1,1899}, { 0,1900}, { 2,1900}, {28,1900}, + {13,1901}, {19,1901}, {18,1903}, { 4,1904}, {10,1904}, {14,1905}, {28,1905}, { 4,1906}, + {10,1906}, { 7,1907}, {31,1909}, {11,1910}, {15,1910}, {19,1910}, { 7,1911}, {14,1912}, + {19,1914}, {21,1914}, { 3,1918}, { 3,1921}, {15,1921}, {31,1921}, { 0,1922}, { 1,1928}, + {31,1928}, { 4,1931}, { 0,1932}, {18,1933}, {10,1934}, {24,1934}, {30,1935}, { 5,1936}, + {13,1936}, {30,1937}, {19,1938}, {31,1938}, {28,1939}, {30,1939}, { 0,1940}, {10,1940}, + {20,1940}, {24,1940}, {11,1941}, { 0,1942}, {19,1943}, { 3,1945}, { 9,1945}, {28,1946}, + {31,1948}, {16,1949}, {13,1950}, {15,1954}, {24,1956}, { 7,1957}, {10,1959}, {15,1961}, + { 0,1962}, { 6,1962}, { 9,1963}, {15,1963}, { 6,1964}, { 1,1969}, {21,1972}, {14,1973}, + { 1,1974}, { 4,1975}, {20,1975}, {15,1976}, { 2,1978}, {16,1978}, {20,1979}, { 7,1981}, + { 1,1982}, { 7,1982}, { 9,1982}, { 4,1983}, { 3,1985}, {14,1986}, {11,1987}, {11,1989}, + {19,1989}, {19,1992}, {10,1993}, { 3,1994}, { 6,1996}, {14,1996}, {16,1996}, { 7,2000}, + { 1,2002}, {19,2002}, { 6,2003}, {18,2003}, {20,2003}, { 1,2005}, { 0,2006}, {18,2006}, + { 7,2007}, {11,2007}, {13,2009}, { 4,2010}, { 5,2011}, { 4,2013}, {14,2013}, {28,2013}, + { 4,2015}, { 6,2015}, {14,2017}, {16,2017}, { 5,2018}, {19,2018}, {31,2018}, { 1,2022}, + { 1,2023}, { 7,2023}, {31,2023}, { 6,2024}, { 1,2025}, { 1,2028}, {11,2028}, {18,2029}, + { 7,2031}, { 6,2032}, { 4,2034}, { 6,2037}, {14,2037}, {28,2037}, {19,2040}, {14,2041}, + { 6,2042}, {30,2042}, { 4,2043}, {13,2044}, {19,2044}, { 1,2045}, {19,2045}, { 5,2047}, +}; diff --git a/src/b25.h b/src/b25.h new file mode 100644 index 0000000..9e898af --- /dev/null +++ b/src/b25.h @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/** @file b25.h + * @brief 32-bit binary De Bruijn sequences + * @author Juuso Alasuutari + */ +#ifndef LIBCANTH_SRC_B25_H_ +#define LIBCANTH_SRC_B25_H_ + +#include + +#include "bits.h" + +#if defined _MSC_VER || defined __INTELLISENSE__ +typedef uint16_t u16bit; +typedef struct uint5 { + unsigned value : 5; +} uint5; +#define uint5(x) (uint5){.value=(x)} +#define uint5_get(x) (x).value +#else +typedef unsigned _BitInt(16) u16bit; +typedef unsigned _BitInt(5) uint5; +#define uint5(x) (uint5)(x) +#define uint5_get(x) x +#endif + +extern const uint32_t B25[2048]; + +struct b25_id { + u16bit rot : 5; + u16bit idx : 11; +}; +_Static_assert(sizeof(struct b25_id) == sizeof(uint16_t), + "width of b25_id must be 16 bits"); + +extern const struct b25_id four8[ 1]; +extern const struct b25_id two16[ 119]; +extern const struct b25_id one32[1936]; + +static const_inline uint32_t +u10x3_count (uint32_t const x) +{ + return x & UINT32_C(3); +} + +typedef struct b25_loc { + u16bit offset : 5; + u16bit period : 6; + u16bit unused : 5; +} b25_loc; + +_Static_assert(sizeof(b25_loc) == sizeof(uint16_t), + "width of b25_loc must be 16 bits"); + +struct b25_info { + uint64_t div; + uint32_t loc[3]; + uint32_t mask[9]; +}; + +static const_inline unsigned +b25_div_offset (uint5 const i) +{ + unsigned const k = uint5_get(i) >> 3U; + return (uint5_get(i) & 7U) + * (uint8_t[]){3,2,1,1}[k] + + (uint8_t[]){8,32,48,56}[k]; +} + +static const_inline uint64_t +b25_div_mask (uint5 const i) +{ + return (uint8_t[]){7,3,1,1}[uint5_get(i) >> 3U]; +} + +static force_inline void +u10x3_append (uint32_t *const dst, + uint5 const lo5, + uint5 const hi5) +{ + uint32_t n = u10x3_count(*dst); + if (n < 3U) { + n = n * 10U + 2U; + *dst += ((uint32_t)uint5_get(hi5) << 5U | uint5_get(lo5)) << n + | UINT32_C(1); + } +} + +static force_inline unsigned +b25_div_get (uint64_t const div, + uint5 const i) +{ + return (unsigned)(div >> b25_div_offset(i) & b25_div_mask(i)); +} + +static const_inline unsigned +b25_div_get_total (uint64_t const div) +{ + return div & UINT64_C(15); +} + +static const_inline unsigned +b25_div_get_unique (uint64_t const div) +{ + return (div >> 4U) & UINT64_C(15); +} + +static force_inline unsigned +b25_info_get_num_cycles (struct b25_info const *const src) +{ + return b25_div_get_total(src->div) - b25_div_get(src->div, uint5(0)); +} + +extern b25_loc +b25_info_get_cycle (struct b25_info const *src, + unsigned i); + +static force_inline unsigned +b25_info_get_num_period_cycles (struct b25_info const *const src, + unsigned const prd) +{ + if (!prd || prd > 32U) + return 0; + return b25_div_get(src->div, uint5(prd - 1U)); +} + +static const_inline struct b25_id +b25_id (uint16_t id) +{ + return (struct b25_id){ + .rot = id & UINT16_C(0x01f), + .idx = id >> 5U, + }; +} + +static const_inline uint32_t +b25 (struct b25_id const id) +{ + return ror32(B25[id.idx], id.rot); +} + +static const_inline uint16_t +b25_id_to_u16 (struct b25_id const id) +{ + return (uint16_t)((uint16_t)id.idx << 5U | id.rot); +} + +extern struct b25_info +b25_info (uint32_t seq); + +static force_inline struct b25_info +b25_info_by_id (struct b25_id const id) +{ + return b25_info(ror32(B25[id.idx], id.rot)); +} + +extern void +b25_info_print (struct b25_info const *info, + struct b25_id id); + +#endif /* LIBCANTH_SRC_B25_H_ */ diff --git a/src/b26.c b/src/b26.c new file mode 100644 index 0000000..a1d99cc --- /dev/null +++ b/src/b26.c @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/** @file b26.c + * @brief 64-bit binary De Bruijn sequences + * @author Juuso Alasuutari + */ + +#include + +#include "b26.h" +#include "file.h" + +static int +b26_cmp (void const *pkey, + void const *pseq) +{ +#if UINTPTR_MAX >= UINT64_MAX + uint64_t key = (uint64_t)pkey; +#else + uint64_t key = *(uint64_t const *)pkey; +#endif + uint64_t seq = *(uint64_t const *)pseq; + return (key > seq) - (key < seq); +} + +static int64_t +b26_find (uint64_t const seq, + uint64_t const *const arr) +{ + if (!arr) + return -1; + +#if UINTPTR_MAX >= UINT64_MAX + void const *k = (void const *)seq; +#else + void const *k = (void const *)&seq; +#endif + uint64_t const *ret = bsearch(k, arr, (size_t)67108864U, + sizeof *arr, b26_cmp); + if (!ret) + return -1; + + return (int64_t)(ptrdiff_t)(ret - arr); +} + +uint64_t +is_b26 (uint64_t const x, + unsigned *const rot) +{ + uint64_t y = x << 6U | x >> (64U - 6U); + uint64_t m = 0U; + unsigned r = 0U; + unsigned n = 0U; + + do { + y >>= 1U; + uint64_t b = UINT64_C(1) << (y & 63U); + uint64_t m_ = m; + m |= b; + if (m == m_) + return 0U; + ++n; + if (b == 1U) + r = n & 63U; + } while (n < 6U); + + y = x; + do { + y >>= 1U; + uint64_t b = UINT64_C(1) << (y & 63U); + uint64_t m_ = m; + m |= b; + if (m == m_) + return 0U; + ++n; + if (b == 1U) + r = n & 63U; + } while (m != UINT64_MAX); + + *rot = r; + return ror64(x, r); +} + +uint64_t +b26_identify (uint64_t x, + struct b26_id *const id, + uint64_t const *const arr) +{ + unsigned rot = 0; + x = is_b26(x, &rot); + if (x) { + int64_t i = b26_find(x, arr); + if (i < 0) + return 0U; + *id = (struct b26_id){ + .rot = rot, + .idx = (uint32_t)i, + }; + } + return x; +} + +static bool +b26_array_is_valid (uint64_t const *const arr) +{ + uint64_t prev = 0U; + uint64_t i = 0U; + do { + uint64_t seq = arr[i]; + unsigned rot; + if (seq <= prev || seq != is_b26(seq, &rot)) + return false; + } while (++i < 67108864U); + return true; +} + +uint64_t * +b26_load (char const *const path, + bool const validate) +{ + struct file_in f = file_in(path, nullptr, + 67108864U * 8U, + FILE_IN_EXACT); + uint64_t *a = (uint64_t *)(void *)f.data; + + if (!file_in_error(&f) && + (!validate || b26_array_is_valid(a))) + return a; + + file_in_fini(&f); + return nullptr; +} diff --git a/src/b26.h b/src/b26.h new file mode 100644 index 0000000..1df2f0e --- /dev/null +++ b/src/b26.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/** @file b26.h + * @brief 64-bit binary De Bruijn sequences + * @author Juuso Alasuutari + */ +#ifndef LIBCANTH_SRC_B26_H_ +#define LIBCANTH_SRC_B26_H_ + +#include "bits.h" + +#if defined _MSC_VER || defined __INTELLISENSE__ +typedef uint32_t u32bit; +typedef struct uint6 { + unsigned value : 6; +} uint6; +#define uint6(x) (uint6){.value=(x)} +#define uint6_get(x) (x).value +#else +typedef unsigned _BitInt(32) u32bit; +typedef unsigned _BitInt(6) uint6; +#define uint6(x) (uint6)(x) +#define uint6_get(x) x +#endif + +struct b26_id { + u32bit rot : 6; + u32bit idx : 26; +}; +_Static_assert(sizeof(struct b26_id) == sizeof(uint32_t), + "width of b26_id must be 32 bits"); + +static const_inline struct b26_id +b26_id (uint32_t id) +{ + return (struct b26_id){ + .rot = id & UINT32_C(0x03f), + .idx = id >> 6U, + }; +} + +static const_inline uint32_t +b26_id_to_u32 (struct b26_id const id) +{ + return (uint32_t)((uint32_t)id.idx << 6U | id.rot); +} + +extern uint64_t * +b26_load (char const *path, + bool validate); + +extern uint64_t +b26_identify (uint64_t x, + struct b26_id *id, + uint64_t const *arr); + +extern uint64_t +is_b26 (uint64_t const x, + unsigned *const rot); + +#endif /* LIBCANTH_SRC_B26_H_ */ diff --git a/src/bits.h b/src/bits.h new file mode 100644 index 0000000..458bd7f --- /dev/null +++ b/src/bits.h @@ -0,0 +1,224 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/** @file bits.h + * @brief Bitwise operations + * @author Juuso Alasuutari + */ +#ifndef LIBCANTH_SRC_BITS_H_ +#define LIBCANTH_SRC_BITS_H_ + +#include "util.h" + +#include +#include + +#ifndef _MSC_VER +# define BITINT_C(n) n##WB +#else +# define BITINT_C(n) n +#endif + +#ifdef _MSC_VER +# include + +# define return_run1(s,x) \ + unsigned long pos = 0; \ + return run1_##s(&pos,x) + +# define return_run0(s,x) \ + unsigned long pos = 0; \ + return run0_##s(&pos,x) + +# define run1_msb(p,x) (\ + bs_(Reverse,x) \ + (p,cmpl(x)) \ + ? (unsigned)CHAR_BIT *\ + (unsigned)sizeof (x)\ + - 1U -(unsigned)*(p)\ + : (unsigned)CHAR_BIT *\ + (unsigned)sizeof (x)) + +# define run0_msb(p,x) (\ + bs_(Reverse,x)(p,x) \ + ? (unsigned)CHAR_BIT *\ + (unsigned)sizeof (x)\ + - 1U -(unsigned)*(p)\ + : (unsigned)CHAR_BIT *\ + (unsigned)sizeof (x)) + +# define run1_lsb(p,x) (\ + bs_(Forward,x) \ + (p,cmpl(x)) \ + ? (unsigned)*(p) \ + : (unsigned)CHAR_BIT *\ + (unsigned)sizeof (x)) + +# define run0_lsb(p,x) (\ + bs_(Forward,x)(p,x) \ + ? (unsigned)*(p) \ + : (unsigned)CHAR_BIT *\ + (unsigned)sizeof (x)) + +# define bs_(f,x) _Generic((x) \ + , uint32_t: _BitScan##f \ + , uint64_t: _BitScan##f##64) + +pragma_msvc(intrinsic(_BitScanForward)) +pragma_msvc(intrinsic(_BitScanReverse)) +pragma_msvc(intrinsic(_BitScanForward64)) +pragma_msvc(intrinsic(_BitScanReverse64)) + +#else // _MSC_VER +# define return_run1(s,x) \ + return (max_hamming(x) \ + ? (unsigned)CHAR_BIT* \ + (unsigned)sizeof(x) \ + : (unsigned)cz_##s(x) \ + (cmpl(x))) + +# define return_run0(s,x) \ + return (!(x) \ + ? (unsigned)CHAR_BIT* \ + (unsigned)sizeof(x) \ + : (unsigned)cz_##s(x)(x)) + +# define max_hamming(x) ( \ + (x) == _Generic( \ + (x), unsigned long: ~0UL, \ + long: ~0L, unsigned: ~0U, \ + int: ~0, long long: ~0LL, \ + unsigned long long: ~0ULL)) + +# define cz_msb(x) cz_(l, x) +# define cz_lsb(x) cz_(t, x) + +# define cz_(y, x) _Generic((x) \ + ,long long int:__builtin_c##y##zll, unsigned:__builtin_c##y##z\ + ,long int:__builtin_c##y##zl, unsigned long:__builtin_c##y##zl\ + ,int:__builtin_c##y##z, unsigned long long:__builtin_c##y##zll) +#endif // _MSC_VER + +#define cmpl(x) _Generic((x) \ + , int: (unsigned)~(x) \ + , long: (unsigned long)~(x) \ + , default: ~(x), long long: \ + (unsigned long long)~(x)) + +static force_inline unsigned +u32_count_msb_1 (uint32_t x) +{ + return_run1(msb, x); +} + +static force_inline unsigned +u32_count_msb_0 (uint32_t x) +{ + return_run0(msb, x); +} + +static force_inline unsigned +u32_count_lsb_1 (uint32_t x) +{ + return_run1(lsb, x); +} + +static force_inline unsigned +u32_count_lsb_0 (uint32_t x) +{ + return_run0(lsb, x); +} + +static force_inline unsigned +u64_count_msb_1 (uint64_t x) +{ + return_run1(msb, x); +} + +static force_inline unsigned +u64_count_msb_0 (uint64_t x) +{ + return_run0(msb, x); +} + +static force_inline unsigned +u64_count_lsb_1 (uint64_t x) +{ + return_run1(lsb, x); +} + +static force_inline unsigned +u64_count_lsb_0 (uint64_t x) +{ + return_run0(lsb, x); +} + +#undef cmpl +#undef return_run0 +#undef return_run1 + +#ifdef _MSC_VER +# undef bs_ +# undef run0_lsb +# undef run1_lsb +# undef run0_msb +# undef run1_msb +#else // _MSC_VER +# undef cz_ +# undef cz_lsb +# undef cz_msb +# undef max_hamming +#endif // _MSC_VER + +#define count_msb_1(x) _Generic((x) \ + ,uint32_t:u32_count_msb_1(x) \ + ,uint64_t:u64_count_msb_1(x) \ + ,int32_t:u32_count_msb_1((uint32_t)(x))\ + ,int64_t:u64_count_msb_1((uint64_t)(x))) + +#define count_msb_0(x) _Generic((x) \ + ,uint32_t:u32_count_msb_0(x) \ + ,uint64_t:u64_count_msb_0(x) \ + ,int32_t:u32_count_msb_0((uint32_t)(x))\ + ,int64_t:u64_count_msb_0((uint64_t)(x))) + +#define count_lsb_1(x) _Generic((x) \ + ,uint32_t:u32_count_lsb_1(x) \ + ,uint64_t:u64_count_lsb_1(x) \ + ,int32_t:u32_count_lsb_1((uint32_t)(x))\ + ,int64_t:u64_count_lsb_1((uint64_t)(x))) + +#define count_lsb_0(x) _Generic((x) \ + ,uint32_t:u32_count_lsb_0(x) \ + ,uint64_t:u64_count_lsb_0(x) \ + ,int32_t:u32_count_lsb_0((uint32_t)(x))\ + ,int64_t:u64_count_lsb_0((uint64_t)(x))) + +#define define_rotators(N) \ +static const_inline uint##N##_t rol##N (uint##N##_t x, unsigned n) \ +{ \ + return (n&=N##U-1U) ? (uint##N##_t)(x<>(N##U-n)) : x; \ +} \ +static const_inline uint##N##_t ror##N (uint##N##_t x, unsigned n) \ +{ \ + return (n&=N##U-1U) ? (uint##N##_t)(x>>n|x<<(N##U-n)) : x; \ +} + +define_rotators(8) +define_rotators(16) +define_rotators(32) +define_rotators(64) + +#undef define_rotators + +#define rol(x, n) (typeof(x)) \ + _Generic((x), uint8_t: rol8 , int8_t: rol8 \ + , uint16_t:rol16, int16_t:rol16 \ + , uint32_t:rol32, int32_t:rol32 \ + , uint64_t:rol64, int64_t:rol64)((uint_type(x))(x),(n)) + +#define ror(x, n) (typeof(x)) \ + _Generic((x), uint8_t: ror8 , int8_t: ror8 \ + , uint16_t:ror16, int16_t:ror16 \ + , uint32_t:ror32, int32_t:ror32 \ + , uint64_t:ror64, int64_t:ror64)((uint_type(x))(x),(n)) + +#endif /* LIBCANTH_SRC_BITS_H_ */ diff --git a/src/debruijn25.c b/src/debruijn25.c new file mode 100644 index 0000000..eb74b97 --- /dev/null +++ b/src/debruijn25.c @@ -0,0 +1,349 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "b25.h" +#include "b26.h" + +#define foreach_prop(it, tab) \ + for (struct prop *o_ = (it), *it_ = o_; \ + it_; (it) = next_prop((tab), it_), \ + it_ = (it) != o_ ? (it) : nullptr) + +struct prop { + char *name; +}; + +struct ptab { + uint32_t m; + struct prop p[32]; +}; + +struct perm { + u16bit rot : 5; + u16bit seq : 11; +}; + +static force_inline size_t +mapping (struct ptab *tab, + unsigned idx) +{ + return (size_t)(ror32(tab->m, idx) & UINT32_C(31)); +} + +static struct prop * +property (struct ptab *tab, + unsigned idx) +{ + return &tab->p[mapping(tab, idx)]; +} + +static force_inline size_t +next_idx (struct ptab *tab, + struct prop *prop) +{ + ptrdiff_t i = (ptrdiff_t)(prop - &tab->p[0]); + return mapping(tab, (unsigned)i); +} + +static struct prop * +next_prop (struct ptab *tab, + struct prop *prop) +{ + return property(tab, next_idx(tab, prop)); +} + +static const_inline struct perm +perm (uint16_t id) +{ + return (struct perm){ + .rot = id & UINT16_C(0x01f), + .seq = id >> 5U, + }; +} + +static const_inline uint32_t +perm_map (struct perm p) +{ + return ror32(B25[p.seq], p.rot); +} + +static const_inline struct ptab +ptab (uint16_t id) +{ + return (struct ptab){ + .m = perm_map(perm(id)) + }; +} + +static int alt_main(int c, char **v); + +int main (int argc, + char **argv) +{ + return alt_main(argc, argv); + struct ptab props = ptab(70); + + struct prop *p = property(&props, 0); + int k = 1; + foreach_prop (p, &props) { + if (k >= argc) + break; + p->name = argv[k++]; + } + + p = property(&props, 0); + k = 0; + foreach_prop (p, &props) { + if (!p->name) + break; + printf(&", \"%s\""[!k++ << 1], p->name); + } + if (k) + putchar('\n'); + + for (size_t i = 0; i < array_size(props.p); ++i) { + char const *name = props.p[i].name; + if (name) + printf("%2zu \"%s\"\n", i, name); + } +} + +struct bag { + uint32_t seq; + uint8_t head[2]; + uint8_t iter[2]; + char const *ptr[32]; +}; + +static const uint64_t BAG_DBSEQ[56] = { + 0xb1aeca7cc653ed7d, 0xab1be9cacc6e53ea, 0x66f2b3e8dfca39be, 0xf46a31f2bb34bea3, + 0xdf2a232f8ed10cad, 0x8f95df35ca3eacd1, 0xe57da3e3728faa8d, 0x4fb946bc3be5a8b4, + 0x4538df5884d6477a, 0x18d64dd1fc4ec6be, 0x48b968fb9365937d, 0x1da4971f64cb5be7, + 0x33ea2593a8df45f3, 0xd4d8e09b98fa896e, 0x89daf97df6a723e7, 0xd94eb7c524e2df4e, + 0x459d6f890e5769f0, 0x2a6a7dc567a7dae2, 0x2fa1a89767b69f72, 0xfae5ea8bec99ea27, + 0xebe25cb2717d3ac4, 0xeb14f9db8967d0ac, 0x326c5f5fbeb13936, 0x134c8adf4cd897d5, + 0xd376be5307d8be6a, 0x13077c9a2addf12c, 0x9371fc4d9752df2b, 0xadcc43c9bac5df15, + 0x62e6a27d4d9713f4, 0xf6695c7fd8b9a93f, 0xe1a9d97c7dc4b35c, 0x7e1beb299c6beca7, + 0x1a137594798ca3ed, 0xe9e43ee568b4f95d, 0x691e85cfb48ad646, 0x6393485f3b48a176, + 0x7c4bb4589f2bb35f, 0xec8a7d73a89767c6, 0x12e2b27c8b2e27d4, 0xc17c9a8ba1cd8afa, + 0x2d77dc98adef5365, 0xa35f4c72b7dff731, 0xb517bd235d9fd3ec, 0xc4ef9525a89dea93, + 0xb93ea2a2ca75f04a, 0x7be589d72ea27cab, 0x90d48b7c934d95f0, 0x2b3f289bacfc574d, + 0xea7df5365c2fd137, 0xcaedf1ba567f465b, 0x2b37df32dc7d70c7, 0xeb28dedd322b7e31, + 0x2a277cb764d47df8, 0x074cadf1ec4cb7d4, 0x6d4df2c756f6b931, 0x0001c9f51b52ea3e, +}; + +static const_inline uint32_t +bag_seq (unsigned i) +{ + if (i > 118U) + return 0U; + + unsigned y = (i *= 30U) >> 6U, x = i & 63U; + uint32_t s = (uint32_t)(BAG_DBSEQ[y] >> x); + + if (x > 34U) + s |= (uint32_t)(BAG_DBSEQ[y + 1U] << (64U - x)); + + return ror32((s >> 4U & 0x3fffffeU) | 0x4000001U, s & 0x1fU); +} + +static const_inline struct bag +bag (unsigned i) +{ + if (i > 118U) + return (struct bag){0}; + uint32_t seq = bag_seq(i); + uint8_t h2 = i < 65U ? 1U + : (i < 104U ? (2U + (i >= 88U)) + : (i < 116U ? (4U + (i >= 110U)) + : (6U + (i >= 117U)))); + return (struct bag){ + .seq = seq, + .head = {0, h2}, + .iter = {0, h2}, + }; +} + +static force_inline uint8_t +bag_map (struct bag const *const bag, + uint32_t const idx) +{ + return (uint8_t)(ror32(bag->seq, idx) & UINT32_C(31)); +} + +static force_inline char const ** +bag_ptr (struct bag *bag, + uint32_t const idx) +{ + return &bag->ptr[bag_map(bag, idx)]; +} + +static force_inline uint32_t +bag_idx (struct bag const *const bag, + char const **const ptr) +{ + return (uint32_t)(ptrdiff_t)(ptr - &bag->ptr[0]); +} + +static force_inline char const ** +bag_iter (struct bag *const bag, + bool const second) +{ + uint8_t const i = bag->head[second]; + bag->iter[second] = i; + return &bag->ptr[i]; +} + +static force_inline void +bag_move_head (struct bag *const bag, + bool const second, + unsigned amount) +{ + uint8_t i = bag->head[second]; + for (; amount; --amount) { + i = bag_map(bag, i); + } + bag->head[second] = i; +} + +static force_inline char const ** +bag_next (struct bag *const bag, + bool const second) +{ + uint8_t const i = bag_map(bag, bag->iter[second]); + bag->iter[second] = i; + return i == bag->head[second] ? nullptr : &bag->ptr[i]; +} + +static uint64_t +bag_gen (struct bag *const a, + struct bag *const b) +{ + char const **i0 = bag_iter(a, 0); + char const **i1 = bag_iter(b, 1); + uint64_t u64 = 0U; + size_t bs = 64U; + do { + unsigned char const *ucp[] = { + (unsigned char const *)*i0, + (unsigned char const *)*i1 + }; + size_t len = ucp[0][0]; + bs -= len; + len *= ucp[0][1] == (unsigned char)'1'; + u64 |= (UINT64_C(1) << len) - 1U << bs; + bs -= (len = ucp[1][0]); + len *= ucp[1][1] == (unsigned char)'1'; + u64 |= (UINT64_C(1) << len) - 1U << bs; + } while ((i0 = bag_next(a, 0)) && + (i1 = bag_next(b, 1))); + + return u64; +} + +static void +bags_scan (struct bag *const b, + uint64_t const *const b26, + unsigned const y, + unsigned const x) +{ + for (unsigned i = 0; i < 119U; ++i) { + for (unsigned j = i; j < 119U; ++j) { + for (unsigned r = 0U; r < 16U; ++r, bag_move_head(&b[j], 1, 1)) { + struct b26_id id = {0}; + uint64_t u64 = b26_identify(bag_gen(&b[i], &b[j]), &id, b26); + if (u64) { + printf("a=%3u 0x%08" PRIx32 " b=%3u 0x%08" PRIx32 + " r=%2u 0x%016" PRIx64 " (%" PRIu32 ",%" PRIu32 ")\n", + i, b[i].seq, j, b[j].seq, r, u64, + (uint32_t)id.idx, (uint32_t)id.rot); + } + if (i == j) + continue; + id = (struct b26_id){0}; + u64 = b26_identify(bag_gen(&b[j], &b[i]), &id, b26); + if (u64) { + printf("a=%3u 0x%08" PRIx32 " b=%3u 0x%08" PRIx32 + " r=%2u 0x%016" PRIx64 " (%" PRIu32 ",%" PRIu32 ")\n", + j, b[j].seq, i, b[i].seq, r, u64, + (uint32_t)id.idx, (uint32_t)id.rot); + } + } + if (i == j) + continue; + for (unsigned r = 0U; r < 16U; ++r, bag_move_head(&b[i], 1, 1)) { + struct b26_id id = {0}; + uint64_t u64 = b26_identify(bag_gen(&b[j], &b[i]), &id, b26); + if (!u64) + continue; + printf("a=%3u 0x%08" PRIx32 " b=%3u 0x%08" PRIx32 + " r=%2u 0x%016" PRIx64 " (%" PRIu32 ",%" PRIu32 ")\n", + j, b[j].seq, i, b[i].seq, r, u64, + (uint32_t)id.idx, (uint32_t)id.rot); + } + } + } +} + +static int +alt_main (int c, + char **v) +{ + uint64_t *b26 = nullptr; + if (c == 2) { + if (!(b26 = b26_load(v[1], false))) + return 1; + } else for (int i = 0; ++i < c;) { + uint16_t id = (uint16_t)strtoul(v[i], nullptr, 0); + struct b25_info info = b25_info_by_id(b25_id(id)); + b25_info_print(&info, b25_id(id)); + } + + if (c > 2) + return 0; + +#if 0 + struct b25_info info[array_size(two16)] = {0}; + for (size_t i = 0; i < array_size(two16); ++i) + info[i] = b25_info_by_id(two16[i].seq << 5U | two16[i].rot); + for (size_t i = 0; i < array_size(two16); ++i) + b25_info_print(&info[i], two16[i].seq << 5U | two16[i].rot); +#endif + char const *const ptr[] = { + "\6" "000000", "\4" "0000", "\3" "000", "\3" "000", "\2" "00", "\2" "00", "\2" "00", + "\2" "00", "\1" "0", "\1" "0", "\1" "0", "\1" "0", "\1" "0", "\1" "0", "\1" "0", "\1" "0", + "\6" "111111", "\4" "1111", "\3" "111", "\3" "111", "\2" "11", "\2" "11", "\2" "11", + "\2" "11", "\1" "1", "\1" "1", "\1" "1", "\1" "1", "\1" "1", "\1" "1", "\1" "1", "\1" "1", + }; + + uint32_t mask[119]; + struct bag b[119]; + + for (unsigned i = 0; i < 119U; ++i) { + b[i] = bag(i); + mask[i] = b25_info(b[i].seq).mask[0]; + } + + for (unsigned y = 0; y < 16; ++y) { + for (unsigned x = 0; x < 16; ++x) { + for (unsigned i = 0; i < 119U; ++i) { + unsigned k0 = x; + unsigned k1 = y; + for (uint32_t n = 0, m = mask[i]; n < 32U; ++n, m >>= 1U) { + b[i].ptr[n] = (m & 1U) + ? ptr[(k0++ & 15U)] + : ptr[(k1++ & 15U) + 16U]; + } + } + bags_scan(b, b26, y, x); + } + } + + free(b26); + return 0; +} diff --git a/src/debruijn26.c b/src/debruijn26.c new file mode 100644 index 0000000..3a51461 --- /dev/null +++ b/src/debruijn26.c @@ -0,0 +1,414 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/** @file debruijn26.c + * @brief 64-bit binary De Bruijn sequence test + * @author Juuso Alasuutari + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "b25.h" +#include "b26.h" + +#if defined _MSC_VER || defined __INTELLISENSE__ +typedef uint8_t u8bit; +typedef int8_t s8bit; + +typedef struct uint3 { + unsigned value : 3; +} uint3; +#define uint3(x) (uint3){.value=(x)} +#define uint3_get(x) (x).value + +typedef struct uint4 { + unsigned value : 4; +} uint4; +#define uint4(x) (uint4){.value=(x)} +#define uint4_get(x) (x).value + +typedef struct uint3x32 { + uint8_t value[12]; +} uint3x32; + +static force_inline uint3x32 * +uint3x32_or (uint3x32 *const x, + unsigned i, + uint3 v) +{ + i = (i & 31U) * 3U; + uint16_t v_ = (uint16_t)uint3_get(v) << (i & 7U); + i >>= 3U; + x->value[i] |= (uint8_t)v_; + v_ >>= 8U; + if (v_) + x->value[i + 1U] |= (uint8_t)v_; + return x; +} + +static const_inline unsigned +uint3x32_get (uint3x32 const *const x, + unsigned const i) +{ + return (((uint16_t)x->value[(i+1U & 31U) * 3U >> 3U] << 8U) + | x->value[(i & 31U) * 3U >> 3U]) >> (i & 7U) & 7U; +} + +#else +typedef unsigned _BitInt(8) u8bit; +typedef signed _BitInt(8) s8bit; + +typedef unsigned _BitInt(3) uint3; +#define uint3(x) (uint3)(x) +#define uint3_get(x) x + +typedef unsigned _BitInt(4) uint4; +#define uint4(x) (uint4)(x) +#define uint4_get(x) x + +typedef unsigned _BitInt(96) uint3x32; +#define uint3x32_nand(x,i,v) \ + ((x) & ~((uint3x32)(v) << (3U * (i)))) +#define uint3x32_or_(x,i,v) \ + ((x) | ((uint3x32)(v) << (3U * (i)))) + +static force_inline uint3x32 * +uint3x32_or (uint3x32 *const x, + unsigned const i, + uint3 const v) +{ + *x |= (uint3x32)uint3_get(v) << (i * 3U); + return x; +} + +static const_inline unsigned +uint3x32_get (uint3x32 const *const x, + unsigned const i) +{ + return (*x >> (3U * i)) & 7U; +} +#endif + +struct b26_bitcount { + union { + uint32_t bits; + struct { + u32bit z6 : 1; // 0 + u32bit z4 : 1; // 1 + u32bit z3 : 2; // 2 + u32bit z2 : 3; // 4 + u32bit z1 : 4; // 7 + u32bit na :10; // 11 + u32bit o1 : 4; // 21 + u32bit o2 : 3; // 25 + u32bit o3 : 2; // 28 + u32bit o4 : 1; // 30 + u32bit o6 : 1; // 31 + }; + }; +}; + +// 0b0000 0 000000 +// 0b0001 1 0000 +// 0b0010 2 000 +// 0b0011 3 00 +// 0b0100 4 0 +// 0b0101 5 1 +// 0b0110 6 11 +// 0b0111 7 111 +// 0b1000 8 1111 +// 0b1001 9 111111 +struct b26_bits { + uint3x32 data; + u8bit wrap : 6; + u8bit is_1 : 1; + u8bit pad_ : 1; +}; + +static uint64_t +parse_u64 (char *arg, + int *err); + +static bool +b26_decompose (struct b26_bits *dest, + uint64_t seq); + +static uint64_t +b26_seq_compose (struct b26_bits const *bits); + +static struct b26_bits +b26_permute (struct b26_bits const *what, + uint32_t with); + +static uint64_t +test_b25 (uint64_t x, + uint16_t id) +{ + struct b26_bits bs = {0}; + if (!b26_decompose(&bs, x)) + return 0; + unsigned r_ = 0; + uint64_t x_ = is_b26(x, &r_); + struct b25_id id_ = b25_id(id); + uint32_t with = b25(id_); + struct b26_bits bs1 = bs; + for (unsigned n = 0;; ++n) { + bs1 = b26_permute(&bs1, with); + uint64_t seq = b26_seq_compose(&bs1); + unsigned rot = 0; + uint64_t lyn = is_b26(seq, &rot); + if (lyn == x_) + break; + if (lyn) + (void)printf("%016" PRIx64 " %4u %016" PRIx64 + " %016" PRIx64 " %2u %08" PRIx32 + " %4u %2u\n", x, n, seq, lyn, rot, + with, (unsigned)id_.idx, + (unsigned)id_.rot); + } + + return 0; +} + +int +main (int c, + char **v) +{ + char *b26_bin_file = nullptr; + uint64_t *b26_data = nullptr; + unsigned num_ids = 0; + +second_round: + for (int i = 0; ++i < c;) { + if (v[i][0] == '-' && + v[i][1] == '-' && + v[i][2] == 'b' && + v[i][3] == '2' && + v[i][4] == '6') { + if (!b26_data) { + char *p = &v[i][5]; + if (*p == '=') + ++p; + else if (!*p && ++i < c) + p = v[i]; + else + return 1; + if (b26_bin_file) + return 1; + b26_bin_file = p; + } + continue; + } + + int e = 0; + uint64_t u = parse_u64(v[i], &e); + if (!e && u < 65536U) { + if (!b26_data) { + num_ids++; + continue; + } + + for (size_t k = 0; k < 67108864U; ++k) { + (void)test_b25(b26_data[k], (uint16_t)u); + } +/* + struct b26_bits bs = {0}; + if (b26_decompose(&bs, u)) { + union { + unsigned char ch[12]; + uint32_t u32[3]; + } tmp = {0}; + memcpy(&tmp.ch[0], &bs.data, sizeof tmp.ch); + printf("%08" PRIx32 "%08" PRIx32 "%08" PRIx32 "\n", + tmp.u32[2], tmp.u32[1], tmp.u32[0]); + uint64_t seq = b26_seq_compose(&bs); + (void)printf("0x%016" PRIx64 "\n", seq); + } +*/ + } + } + + if (!b26_bin_file) + return 0; + + if (!b26_data) { + b26_data = b26_load(b26_bin_file, false); + if (b26_data) { + if (num_ids) + goto second_round; + for (size_t i = 0; i < 67108864U; ++i) { + (void)test_b25(b26_data[i], 0); + } + } + } + + free(b26_data); +} + +struct i_m { + int8_t index; + uint8_t mask; + uint8_t want; +}; + +static const_inline struct i_m +b26_bitcount_offset (unsigned const size, + bool const is_1) +{ + return (struct i_m[]){ + {-1, 0, 0}, { 7,15, 8}, + { 4, 7, 4}, { 2, 3, 2}, + { 1, 1, 1}, {-1, 0, 0}, + { 0, 1, 1}, {-1, 0, 0}, + {21,15, 8}, {25, 7, 4}, + {28, 3, 2}, {30, 1, 1}, + {-1, 0, 0}, {31, 1, 1}, + }[(size < 7U) * (size + is_1 * 7U)]; +} + +static bool +b26_bitcount_add (struct b26_bitcount *const dest, + unsigned const size, + bool const is_1) +{ + struct i_m i = b26_bitcount_offset(size, is_1); + if (i.index == -1) + return false; + unsigned off = (unsigned)i.index; + uint32_t cur = (dest->bits >> off) & i.mask; + if (cur >= i.want) + return false; + dest->bits += UINT32_C(1) << off; + return true; +} + +static uint64_t +b26_seq_compose (struct b26_bits const *const bits) +{ + uint64_t seq = 0U; + unsigned i = 32U; + unsigned is_1 = bits->is_1; + do { + unsigned n = uint3x32_get(&bits->data, --i); + seq += is_1; + seq <<= n; + seq -= is_1; + //printf("\033[%c;3%cm\xe2\x96%c", (char)('0'+is_1), + // (char)('0'+n+(n<6U)), (char)(0x80+n)); + is_1 ^= 1U; + } while (i); + //puts("\033[m"); + //i = 32U; + //is_1 = bits->is_1; + //do { + // unsigned n = uint3x32_get(&bits->data, --i); + // putchar('0' + (int)n); + // is_1 ^= 1U; + //} while (i); + //putchar('\n'); + return rol(seq, bits->wrap); +} + +static bool +b26_decompose (struct b26_bits *const dest, + uint64_t seq) +{ + struct b26_bitcount bc = {0}; + unsigned nbit = count_msb_0(seq); + unsigned is_1 = !nbit; + + if (is_1) + nbit = count_msb_1(seq); + + unsigned wrap = is_1 == (seq & 1U) && nbit < 64U; + if (wrap) + wrap = is_1 ? count_lsb_1(seq) + : count_lsb_0(seq); + unsigned n = nbit + wrap; + unsigned i = 31U; + + if (!b26_bitcount_add(&bc, n, is_1)) + return false; + + struct b26_bits bs = { + .wrap = (u8bit)wrap, + .is_1 = (u8bit)is_1, + }; + uint3x32_or(&bs.data, 31U, uint3(n)); + + for (unsigned left = 64U - n; left; left -= nbit) { + seq <<= nbit; + is_1 ^= 1U; + nbit = is_1 ? count_msb_1(seq) + : count_msb_0(seq); + if (nbit > left) + nbit = left; + if (!b26_bitcount_add(&bc, nbit, is_1)) + return false; + uint3x32_or(&bs.data, --i, uint3(nbit)); + } + + *dest = bs; + return true; +} + +static struct b26_bits +b26_permute (struct b26_bits const *const what, + uint32_t const with) +{ + struct b26_bits ret = { + .wrap = what->wrap, + .is_1 = what->is_1 + }; + uint64_t b = ((uint64_t)with << 32U) | with; + + for (unsigned i = 0; i < 32U; ++i) { + unsigned n = uint3x32_get(&what->data, i); + uint3x32_or(&ret.data, (b >> i) & 31U, uint3(n)); + } + + return ret; +} + +static uint64_t +parse_u64 (char *const arg, + int *const err) +{ + int e = !arg ? EFAULT : !*arg ? ENODATA : 0; + uint64_t u64 = 0; + + if (!e) { + errno = 0; + char *p = arg; + int64_t i64 = _Generic( + i64, long: strtol, + long long: strtoll + )(arg, &p, 0); + e = errno; + + if (!e) { + u64 = (uint64_t)i64; + + } else if (e == ERANGE && + i64 == _Generic(i64, long: LONG_MAX, + long long: LLONG_MAX)) { + errno = 0; + p = arg; + u64 = _Generic( + u64, unsigned long: strtoul, + unsigned long long: strtoull + )(arg, &p, 0); + e = errno; + } + + if (!e && *p) + e = EINVAL; + } + + *err = e; + return u64; +} diff --git a/src/file.c b/src/file.c index e85937c..bd2887e 100644 --- a/src/file.c +++ b/src/file.c @@ -11,55 +11,124 @@ #include "compat.h" #include "file.h" +/** + * @brief Read a file into a buffer. + * + * `FILE_IN_EXACT` adds constraints to the input file size, while + * `FILE_IN_TEXT` modifies the handling of the output buffer. + * + * The following rules apply: + * + * - If `FILE_IN_EXACT` is set, `size` must be non-zero and exactly + * match the file size. + * + * - If `FILE_IN_EXACT` is set and `dest` is not `nullptr`, no null + * terminator is appended even if `FILE_IN_TEXT` is set. + * + * - If `FILE_IN_TEXT` is set and `dest` is `nullptr`, the buffer is + * always null-terminated. + * + * - If `FILE_IN_TEXT` is set, `FILE_IN_EXACT` is set, and `dest` is + * `nullptr`, the allocated buffer will contain a null terminator. + * + * - If `FILE_IN_TEXT` is set, `FILE_IN_EXACT` is not set, and `dest` + * is not `nullptr`, a null terminator is appended if there's space + * left after reading the file (i.e. file is smaller than `size`). + * + * @param path The path to the file. + * @param dest The buffer to read the file into. + * @param size The size of the buffer. + * @param flags The flags to use. + */ struct file_in -file_read (char const *path) +file_in (char const *path, + void *dest, + size_t size, + unsigned flags) { struct file_in ret = {0}; do { + if (size < 1U && (dest || (flags & FILE_IN_EXACT))) { + ret.error = EINVAL; + break; + } + FILE *fp = fopen(path, "rbem"); if (!fp) { - ret.ec[0] = errno; + ret.error = errno; break; } + if (dest) + flags &= (unsigned)~FILE_IN_ALLOC; + else + flags |= FILE_IN_ALLOC; + do { struct stat s = {0}; - ret.ec[0] = fileno(fp); + ret.error = fileno(fp); - ret.ec[0] = ret.ec[0] < 0 || fstat(ret.ec[0], &s); - if (ret.ec[0]) { - ret.ec[0] = errno; + ret.error = ret.error < 0 || fstat(ret.error, &s); + if (ret.error) { + ret.error = errno; break; } if ((s.st_mode & S_IFMT) != S_IFREG) { - ret.ec[0] = EINVAL; + ret.error = ENOENT; break; } if (s.st_size < 0) { - ret.ec[0] = EIO; + ret.error = EIO; + break; + } + + size_t len = (size_t)s.st_size; + + if ((flags & FILE_IN_EXACT) && size != len) { + ret.error = EINVAL; break; } - ret.size = (size_t)s.st_size; - ret.data = malloc(ret.size + 1U); - if (!ret.data) { - ret.size = 0; - ret.ec[0] = errno; + if (flags & FILE_IN_ALLOC) { + dest = malloc(len + !!(flags & FILE_IN_TEXT)); + if (!dest) { + ret.error = errno; + break; + } + } else if (len > size) { + ret.error = ENOMEM; break; } - if (fread(ret.data, 1, ret.size, fp) != ret.size) { - ret.size = 0; - ret.ec[0] = errno; - free(ret.data); - ret.data = nullptr; + if (fread(dest, 1, len, fp) != len) { + ret.error = errno; + if (flags & FILE_IN_ALLOC) { + free(dest); + dest = nullptr; + } break; } - ret.data[ret.size] = 0; + ret.data = dest; + ret.size = len; + ret.flags = flags & (FILE_IN_TEXT + |FILE_IN_EXACT + |FILE_IN_ALLOC); + + switch (ret.flags & (FILE_IN_TEXT | FILE_IN_ALLOC)) { + case FILE_IN_TEXT: + if (size > len) + ret.data[ret.size] = 0; + break; + case FILE_IN_TEXT | FILE_IN_ALLOC: + ret.data[ret.size] = 0; + break; + default: + break; + } } while (0); (void)fclose(fp); @@ -73,8 +142,11 @@ void file_in_fini (struct file_in *f) { if (f) { - free(f->data); + if (f->flags & FILE_IN_ALLOC) + free(f->data); f->data = nullptr; f->size = 0; + f->flags = 0; + f->error = 0; } } diff --git a/src/file.h b/src/file.h index dc0d40f..da34039 100644 --- a/src/file.h +++ b/src/file.h @@ -12,27 +12,34 @@ struct file_in { unsigned char *data; - union { - size_t size; - int ec[!(sizeof (size_t) / sizeof (int)) - + sizeof (size_t) / sizeof (int)]; - }; + size_t size; + unsigned flags; + int error; +}; + +enum file_in_flags { + FILE_IN_TEXT = 1U << 0U, + FILE_IN_EXACT = 1U << 1U, + FILE_IN_ALLOC = 1U << 2U, }; extern struct file_in -file_read (char const *path); +file_in (char const *path, + void *dest, + size_t size, + unsigned flags); extern void file_in_fini (struct file_in *f); static force_inline int -file_error (struct file_in const *f) +file_in_error (struct file_in const *f) { - return f && !f->data ? f->ec[0] : 0; + return f && !f->data ? f->error : 0; } static force_inline char const * -file_text (struct file_in const *f) +file_in_text (struct file_in const *f) { return f && f->data ? (char const *)f->data : ""; } diff --git a/src/json.c b/src/json.c new file mode 100644 index 0000000..1710680 --- /dev/null +++ b/src/json.c @@ -0,0 +1,479 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/** @file json.c + * + * @author Juuso Alasuutari + */ +#include +#include + +#include "dbg.h" +#include "json_internal.h" + +/* + * ::= + * ::= + * ::= | | | | "true" | "false" | "null" + * ::= "{" "}" | "{" "}" + * ::= | "," + * ::= ":" + * ::= "[" "]" | "[" "]" + * ::= | "," + * ::= "\"" "\"" + * ::= "" | + * ::= | + * ::= + * ::= "\\" + * ::= "\"" | "/" | "b" | "f" | "n" | "r" | "t" | "u" + * ::= + * ::= | | "-" | "-" + * ::= | + * ::= "0" | + * ::= "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" + * ::= "" | "." + * ::= "" | "e" | "E" + * ::= "" | "+" | "-" + * ::= "" | " " | "\t" | "\n" | "\r" + */ + +/** + * @brief Parse the fractional and exponential parts of a number. + * + * Parse the fractional (dot and digits) and exponential (e/E with + * optional sign and digits) parts of a number, if any. + * + * Corresponds to the regex `(\.[0-9]+)?([Ee][\+-]?[0-9]+)?`. + * + * @param arg JSON parser input. @ref json_arg::ptr must point to + * the first non-digit following `-?(0|[1-9][0-9]*)`. + * @return JSON parsing result. @ref json_ret::code will be 0 on + * success, `ENODATA` on unexpected end of input buffer, + * and `EILSEQ` on JSON syntax error. + */ +static struct json_ret +json_parse_frc_exp (struct json_arg arg) +{ + int e = 0; + uint8_t const *p = arg.ptr; + + if (p < arg.end) do { + if (*p == '.') { + if (++p >= arg.end) { + // last character is '.' + e = ENODATA; + break; + } + + if (!(json_lut[*p] & json_dig)) { + // non-digit follows '.' + e = EILSEQ; + break; + } + + p = json_skip_digits(&p[1], arg.end); + } + + if (p < arg.end && (json_lut[*p] & json_exp)) { + if (++p >= arg.end || + ((json_lut[*p] & json_sig) && ++p >= arg.end)) { + // last character is [Ee+-] + e = ENODATA; + break; + } + + if (!(json_lut[*p] & json_dig)) { + // non-digit follows [Ee][+-]? + e = EILSEQ; + break; + } + + p = json_skip_digits(&p[1], arg.end); + } + } while (0); + + return (struct json_ret){ + .size = (uint64_t)(ptrdiff_t)(p - arg.ptr), + .type = json_number, + .code = (uint64_t)e + }; +} + +static force_inline struct json_ret +json_parse_num (struct json_arg arg) +{ + uint8_t const *p = json_skip_digits(&arg.ptr[1], arg.end); + struct json_ret ret = json_parse_frc_exp((struct json_arg){ + .ptr = p, + .end = arg.end, + .ctx = arg.ctx, + }); + ret.size += (uint64_t)(ptrdiff_t)(p - arg.ptr); + return ret; +} + +static struct json_ret +json_parse_array (useless struct json_arg arg) +{ + return (struct json_ret){ + .size = 0, + .type = json_array, + .code = ENOSYS + }; +} + +static struct json_ret +json_parse_object (useless struct json_arg arg) +{ + return (struct json_ret){ + .size = 0, + .type = json_object, + .code = ENOSYS + }; +} + +/** + * @brief Parse a JSON string. + * + * On entry, `arg.ptr` points to the opening '"' character. + * + * @param arg JSON argument. + * @return JSON return value. + */ +static struct json_ret +json_parse_string (struct json_arg arg) +{ + struct json_ret ret = { + .size = 0, + .type = json_string, + .code = ENODATA + }; + + uint8_t const *p = &arg.ptr[1]; + while (p < arg.end) { + if (*p < 0x20U) { + // unescaped control character + ret.code = EILSEQ; + break; + } + + if (*p == (uint8_t)'"') { + ++p; + ret.code = 0; + break; + } + + if (*p == (uint8_t)'\\') { + if (++p >= arg.end) + // buffer ends in backslash + break; + + uint8_t flags = json_lut[*p]; + + if (!(flags & json_esc)) { + // lone unescaped backslash + ret.code = EILSEQ; + break; + } + + if (*p++ != (uint8_t)'u') + // single-character escape sequence + continue; + + // unicode escape sequence + for (size_t i = 0;; ++p) { + if (p >= arg.end) + // unexpected end of input + goto done; + + if (!(json_lut[*p] & (json_dig|json_hex))) { + // non-hex character + ret.code = EILSEQ; + goto done; + } + if (++i == 4) + break; + } + } + + ++p; + } + +done: + ret.size = (uint64_t)(ptrdiff_t)(p - arg.ptr); + return ret; +} + +/** + * @brief Parse a negative number. + * + * On entry, `arg.ptr` points to the '-' character. + * + * @param arg JSON argument. + * @return JSON return value. + */ +static force_inline struct json_ret +json_parse_neg (struct json_arg arg) +{ + struct json_ret ret = { + .size = 1, + .type = json_number, + .code = 0 + }; + + uint8_t const *p = &arg.ptr[1]; + if (p >= arg.end) { + // last character is '-' + ret.code = ENODATA; + return ret; + } + + uint8_t flags = json_lut[*p]; + + if (!(flags & json_dig)) { + // non-digit follows '-' + ret.code = EILSEQ; + return ret; + } + + ++p; + + if (flags & json_1_9) + // -[1-9][0-9]* (instead of -0) + p = json_skip_digits(p, arg.end); + + ret = json_parse_frc_exp((struct json_arg){ + .ptr = p, + .end = arg.end, + .ctx = arg.ctx, + }); + if (!(flags & json_1_9)) { + if (ret.size == 0 && p < arg.end + && (json_lut[*p] & json_dig)) + // digit follows "-0" + ret.code = EILSEQ; + } + ret.size += (uint64_t)(ptrdiff_t)(p - arg.ptr); + return ret; +} + +/** + * @brief Copy a @ref json_ret while constraining its @ref json_ret::size. + * + * If `size` is not less than the @ref json_ret::size field of `ret`, the + * return value is the unmodified `ret`. If `size` is less than that, the + * return value is a copy of `ret` save for the @ref json_ret::size field + * which contains `size`. + * + * @param ret The @ref json_ret to copy and possibly constrain. + * @param size The constraint. + * + * @return A copy of `ret`, possibly with a smaller @ref json_ret::size. + */ +static const_inline struct json_ret +json_ret_dup_size_constrained (const struct json_ret ret, + const size_t size) +{ + return (struct json_ret){ + .size = ret.size < size + ? ret.size : size, + .type = ret.type, + .code = ret.code + }; +} + +/** + * @brief Copy a @ref json_ret while constraining its @ref json_ret::size. + * + * If the remaining buffer size calculated from `arg` is not less than the + * @ref json_ret::size of `ret`, the return value is the unmodified `ret`. + * If the remaining buffer size is less than that, the result is otherwise + * identical except @ref json_ret::size contains the remaining buffer size. + * + * @param ret The @ref json_ret to copy and possibly constrain. + * @param arg A @ref json_arg for calculating the remaining buffer size. + * + * @return A copy of `ret`, possibly with a smaller @ref json_ret::size. + */ +__attribute__((pure)) static force_inline struct json_ret +json_ret_dup_arg_constrained (const struct json_ret ret, + const struct json_arg arg) +{ + return json_ret_dup_size_constrained( + ret, + (size_t)(arg.end - arg.ptr) + ); +} + +/** + * @brief Parse a JSON keyword. + * @param kwd JSON keyword. + * @param arg JSON argument. + * @return JSON return value. + */ +static struct json_ret __attribute__((pure)) +json_parse_keyword (const struct json_kwd kwd, + const struct json_arg arg) +{ + struct json_ret ret = json_ret_dup_arg_constrained( + (struct json_ret){ + .size = kwd.size, + .type = kwd.type, + .code = 0 + }, + arg + ); + + unsigned i = 0; + char const *const str = (char const *)arg.ptr; + while (i < ret.size && str[i] == kwd.str[i]) { + ++i; + } + + if (i < ret.size) { + ret.size = i; + ret.code = EILSEQ; + + } else if (i < kwd.size) { + ret.code = ENODATA; + } + + return ret; +} + +#define json_define_keyword(x) \ +static force_inline struct json_ret \ +json_parse_##x (struct json_arg a_) \ +{ \ + return json_parse_keyword( \ + (struct json_kwd){ \ + .type = json_##x, \ + .size = sizeof #x-1U, \ + .str = #x \ + }, \ + a_ \ + ); \ +} \ +_Static_assert(sizeof #x>1U,""); \ +_Static_assert(sizeof #x-1U <= \ + sizeof ((struct json_kwd *)0)->str,"") + +json_define_keyword(false); +json_define_keyword(null); +json_define_keyword(true); + +#undef json_define_keyword + +static struct json_ret +json_parse_value (struct json_arg arg) +{ + struct json_ret ret = {0}; + + switch (*arg.ptr) { + case '"': + ret = json_parse_string(arg); + break; + case '-': + // -(0|[1-9][0-9]*)(\.[0-9]+)?([Ee][\+-]?[0-9]+)? + ret = json_parse_neg(arg); + break; + case '0': + // 0(\.[0-9]+)?([Ee][\+-]?[0-9]+)? + ++arg.ptr; + ret = json_parse_frc_exp(arg); + if (ret.size == 0 && arg.ptr < arg.end + && (json_lut[*arg.ptr] & json_dig)) + // digit follows '0' + ret.code = EILSEQ; + ++ret.size; + break; + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + // [1-9][0-9]*(\.[0-9]+)?([Ee][\+-]?[0-9]+)? + ret = json_parse_num(arg); + break; + case '[': + ret = json_parse_array(arg); + break; + case 'f': + ret = json_parse_false(arg); + break; + case 'n': + ret = json_parse_null(arg); + break; + case 't': + ret = json_parse_true(arg); + break; + case '{': + ret = json_parse_object(arg); + break; + default: + ret.code = EILSEQ; + } + + return ret; +} + +static void +json_describe (struct json_ret ret, + uint8_t const *ptr, + size_t off, + char const *func) +{ + if (ret.code) { + pr_errno_(ret.code, "%s: at offset %zu", + func, off + (size_t)ret.size); + return; + } + + switch (ret.type) { + case json_false: + case json_true: + case json_null: + case json_number: + case json_string: + case json_array: + case json_object: + if (ret.size) + pr_("%.*s\n", (int)ret.size, &ptr[off]); + break; + default: + break; + } +} + +static struct json_ret +json_parse_element (struct json_arg arg) +{ + uint8_t const *ptr = json_skip_ws(arg.ptr, arg.end); + struct json_ret ret = json_parse_value((struct json_arg){ + .ptr = ptr, + .end = arg.end, + .ctx = arg.ctx, + }); + json_describe(ret, arg.ptr, (size_t)(ptr - arg.ptr), __func__); + ptr = &ptr[ret.size]; + if (!ret.code) + ptr = json_skip_ws(ptr, arg.end); + ret.size = (uint64_t)(ptrdiff_t)(ptr - arg.ptr); + return ret; +} + +void +json_parse (char const *str) +{ + if (!str) + return; + + struct json_w w = { + .buf = (void const *)str, + .len = strlen(str), + }; + + struct json_ret ret = json_parse_element((struct json_arg){ + .ptr = (uint8_t const *)w.buf, + .end = &w.buf[w.len], + .ctx = &w, + }); + + (void)ret; +} diff --git a/src/json.h b/src/json.h new file mode 100644 index 0000000..3f3d7f3 --- /dev/null +++ b/src/json.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/** @file json.h + * + * @author Juuso Alasuutari + */ +#ifndef LIBCANTH_SRC_JSON_H_ +#define LIBCANTH_SRC_JSON_H_ + +#include +#include + +#include "util.h" + +fixed_enum(json_type, uint8_t) { + json_false, + json_true, + json_null, + json_number, + json_string, + json_array, + json_object +}; + +/** + * @brief JSON parser context. + */ +struct json_w { + unsigned char const *buf; + size_t len; +}; + +/** + * @brief JSON parser input. + */ +struct json_arg { + uint8_t const *ptr; //!< Input buffer read pointer. + uint8_t const *end; //!< Input buffer end pointer. + struct json_w *ctx; //!< Parser context pointer. +}; + +/** + * @brief JSON parser result. + */ +struct json_ret { + uint64_t size : 48; //!< Parsed data size. + uint64_t type : 8; //!< Parsed data type. + uint64_t code : 8; //!< Optional `errno`. +}; + +extern void +json_parse (char const *s); + +#endif /* LIBCANTH_SRC_JSON_H_ */ diff --git a/src/json_internal.h b/src/json_internal.h new file mode 100644 index 0000000..f17611e --- /dev/null +++ b/src/json_internal.h @@ -0,0 +1,148 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/** @file json_internal.h + * + * @author Juuso Alasuutari + */ +#ifndef LIBCANTH_SRC_JSON_INTERNAL_H_ +#define LIBCANTH_SRC_JSON_INTERNAL_H_ + +#include "json.h" + +/** + * @brief JSON parser internal function type. + * @param arg JSON parsing input. + * @return JSON parsing result. + */ +typedef struct json_ret json_fn (struct json_arg arg); + +/** + * @brief JSON parsing flags. + */ +fixed_enum(json_flag, uint8_t) { + json_1_9 = 1U, + json_dig = json_1_9 << 1U, + json_esc = json_dig << 1U, + json_exp = json_esc << 1U, + json_frc = json_exp << 1U, + json_hex = json_frc << 1U, + json_sig = json_hex << 1U, + json_ws = json_sig << 1U, +}; + +/** + * @brief Lookup table for parsing JSON. + */ +constexpr static const uint8_t json_lut[256] = { + ['\t'] = json_ws, + ['\n'] = json_ws, + ['\r'] = json_ws, + [' ' ] = json_ws, + + ['"' ] = json_esc, + + ['+' ] = json_sig, + ['-' ] = json_sig, + + ['.' ] = json_frc, + + ['/' ] = json_esc, + + ['0' ] = json_dig, + ['1' ] = json_dig| json_1_9, + ['2' ] = json_dig| json_1_9, + ['3' ] = json_dig| json_1_9, + ['4' ] = json_dig| json_1_9, + ['5' ] = json_dig| json_1_9, + ['6' ] = json_dig| json_1_9, + ['7' ] = json_dig| json_1_9, + ['8' ] = json_dig| json_1_9, + ['9' ] = json_dig| json_1_9, + + ['A' ] = json_hex, + ['B' ] = json_hex, + ['C' ] = json_hex, + ['D' ] = json_hex, + ['E' ] = json_hex| json_exp, + ['F' ] = json_hex, + + ['\\'] = json_esc, + + ['a' ] = json_hex, + ['b' ] = json_hex| json_esc, + ['c' ] = json_hex, + ['d' ] = json_hex, + ['e' ] = json_hex| json_exp, + ['f' ] = json_hex| json_esc, + + ['n' ] = json_esc, + ['r' ] = json_esc, + ['t' ] = json_esc, + ['u' ] = json_esc, +}; + +/** + * @brief Skip digits. + * + * Skip decimal digit characters until a non-digit character is found + * or the end of the buffer is reached. Return a pointer to the first + * non-digit character. + * + * Digits are as defined by the JSON standard, i.e. the characters '0' + * to '9'. + * + * @note No bounds checking or input validation is performed. `ptr` and + * `end` must be valid pointers within the same contiguous buffer. + * + * @param ptr Pointer to the current position. Must be within a valid + * buffer and not larger than `end`. + * @param end Pointer to the end of the buffer. Must not be larger than + * a pointer to one past the last element of a valid buffer. + * @return Pointer to the first encountered non-decimal digit character. + */ +nonnull_in() nonnull_out +static force_inline uint8_t const * +json_skip_digits (uint8_t const * ptr, + uint8_t const *const end) +{ + for (; ptr < end && (json_lut[*ptr] & json_dig); ++ptr); + return ptr; +} + +/** + * @brief Skip whitespace characters. + * + * Skip whitespace characters until a non-whitespace character is found + * or the end of the buffer is reached, whichever comes first. Return a + * pointer to the first non-whitespace character. + * + * Whitespace characters are as defined by the JSON standard, i.e. tab, + * newline, carriage return, and space. + * + * @note No bounds checking or input validation is performed. `ptr` and + * `end` must be valid pointers within the same contiguous buffer. + * + * @param ptr Pointer to the current position. Must be within a valid + * buffer and not larger than `end`. + * @param end Pointer to the end of the buffer. Must not be larger than + * a pointer to one past the last element of a valid buffer. + * @return Pointer to the first encountered non-whitespace character. + */ +nonnull_in() nonnull_out +static force_inline uint8_t const * +json_skip_ws (uint8_t const * ptr, + uint8_t const *const end) +{ + for (; ptr < end && (json_lut[*ptr] & json_ws); ++ptr); + return ptr; +} + +/** + * @brief JSON keyword descriptor. + */ +struct json_kwd { + const uint8_t type; //!< JSON type enum. + const uint8_t size; //!< Keyword length. + const char str[6U]; //!< Keyword string. +}; + +#endif /* LIBCANTH_SRC_JSON_INTERNAL_H_ */ diff --git a/src/parse.c b/src/parse.c new file mode 100644 index 0000000..4d704c5 --- /dev/null +++ b/src/parse.c @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/** @file parse.c + * @brief String parsing + * @author Juuso Alasuutari + */ +#include +#include +#include +#include +#include + +#include "parse.h" + +struct parsed +parse_u64 (char *const str) +{ + struct parsed p = { + .u64 = 0U, + .err = !str ? EFAULT + : !*str ? ENODATA : 0 + }; + + if (!p.err) { + errno = 0; + char *s = str; + p.i64 = _Generic( + p.i64, long: strtol + , long long: strtoll + )(str, &s, 0); + p.err = errno; + + if (p.err == ERANGE && + p.i64 == _Generic( + p.i64, long: LONG_MAX + , long long: LLONG_MAX) + ) { + errno = 0; + s = str; + p.u64 = _Generic( + p.u64, unsigned long: strtoul + , unsigned long long: strtoull + )(str, &s, 0); + p.err = errno; + } + + if (!p.err && *s) + p.err = EINVAL; + } + + return p; +} + +bool +parsed_ok (struct parsed const par, + char const *const msg) +{ + bool const ret = !par.err; + if (!ret) { + char const sep[] = ": "; + char const *const m = msg ?: ""; + char const *const e = strerror(par.err) ?: ""; + (void)fprintf(stderr, "%s%s%s\n", m, + &sep[(unsigned)(!*m | !*e) << 1U], e); + } + return ret; +} diff --git a/src/parse.h b/src/parse.h new file mode 100644 index 0000000..f52533a --- /dev/null +++ b/src/parse.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/** @file parse.h + * @brief String parsing + * @author Juuso Alasuutari + */ +#ifndef LIBCANTH_SRC_PARSE_H_ +#define LIBCANTH_SRC_PARSE_H_ + +#include + +#include "compat.h" + +struct parsed { + union { + uint64_t u64; + int64_t i64; + }; + int64_t err; +}; + +extern struct parsed +parse_u64 (char *str); + +extern bool +parsed_ok (struct parsed par, + char const *msg); + +#endif /* LIBCANTH_SRC_PARSE_H_ */ diff --git a/src/rev.h b/src/rev.h new file mode 100644 index 0000000..817f78f --- /dev/null +++ b/src/rev.h @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/** @file rev.h + * @brief XOR bit reverse + * @author Juuso Alasuutari + */ +#ifndef LIBCANTH_SRC_REV_ +#define LIBCANTH_SRC_REV_ + +#include "util.h" + +#if defined _MSC_VER || defined __INTELLISENSE__ +typedef struct uint2 { unsigned v : 2; } uint2; +typedef struct uint4 { unsigned v : 4; } uint4; +#define uint2(x) (uint2){.v=(x)} +#define uint4(x) (uint4){.v=(x)} +#define uint2_get(x) (x).v +#define uint4_get(x) (x).v +#else +typedef unsigned _BitInt(2) uint2; +typedef unsigned _BitInt(4) uint4; +#define uint2(x) (uint2)(x) +#define uint4(x) (uint4)(x) +#define uint2_get(x) (x) +#define uint4_get(x) (x) +#endif + +static const_inline uint2 +rev2 (uint2 x) +{ + uint2_get(x) ^= (uint2_get(x) & 0x2U) >> 1U; + uint2_get(x) ^= (uint2_get(x) & 0x1U) << 1U; + uint2_get(x) ^= (uint2_get(x) & 0x2U) >> 1U; + return x; +} + +static const_inline uint4 +rev4 (uint4 x) +{ + uint4_get(x) ^= (uint4_get(x) & 0xaU) >> 1U; + uint4_get(x) ^= (uint4_get(x) & 0x5U) << 1U; + uint4_get(x) ^= (uint4_get(x) & 0xcU) >> 2U; + uint4_get(x) ^= (uint4_get(x) & 0x3U) << 2U; + uint4_get(x) ^= (uint4_get(x) & 0xcU) >> 2U; + uint4_get(x) ^= (uint4_get(x) & 0xaU) >> 1U; + return x; +} + +static const_inline uint8_t +rev8 (uint8_t x) +{ + x ^= (x & UINT8_C(0xaa)) >> 1U; + x ^= (x & UINT8_C(0x55)) << 1U; + x ^= (x & UINT8_C(0xcc)) >> 2U; + x ^= (x & UINT8_C(0x33)) << 2U; + x ^= (x & UINT8_C(0xf0)) >> 4U; + x ^= (x & UINT8_C(0x0f)) << 4U; + x ^= (x & UINT8_C(0xf0)) >> 4U; + x ^= (x & UINT8_C(0xcc)) >> 2U; + x ^= (x & UINT8_C(0xaa)) >> 1U; + return x; +} + +static const_inline uint16_t +rev16 (uint16_t x) +{ + x ^= (x & UINT16_C(0xaaaa)) >> 1U; + x ^= (x & UINT16_C(0x5555)) << 1U; + x ^= (x & UINT16_C(0xcccc)) >> 2U; + x ^= (x & UINT16_C(0x3333)) << 2U; + x ^= (x & UINT16_C(0xf0f0)) >> 4U; + x ^= (x & UINT16_C(0x0f0f)) << 4U; + x ^= (x & UINT16_C(0xff00)) >> 8U; + x ^= (x & UINT16_C(0x00ff)) << 8U; + x ^= (x & UINT16_C(0xff00)) >> 8U; + x ^= (x & UINT16_C(0xf0f0)) >> 4U; + x ^= (x & UINT16_C(0xcccc)) >> 2U; + x ^= (x & UINT16_C(0xaaaa)) >> 1U; + return x; +} + +static const_inline uint32_t +rev32 (uint32_t x) +{ + x ^= (x & UINT32_C(0xaaaaaaaa)) >> 1U; + x ^= (x & UINT32_C(0x55555555)) << 1U; + x ^= (x & UINT32_C(0xcccccccc)) >> 2U; + x ^= (x & UINT32_C(0x33333333)) << 2U; + x ^= (x & UINT32_C(0xf0f0f0f0)) >> 4U; + x ^= (x & UINT32_C(0x0f0f0f0f)) << 4U; + x ^= (x & UINT32_C(0xff00ff00)) >> 8U; + x ^= (x & UINT32_C(0x00ff00ff)) << 8U; + x ^= (x & UINT32_C(0xffff0000)) >> 16U; + x ^= (x & UINT32_C(0x0000ffff)) << 16U; + x ^= (x & UINT32_C(0xffff0000)) >> 16U; + x ^= (x & UINT32_C(0xff00ff00)) >> 8U; + x ^= (x & UINT32_C(0xf0f0f0f0)) >> 4U; + x ^= (x & UINT32_C(0xcccccccc)) >> 2U; + x ^= (x & UINT32_C(0xaaaaaaaa)) >> 1U; + return x; +} + +static const_inline uint64_t +rev64 (uint64_t x) +{ + x ^= (x & UINT64_C(0xaaaaaaaaaaaaaaaa)) >> 1U; + x ^= (x & UINT64_C(0x5555555555555555)) << 1U; + x ^= (x & UINT64_C(0xcccccccccccccccc)) >> 2U; + x ^= (x & UINT64_C(0x3333333333333333)) << 2U; + x ^= (x & UINT64_C(0xf0f0f0f0f0f0f0f0)) >> 4U; + x ^= (x & UINT64_C(0x0f0f0f0f0f0f0f0f)) << 4U; + x ^= (x & UINT64_C(0xff00ff00ff00ff00)) >> 8U; + x ^= (x & UINT64_C(0x00ff00ff00ff00ff)) << 8U; + x ^= (x & UINT64_C(0xffff0000ffff0000)) >> 16U; + x ^= (x & UINT64_C(0x0000ffff0000ffff)) << 16U; + x ^= (x & UINT64_C(0xffffffff00000000)) >> 32U; + x ^= (x & UINT64_C(0x00000000ffffffff)) << 32U; + x ^= (x & UINT64_C(0xffffffff00000000)) >> 32U; + x ^= (x & UINT64_C(0xffff0000ffff0000)) >> 16U; + x ^= (x & UINT64_C(0xff00ff00ff00ff00)) >> 8U; + x ^= (x & UINT64_C(0xf0f0f0f0f0f0f0f0)) >> 4U; + x ^= (x & UINT64_C(0xcccccccccccccccc)) >> 2U; + x ^= (x & UINT64_C(0xaaaaaaaaaaaaaaaa)) >> 1U; + return x; +} + +#endif /* LIBCANTH_SRC_REV_ */ diff --git a/src/stochar.c b/src/stochar.c new file mode 100644 index 0000000..436e0bd --- /dev/null +++ b/src/stochar.c @@ -0,0 +1,357 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/** @file stochar.c + * + * @author Juuso Alasuutari + */ +#include +#include + +#define PROGNAME "stochar" +#define SYNOPSIS "[OPTION]... [--] [STRING]..." +#define PURPOSE "Converts stochastic unicode to/from UTF-8." + +#define OPTIONS(X) \ + X(boolean, help, 'h', "help", \ + "print this help text and exit") \ + \ + X(boolean, replace, 'r', "replace", \ + "substitute U+FFFD for bad UTF-8") \ + \ + X(boolean, separate, 's', "separate", \ + "don't concatenate input strings") + +#define DETAILS \ + "Given no options, input strings are concatenated\n" \ + "and invalid UTF-8 sequences silently ignored.\n\n" \ + "Encoded output should be considered binary data,\n" \ + "it is not printable in any existing encoding." + +#include "letopt.h" + +#undef DETAILS +#undef OPTIONS +#undef PURPOSE +#undef SYNOPSIS +#undef PROGNAME + +#include "dbg.h" +#include "utf8.h" + +static const_inline size_t +saturated_add_uz (size_t a, + size_t b) +{ + if ((a += b) < b) + a = SIZE_MAX; + return a; +} + +static size_t +arg_sizes_max (struct letopt const *opt) +{ + size_t ret = 0; + for (int i = 0, m = letopt_nargs(opt); i < m; ++i) { + size_t n = __builtin_strlen(letopt_arg(opt, i)); + if (n > ret && (ret = n) == SIZE_MAX) + break; + } + return ret; +} + +/** + * @brief Calculate saturated sum of string argument lengths. + * + * The largest safe result is `SIZE_MAX - 1`. `SIZE_MAX` is always an + * error condition and indicates that an overflow would be inevitable + * if the result were to be used for string allocation. + * + * It is not possible to distinguish between an overflow and an actual + * `SIZE_MAX` sum. Such information would be useless, however, because + * either way there would be no space left for the null terminator. + * + * @param opt The option object whose string argument lengths to sum. + * + * @return The saturated sum of the argument string lengths. + * @retval `0` if the argument list is empty or all arguments are empty. + * @retval `SIZE_MAX` if the sum is greater than or equal to `SIZE_MAX`. + */ +static size_t +arg_sizes_sum (struct letopt const *opt) +{ + size_t ret = 0; + for (int i = 0, m = letopt_nargs(opt); i < m; ++i) { + size_t n = __builtin_strlen(letopt_arg(opt, i)); + if ((ret = saturated_add_uz(ret, n)) == SIZE_MAX) + break; + } + return ret; +} + +/** + * @brief Unicode code point encoding. + * + * These serve a dual purpose: they act as identifiers for different + * encodings, and their enumeration values themselves are defined as + * something relevant to that specific encoding. `UCP_UTF8_*` values + * are the number of bytes when encoding as UTF-8, `UCP_UTF32_*` the + * maximum amount of data bits the corresponding code points use. + */ +fixed_enum(ucp_kind, uint32_t) { + UCP_UTF8_1 = 1, //!< UTF-8 encoding, 1 byte + UCP_UTF8_2 = 2, //!< UTF-8 encoding, 2 bytes + UCP_UTF8_3 = 3, //!< UTF-8 encoding, 3 bytes + UCP_UTF8_4 = 4, //!< UTF-8 encoding, 4 bytes + UCP_UTF32_7 = 7, //!< 1-byte UTF-8 as UTF-32 + UCP_UTF32_11 = 11, //!< 2-byte UTF-8 as UTF-32 + UCP_UTF32_16 = 16, //!< 3-byte UTF-8 as UTF-32 + UCP_UTF32_21 = 21 //!< 4-byte UTF-8 as UTF-32 +}; + +/** @brief UTF-32 conversion from 1-byte UTF-8. */ +struct ucp_utf32_7 { + uint32_t b0 : 7; //!< ASCII byte [ 0: 6] + uint32_t p0 : 25; //!< [ 7:31] +}; + +/** @brief UTF-32 conversion from 2-byte UTF-8. */ +struct ucp_utf32_11 { + uint32_t b1 : 6; //!< Cont. byte 1 [ 0: 5] + uint32_t b0 : 5; //!< Leading byte [ 6:11] + uint32_t p0 : 21; //!< [12:31] +}; + +/** @brief UTF-32 conversion from 3-byte UTF-8. */ +struct ucp_utf32_16 { + uint32_t b2 : 6; //!< Cont. byte 2 [ 0: 5] + uint32_t b1 : 6; //!< Cont. byte 1 [ 6:11] + uint32_t b0 : 4; //!< Leading byte [12:15] + uint32_t p0 : 16; //!< [16:31] +}; + +/** @brief UTF-32 conversion from 4-byte UTF-8. */ +struct ucp_utf32_21 { + uint32_t b3 : 6; //!< Cont. byte 3 [ 0: 5] + uint32_t b2 : 6; //!< Cont. byte 2 [ 6:11] + uint32_t b1 : 6; //!< Cont. byte 1 [12:17] + uint32_t b0 : 3; //!< Leading byte [18:20] + uint32_t p0 : 11; //!< [21:31] +}; + +/** @brief 1-byte UTF-8 encoding. */ +struct ucp_utf8_1 { + uint32_t b0 : 7; //!< ASCII byte [ 0: 6] + uint32_t p1 : 25; //!< [ 7:31] +}; + +/** @brief 2-byte UTF-8 encoding. */ +struct ucp_utf8_2 { + uint32_t b0 : 5; //!< Leading byte [ 0: 4] + uint32_t p0 : 3; //!< [ 5: 7] + uint32_t b1 : 6; //!< Cont. byte 1 [ 8:13] + uint32_t p1 : 18; //!< [14:31] +}; + +/** @brief 3-byte UTF-8 encoding. */ +struct ucp_utf8_3 { + uint32_t b0 : 4; //!< Leading byte [ 0: 3] + uint32_t p0 : 4; //!< [ 4: 7] + uint32_t b1 : 6; //!< Cont. byte 1 [ 8:13] + uint32_t p1 : 2; //!< [14:15] + uint32_t b2 : 6; //!< Cont. byte 2 [16:21] + uint32_t p2 : 10; //!< [22:31] +}; + +/** @brief 4-byte UTF-8 encoding. */ +struct ucp_utf8_4 { + uint32_t b0 : 3; //!< Leading byte [ 0: 2] + uint32_t p0 : 5; //!< [ 3: 7] + uint32_t b1 : 6; //!< Cont. byte 1 [ 8:13] + uint32_t p1 : 2; //!< [14:15] + uint32_t b2 : 6; //!< Cont. byte 2 [16:21] + uint32_t p2 : 2; //!< [22:23] + uint32_t b3 : 6; //!< Cont. byte 3 [24:29] + uint32_t p3 : 2; //!< [30:31] +}; + +/** + * @brief Unicode code point structure. + * + * Union-based type for representing Unicode code points in + * various encodings, moving data bits around between them, + * and for use as by-value function arguments. + */ +union ucp { + uint32_t u32; //!< Raw 32-bit data view. + + struct ucp_utf32_7 utf32_0; //!< 7b UTF-32/1B UTF-8 view. + struct ucp_utf32_11 utf32_1; //!< 11b UTF-32/2B UTF-8 view. + struct ucp_utf32_16 utf32_2; //!< 16b UTF-32/3B UTF-8 view. + struct ucp_utf32_21 utf32_3; //!< 21b UTF-32/4B UTF-8 view. + + uint8_t u8[4]; //!< Raw octets view. + + struct ucp_utf8_1 utf8_0; //!< 1B UTF-8 view. + struct ucp_utf8_2 utf8_1; //!< 2B UTF-8 view. + struct ucp_utf8_3 utf8_2; //!< 3B UTF-8 view. + struct ucp_utf8_4 utf8_3; //!< 4B UTF-8 view. + + unsigned char data[4]; //!< Raw bytes view. + char str[4]; //!< Raw characters view. +}; + +_Static_assert(sizeof(union ucp) * CHAR_BIT == 32U,""); + +#if clang_at_least_version(19) +# define BAD_CODE_POINT (const union ucp){.u32 = UINT32_MAX} +#else +# define BAD_CODE_POINT (constexpr const union ucp){.u32 = UINT32_MAX} +#endif + +static const_inline bool +code_point_error (const union ucp c) +{ + return c.u32 == BAD_CODE_POINT.u32; +} + +static const_inline union ucp +utf8_to_utf32 (const union ucp c, + const size_t n) +{ + switch (n) { + case 1: + return (union ucp){ + .utf32_0.b0 = c.utf8_0.b0, + .utf32_0.p0 = 0 + }; + case 2: + return (union ucp){ + .utf32_1.b1 = c.utf8_1.b1, + .utf32_1.b0 = c.utf8_1.b0, + .utf32_1.p0 = 0 + }; + case 3: + return (union ucp){ + .utf32_2.b2 = c.utf8_2.b2, + .utf32_2.b1 = c.utf8_2.b1, + .utf32_2.b0 = c.utf8_2.b0, + .utf32_2.p0 = 0 + }; + case 4: + return (union ucp){ + .utf32_3.b3 = c.utf8_3.b3, + .utf32_3.b2 = c.utf8_3.b2, + .utf32_3.b1 = c.utf8_3.b1, + .utf32_3.b0 = c.utf8_3.b0, + .utf32_3.p0 = 0 + }; + default: + break; + } + + return BAD_CODE_POINT; +} + +static union ucp +utf8_code_point (struct utf8 *const u8p) +{ + uint8_t const *const d = (uint8_t const *)utf8_result(u8p); + return utf8_to_utf32( + (union ucp){.u8 = {d[0], d[1], d[2], d[3]}}, + utf8_size(u8p) + ); +} + +int +main (int c, + char **v) +{ + struct letopt opt = letopt_init(c, v); + + if (letopt_nargs(&opt) < 1 || opt.m_help) + letopt_helpful_exit(&opt); + + size_t buf_sz = opt.m_separate ? arg_sizes_max(&opt) + : arg_sizes_sum(&opt); + buf_sz = saturated_add_uz( + saturated_add_uz(buf_sz, 2U), + saturated_add_uz(buf_sz, buf_sz)); + + if (buf_sz == SIZE_MAX) { + pr_errno_(EOVERFLOW, "refusing to allocate buffer"); + (void)letopt_fini(&opt); + return EXIT_FAILURE; + } + + char *buf = malloc(buf_sz); + if (!buf) { + pr_errno_(errno, "malloc(%zu)", buf_sz); + (void)letopt_fini(&opt); + return EXIT_FAILURE; + } + + struct utf8 u8p = utf8(); + uint64_t n[] = {0,0,0,0}; + //char *out = buf; + + for (int i = 0; i < letopt_nargs(&opt); ++i) { + uint8_t const *p = (uint8_t const *)letopt_arg(&opt, i); + for (uint8_t const *q = p; *q; q = p) { + p = utf8_parse_next_code_point(&u8p, q); + if (u8p.error) { + if (opt.m_replace && (*p || opt.m_separate)) { + const union ucp uc = utf8_to_utf32( + (union ucp){ + .u8 = {0xefU, 0xbfU, + 0xbdU, 0x00U} + }, + 3U + ); + printf("U+%04" PRIx32 "\n", uc.u32); +/* + *out++ = (char)(unsigned char)0xefU; + *out++ = (char)(unsigned char)0xbfU; + *out++ = (char)(unsigned char)0xbdU; +*/ + } + if (!*p) + break; + if (p == q && utf8_expects_leading_byte(&u8p)) + ++p; + utf8_reset(&u8p); + continue; + } + + const union ucp uc = utf8_code_point(&u8p); + if (!code_point_error(uc)) + printf("U+%04" PRIx32 "\n", uc.u32); + } + + if (opt.m_separate) { +/* + if (out == buf || *(out - 1) != '\n') + *out++ = '\n'; + *out = '\0'; + out = buf; + (void)printf("%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 + "\t%" PRIu64 "\t%" PRIu64 "\t%s", + n[0], n[1], n[2], n[3], n[0] + + 2U*n[1] + 3U*n[2] + 4U*n[3], buf); +*/ + __builtin_memset(&n[0], 0, sizeof n); + utf8_reset(&u8p); + } + } +/* + if (!opt.m_separate) { + if (out == buf || *(out - 1) != '\n') + *out++ = '\n'; + *out = '\0'; + (void)printf("%" PRIu64 "\t%" PRIu64 "\t%" PRIu64 + "\t%" PRIu64 "\t%" PRIu64 "\t%s", + n[0], n[1], n[2], n[3], n[0] + + 2U*n[1] + 3U*n[2] + 4U*n[3], buf); + } +*/ + free(buf); + return letopt_fini(&opt); +} diff --git a/src/test-json.c b/src/test-json.c new file mode 100644 index 0000000..fec0006 --- /dev/null +++ b/src/test-json.c @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/** @file test-json.c + * + * @author Juuso Alasuutari + */ +#include "json.h" + +int +main (int c, + char **v) +{ + for (int i = 0; ++i < c;) { + json_parse(v[i]); + } +} diff --git a/src/test-rev.c b/src/test-rev.c new file mode 100644 index 0000000..b7bb304 --- /dev/null +++ b/src/test-rev.c @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/** @file test-rev.c + * + * @author Juuso Alasuutari + */ +#include +#include + +#include "parse.h" +#include "rev.h" + +struct asc4 { + char d[4]; +}; + +__attribute__((const)) +static struct asc4 +bin_asc (uint4 x) +{ + + union { + char c[35U]; + unsigned char u[35U]; + } const t = { + "1111000010011010" "111" + "\x04""\x05""\x06" + "\x09""\x07""\x0d" + "\x0a""\x0f""\x03" + "\x08""\x0c""\x0e" + "\x02""\x0b""\x01" + }; + unsigned const i = t.u[ + 19U + uint4_get(x) + ]; + return (struct asc4){ .d = { + t.c[i+0U], t.c[i+1U], + t.c[i+2U], t.c[i+3U] + }}; +} + +int +main (int c, + char **v) +{ + for (int i = 0; ++i < c;) { + struct parsed p = parse_u64(v[i]); + if (!parsed_ok(p, v[i])) + continue; + + (void)printf("%016" PRIx64 " " + "%016" PRIx64 "\n", + p.u64, rev64(p.u64)); + + if (p.u64 > UINT32_MAX) + continue; + (void)printf(" %0" + "8" PRIx64 " " + "%08" PRIx32 "\n", + p.u64, rev32((uint32_t)p.u64)); + + if (p.u64 > UINT16_MAX) + continue; + (void)printf(" %0" + "4" PRIx64 " " + "%04" PRIx16 "\n", + p.u64, rev16((uint16_t)p.u64)); + + if (p.u64 > UINT8_MAX) + continue; + (void)printf(" %0" + "2" PRIx64 " " + "%02" PRIx8 "\n", + p.u64, rev8((uint8_t)p.u64)); + + if (p.u64 > 15U) + continue; + (void)printf(" " + "%" PRIx64 " %x\n", + p.u64, (unsigned)uint4_get(rev4(uint4(p.u64)))); + + if (p.u64 > 3U) + continue; + (void)printf(" " + "%" PRIx64 " %x\n", + p.u64, (unsigned)uint2_get(rev2(uint2(p.u64)))); + } +} diff --git a/src/test.c b/src/test.c index 55771d2..24d2a60 100644 --- a/src/test.c +++ b/src/test.c @@ -25,14 +25,14 @@ main (int argc, pr_out("%s / %s", canth_c_version(), canth_cxx_version()); for (int i = 0; ++i < argc;) { - struct file_in f = file_read(argv[i]); - int e = file_error(&f); + struct file_in f = file_in(argv[i], nullptr, 0, FILE_IN_TEXT); + int e = file_in_error(&f); if (e) { pr_errno(e, "file_read"); ret = EXIT_FAILURE; continue; } - char const *txt = file_text(&f); + char const *txt = file_in_text(&f); cJSON *json = cJSON_Parse(txt); if (!json) { pr_err_("parsing failed"); diff --git a/src/utf8.c b/src/utf8.c index 311232c..17ad532 100644 --- a/src/utf8.c +++ b/src/utf8.c @@ -310,3 +310,33 @@ utf8_parse_next_code_point (struct utf8 *const u8p, return ptr; } + +nonnull_in() nonnull_out +uint8_t const * +utf8_parse_next_code_point2 (struct utf8 *const u8p, + uint8_t const *ptr, + uint8_t const *const end) +{ + enum utf8_st8 st8 = utf8_ini; + + if (utf8_get_state(u8p, &st8)) { + for (;;) { + if (ptr >= end) { + u8p->error = ENODATA; + break; + } + + if (!utf8_set_state(u8p, &st8, *ptr)) + break; + + ++ptr; + + if (utf8_done(u8p)) { + u8p->error = 0; + break; + } + } + } + + return ptr; +} diff --git a/src/utf8.h b/src/utf8.h index 1a4d8ee..80dca4e 100644 --- a/src/utf8.h +++ b/src/utf8.h @@ -224,6 +224,41 @@ extern uint8_t const * utf8_parse_next_code_point (struct utf8 *u8p, uint8_t const *ptr) nonnull_in(); +/** + * @brief Parse the next UTF-8 code point from a byte buffer + * bounded by an end pointer. + * + * Consumes up to 4 bytes of UTF-8 encoded input until a code + * point is assembled, the buffer end is reached, or an error + * occurs. Returns the address of the next byte following the + * consumed bytes. + * + * This is otherwise like @ref utf8_parse_next_code_point() + * except that the user doesn't need to ensure there are at + * least 4 bytes available in the input buffer. Instead, if + * the buffer end is reached prematurely, the `end` pointer + * is returned and `u8p->error` is assigned `ENODATA`. + * + * @param u8p A pointer to the UTF-8 parser object. Must not + * be null. + * @param ptr A pointer to the input buffer. Must not be null. + * @param end A pointer to the byte one past the end of the + * input buffer. Must not be null. + * @return A pointer to the byte immediately after the parsed + * code point, the `end` pointer if the buffer end is + * reached, or a pointer to the first invalid byte if + * malformed input is encountered. Note that the `end` + * pointer is returned both when successfully parsing + * the last code point, and when reaching the buffer + * end prematurely; check `u8p->error` to distinguish + * between these cases. + */ +nonnull_out +extern uint8_t const * +utf8_parse_next_code_point2 (struct utf8 *u8p, + uint8_t const *ptr, + uint8_t const *end) nonnull_in(); + /** * @brief Get the result of the last UTF-8 code point parsing operation. * diff --git a/src/util.h b/src/util.h index 7eec07e..2a3e35c 100644 --- a/src/util.h +++ b/src/util.h @@ -23,32 +23,43 @@ diag_clang(pop) #define naught(...) +#ifndef _MSC_VER + /** @brief Instruct the compiler to always inline a function. */ -#define force_inline __attribute__((always_inline)) inline +# define force_inline __attribute__((always_inline)) inline /** @brief Instruct the compiler to always inline a function * and to assume its return value is determined only * by its arguments. */ -#define const_inline __attribute__((always_inline,const)) inline +# define const_inline __attribute__((always_inline,const)) inline /** @brief Function returns a specific baked-in data pointer. */ -#define const_nonnull __attribute__((const,returns_nonnull)) +# define const_nonnull __attribute__((const,returns_nonnull)) /** @brief Assume that the specified argument indices are not null. */ -#define nonnull_in(...) __attribute__(( \ - nonnull maybe_parenthesize(__VA_ARGS__))) +# define nonnull_in(...) __attribute__(( \ + nonnull maybe_parenthesize(__VA_ARGS__))) /** @brief Assume that the return value of a function is not null. */ -#define nonnull_out __attribute__((returns_nonnull)) +# define nonnull_out __attribute__((returns_nonnull)) /** @brief Suppress compiler warnings about an unused entity. */ -#define useless __attribute__((unused)) +# define useless __attribute__((unused)) + +#else /* _MSC_VER */ +# define force_inline __forceinline +# define const_inline __forceinline +# define const_nonnull +# define nonnull_in(...) +# define nonnull_out +# define useless +#endif /* _MSC_VER */ /** @brief Calculate the element count of an array. */ @@ -89,6 +100,12 @@ diag_clang(pop) unsigned short:1, unsigned:1, \ typeof(1UL):1,typeof(1ULL):1) < (typeof(x))0) +#define uint_type(x) typeof(_Generic(*(typeof(x) *)(void *)0, \ + signed char: (unsigned char)0, unsigned char: (unsigned char)0, \ + signed short: (unsigned short)0, unsigned short: (unsigned short)0, \ + signed int: 0U, unsigned: 0U, signed long: 0UL, unsigned long: 0UL, \ + signed long long: 0ULL, unsigned long long: 0ULL)) + /** @brief Check if a value is a char array. */ #define is_char_array(x) _Generic((typeof(x) *){0}, \