From 80607a827aec830c2da51cdecd385eb04e3886b8 Mon Sep 17 00:00:00 2001 From: Rom Date: Sun, 17 May 2026 07:44:28 +0700 Subject: [PATCH 1/3] feat: update NitroBase64 - json and yarn.lock to upgrade nitrogen and react-native-nitro-modules to version 0.35.6. --- README.md | 81 +- cpp/NitroBase64.hpp | 44 +- cpp/simdutf.cpp | 8879 +++++++++-------- cpp/simdutf.h | 6916 ++++++++++++- example/Gemfile.lock | 22 +- .../project.pbxproj | 50 +- example/ios/Podfile.lock | 38 +- example/package.json | 4 +- example/src/Benchmark.tsx | 10 +- example/src/Benchmark2.tsx | 452 +- nitro.json | 5 +- .../android/NitroBase64+autolinking.cmake | 2 +- .../android/NitroBase64+autolinking.gradle | 2 +- .../generated/android/NitroBase64OnLoad.cpp | 41 +- .../generated/android/NitroBase64OnLoad.hpp | 19 +- .../nitro/nitrobase64/NitroBase64OnLoad.kt | 2 +- .../generated/ios/NitroBase64+autolinking.rb | 6 +- .../ios/NitroBase64-Swift-Cxx-Bridge.cpp | 2 +- .../ios/NitroBase64-Swift-Cxx-Bridge.hpp | 2 +- .../ios/NitroBase64-Swift-Cxx-Umbrella.hpp | 2 +- .../generated/ios/NitroBase64Autolinking.mm | 2 +- .../ios/NitroBase64Autolinking.swift | 6 +- .../shared/c++/HybridNitroBase64Spec.cpp | 4 +- .../shared/c++/HybridNitroBase64Spec.hpp | 7 +- package.json | 6 +- src/NitroBase64.nitro.ts | 4 +- src/index.tsx | 23 +- yarn.lock | 108 +- 28 files changed, 11915 insertions(+), 4824 deletions(-) diff --git a/README.md b/README.md index 79c3ca3..660bcef 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ A high-performance, cross-platform Base64 encoding/decoding module for React Nat ```sh yarn add react-native-nitro-base64 -yarn add react-native-nitro-modules@0.29.8 +yarn add react-native-nitro-modules ``` ### Linking @@ -24,39 +24,72 @@ This module uses [react-native-nitro-modules](https://github.com/mrousavy/nitro) ## API +| Function | Input | Output | Use case | +|---|---|---|---| +| `encode(str, urlSafe?)` | binary string | base64 string | Text/JWT encoding | +| `decode(base64)` | base64 string | binary string | Text/JWT decoding — accepts standard and URL-safe automatically | +| `encodeBuffer(ArrayBuffer, urlSafe?)` | ArrayBuffer | base64 string | Image/file → base64 | +| `decodeBuffer(base64)` | base64 string | ArrayBuffer | base64 → raw binary — accepts standard and URL-safe automatically | +| `fromByteArray(Uint8Array, urlSafe?)` | Uint8Array | base64 string | Crypto/hashing output → base64 | +| `toByteArray(base64)` | base64 string | Uint8Array | base64 → typed binary — accepts standard and URL-safe automatically | + +### Which one should I use? + +**Working with images?** Use `encodeBuffer` / `decodeBuffer`. + +Images are binary data. Passing them through a JS string crosses the JSI bridge as UTF-16, doubling memory and adding overhead. `ArrayBuffer` avoids that — raw bytes go directly to C++. + ```typescript -export interface NitroBase64 extends HybridObject<{ ios: 'c++'; android: 'c++' }> { - install(): void; - encode(input: string, urlSafe: boolean): string; - decode(base64: string, urlSafe: boolean): string; -} +import { encodeBuffer, decodeBuffer } from 'react-native-nitro-base64'; +import { readFile } from 'react-native-fs'; + +// Image file → base64 (for API upload) +const bytes = await readFile(imagePath, 'ascii'); // or use arrayBuffer APIs +const base64 = encodeBuffer(buffer); // ArrayBuffer → base64 + +// base64 from API → display in +const buffer = decodeBuffer(base64String); // base64 → ArrayBuffer ``` -- `install(): void` — Initializes the native module for faster first-time encoding/decoding. Call this early in your app lifecycle via index.js or root -- `encode(input: string, urlSafe: boolean): string` — Encodes binary data to base64. Set `urlSafe` to `true` for base64url encoding. -- `decode(base64: string, urlSafe: boolean): string` — Decodes base64 (WHATWG forgiving) to binary. Set `urlSafe` to `true` for base64url decoding. +**Working with text / JWT?** Use `encode` / `decode`. -## Usage -```javascript -index.js -import { install } from 'react-native-nitro-base64'; -import { AppRegistry } from 'react-native'; -import { install } from 'react-native-nitro-base64'; -import { name as appName } from './app.json'; -import App from './src/App'; -install(); // <=== Call -AppRegistry.registerComponent(appName, () => App); +```typescript +import { encode, decode } from 'react-native-nitro-base64'; +const encoded = encode('Hello World!'); // "SGVsbG8gV29ybGQh" +const decoded = decode(encoded); // "Hello World!" +// URL-safe (JWT, URL params) +const token = encode(payload, true); +``` + +**Working with Web Crypto / hashing?** Use `fromByteArray` / `toByteArray`. + +Web Crypto APIs (`crypto.subtle`) return `Uint8Array`. These are drop-in compatible with `react-native-quick-base64`. ```typescript -import { encode,decode } from 'react-native-nitro-base64'; +import { fromByteArray, toByteArray } from 'react-native-nitro-base64'; -const encoded = encode('Hello World!', false); // "SGVsbG8gV29ybGQh" -const urlEncoded = encode('Hello World!', true); // "SGVsbG8gV29ybGQh" +// Hash → base64 +const hash = new Uint8Array(await crypto.subtle.digest('SHA-256', data)); +const encoded = fromByteArray(hash); -const decoded = decode(encoded, false); // "Hello World!" -const urlDecoded = decode(urlEncoded, true); // "Hello World!" +// base64 → bytes for TextDecoder +const bytes = toByteArray(base64String); +const text = new TextDecoder().decode(bytes); +``` + +## Setup + +```javascript +// index.js +import { install } from 'react-native-nitro-base64'; +import { AppRegistry } from 'react-native'; +import { name as appName } from './app.json'; +import App from './src/App'; + +install(); // call before app mounts +AppRegistry.registerComponent(appName, () => App); ``` ## Platform Support diff --git a/cpp/NitroBase64.hpp b/cpp/NitroBase64.hpp index c33ddcd..f71ce64 100644 --- a/cpp/NitroBase64.hpp +++ b/cpp/NitroBase64.hpp @@ -2,6 +2,7 @@ #include "HybridNitroBase64Spec.hpp" #include "simdutf.h" +#include namespace margelo::nitro::nitrobase64 { using namespace margelo::nitro; @@ -27,7 +28,7 @@ namespace margelo::nitro::nitrobase64 { return output; } - std::string decode(const std::string &base64,bool urlSafe = false) override { + std::string decode(const std::string &base64) override { size_t max_decoded_size = simdutf::maximal_binary_length_from_base64( base64.data(), base64.size() @@ -39,7 +40,7 @@ namespace margelo::nitro::nitrobase64 { base64.data(), base64.size(), output.data(), - urlSafe ? simdutf::base64_url : simdutf::base64_default + simdutf::base64_default_or_url ); if (result.error != simdutf::error_code::SUCCESS) { @@ -50,6 +51,45 @@ namespace margelo::nitro::nitrobase64 { return output; } + std::string encodeBuffer(const std::shared_ptr& input, bool urlSafe = false) override { + auto opts = urlSafe ? simdutf::base64_url : simdutf::base64_default; + size_t base64_size = simdutf::base64_length_from_binary(input->size(), opts); + std::string output(base64_size, '\0'); + + size_t actual_size = simdutf::binary_to_base64( + reinterpret_cast(input->data()), + input->size(), + output.data(), + opts + ); + + output.resize(actual_size); + return output; + } + + std::shared_ptr decodeBuffer(const std::string &base64) override { + size_t max_decoded_size = simdutf::maximal_binary_length_from_base64( + base64.data(), + base64.size() + ); + + std::vector buffer(max_decoded_size); + + auto result = simdutf::base64_to_binary( + base64.data(), + base64.size(), + reinterpret_cast(buffer.data()), + simdutf::base64_default_or_url + ); + + if (result.error != simdutf::error_code::SUCCESS) { + throw std::runtime_error("Base64 decode failed"); + } + + buffer.resize(result.count); + return ArrayBuffer::move(std::move(buffer)); + } + private: static constexpr auto TAG = "NitroBase64"; }; diff --git a/cpp/simdutf.cpp b/cpp/simdutf.cpp index 28843c9..5bba36d 100644 --- a/cpp/simdutf.cpp +++ b/cpp/simdutf.cpp @@ -1,910 +1,10 @@ -/* auto-generated on 2025-09-10 16:01:38 -0400. Do not edit! */ +/* auto-generated on 2026-05-07 06:54:06 +0200. Do not edit! */ /* begin file src/simdutf.cpp */ #include "simdutf.h" - // We include base64_tables once. -/* begin file src/tables/base64_tables.h */ -#ifndef SIMDUTF_BASE64_TABLES_H -#define SIMDUTF_BASE64_TABLES_H -#include -#include - -namespace simdutf { -namespace { -namespace tables { -namespace base64 { -namespace base64_default { - -const char e0[256] = { - 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'C', 'C', 'C', 'C', 'D', 'D', 'D', - 'D', 'E', 'E', 'E', 'E', 'F', 'F', 'F', 'F', 'G', 'G', 'G', 'G', 'H', 'H', - 'H', 'H', 'I', 'I', 'I', 'I', 'J', 'J', 'J', 'J', 'K', 'K', 'K', 'K', 'L', - 'L', 'L', 'L', 'M', 'M', 'M', 'M', 'N', 'N', 'N', 'N', 'O', 'O', 'O', 'O', - 'P', 'P', 'P', 'P', 'Q', 'Q', 'Q', 'Q', 'R', 'R', 'R', 'R', 'S', 'S', 'S', - 'S', 'T', 'T', 'T', 'T', 'U', 'U', 'U', 'U', 'V', 'V', 'V', 'V', 'W', 'W', - 'W', 'W', 'X', 'X', 'X', 'X', 'Y', 'Y', 'Y', 'Y', 'Z', 'Z', 'Z', 'Z', 'a', - 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'c', 'd', 'd', 'd', 'd', - 'e', 'e', 'e', 'e', 'f', 'f', 'f', 'f', 'g', 'g', 'g', 'g', 'h', 'h', 'h', - 'h', 'i', 'i', 'i', 'i', 'j', 'j', 'j', 'j', 'k', 'k', 'k', 'k', 'l', 'l', - 'l', 'l', 'm', 'm', 'm', 'm', 'n', 'n', 'n', 'n', 'o', 'o', 'o', 'o', 'p', - 'p', 'p', 'p', 'q', 'q', 'q', 'q', 'r', 'r', 'r', 'r', 's', 's', 's', 's', - 't', 't', 't', 't', 'u', 'u', 'u', 'u', 'v', 'v', 'v', 'v', 'w', 'w', 'w', - 'w', 'x', 'x', 'x', 'x', 'y', 'y', 'y', 'y', 'z', 'z', 'z', 'z', '0', '0', - '0', '0', '1', '1', '1', '1', '2', '2', '2', '2', '3', '3', '3', '3', '4', - '4', '4', '4', '5', '5', '5', '5', '6', '6', '6', '6', '7', '7', '7', '7', - '8', '8', '8', '8', '9', '9', '9', '9', '+', '+', '+', '+', '/', '/', '/', - '/'}; - -const char e1[256] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', - 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', - 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', - 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', - 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', - 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', - 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', - 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', 'C', - 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', - 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', - 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', - '/'}; - -const char e2[256] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', - 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', - 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', - 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', - 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', - 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', - 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', - 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', 'C', - 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', - 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', - 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', - '/'}; - -const uint32_t d0[256] = { - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x000000f8, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000fc, - 0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4, - 0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, - 0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018, - 0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030, - 0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048, - 0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060, - 0x00000064, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078, - 0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090, - 0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8, - 0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0, - 0x000000c4, 0x000000c8, 0x000000cc, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; - -const uint32_t d1[256] = { - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x0000e003, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000f003, - 0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003, - 0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, - 0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000, - 0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000, - 0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001, - 0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001, - 0x00009001, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001, - 0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002, - 0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002, - 0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003, - 0x00001003, 0x00002003, 0x00003003, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; - -const uint32_t d2[256] = { - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x00800f00, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00c00f00, - 0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00, - 0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, - 0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100, - 0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300, - 0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400, - 0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600, - 0x00400600, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700, - 0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900, - 0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00, - 0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00, - 0x00400c00, 0x00800c00, 0x00c00c00, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; - -const uint32_t d3[256] = { - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x003e0000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003f0000, - 0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000, - 0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, - 0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000, - 0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000, - 0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000, - 0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000, - 0x00190000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000, - 0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000, - 0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000, - 0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000, - 0x00310000, 0x00320000, 0x00330000, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; -} // namespace base64_default - -namespace base64_url { - -const char e0[256] = { - 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'C', 'C', 'C', 'C', 'D', 'D', 'D', - 'D', 'E', 'E', 'E', 'E', 'F', 'F', 'F', 'F', 'G', 'G', 'G', 'G', 'H', 'H', - 'H', 'H', 'I', 'I', 'I', 'I', 'J', 'J', 'J', 'J', 'K', 'K', 'K', 'K', 'L', - 'L', 'L', 'L', 'M', 'M', 'M', 'M', 'N', 'N', 'N', 'N', 'O', 'O', 'O', 'O', - 'P', 'P', 'P', 'P', 'Q', 'Q', 'Q', 'Q', 'R', 'R', 'R', 'R', 'S', 'S', 'S', - 'S', 'T', 'T', 'T', 'T', 'U', 'U', 'U', 'U', 'V', 'V', 'V', 'V', 'W', 'W', - 'W', 'W', 'X', 'X', 'X', 'X', 'Y', 'Y', 'Y', 'Y', 'Z', 'Z', 'Z', 'Z', 'a', - 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'c', 'd', 'd', 'd', 'd', - 'e', 'e', 'e', 'e', 'f', 'f', 'f', 'f', 'g', 'g', 'g', 'g', 'h', 'h', 'h', - 'h', 'i', 'i', 'i', 'i', 'j', 'j', 'j', 'j', 'k', 'k', 'k', 'k', 'l', 'l', - 'l', 'l', 'm', 'm', 'm', 'm', 'n', 'n', 'n', 'n', 'o', 'o', 'o', 'o', 'p', - 'p', 'p', 'p', 'q', 'q', 'q', 'q', 'r', 'r', 'r', 'r', 's', 's', 's', 's', - 't', 't', 't', 't', 'u', 'u', 'u', 'u', 'v', 'v', 'v', 'v', 'w', 'w', 'w', - 'w', 'x', 'x', 'x', 'x', 'y', 'y', 'y', 'y', 'z', 'z', 'z', 'z', '0', '0', - '0', '0', '1', '1', '1', '1', '2', '2', '2', '2', '3', '3', '3', '3', '4', - '4', '4', '4', '5', '5', '5', '5', '6', '6', '6', '6', '7', '7', '7', '7', - '8', '8', '8', '8', '9', '9', '9', '9', '-', '-', '-', '-', '_', '_', '_', - '_'}; - -const char e1[256] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', - 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', - 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', - 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', '-', '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', - 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '-', '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G', - 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', - 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', - 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', 'A', 'B', 'C', - 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', - 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', - 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', - '_'}; - -const char e2[256] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', - 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', - 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', - 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', '-', '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', - 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '-', '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G', - 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', - 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', - 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', 'A', 'B', 'C', - 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', - 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', - 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', - '_'}; - -const uint32_t d0[256] = { - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000f8, 0x01ffffff, 0x01ffffff, - 0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4, - 0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, - 0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018, - 0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030, - 0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048, - 0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060, - 0x00000064, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000fc, - 0x01ffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078, - 0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090, - 0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8, - 0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0, - 0x000000c4, 0x000000c8, 0x000000cc, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; -const uint32_t d1[256] = { - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000e003, 0x01ffffff, 0x01ffffff, - 0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003, - 0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, - 0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000, - 0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000, - 0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001, - 0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001, - 0x00009001, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000f003, - 0x01ffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001, - 0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002, - 0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002, - 0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003, - 0x00001003, 0x00002003, 0x00003003, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; -const uint32_t d2[256] = { - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00800f00, 0x01ffffff, 0x01ffffff, - 0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00, - 0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, - 0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100, - 0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300, - 0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400, - 0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600, - 0x00400600, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00c00f00, - 0x01ffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700, - 0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900, - 0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00, - 0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00, - 0x00400c00, 0x00800c00, 0x00c00c00, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; -const uint32_t d3[256] = { - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003e0000, 0x01ffffff, 0x01ffffff, - 0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000, - 0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, - 0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000, - 0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000, - 0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000, - 0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000, - 0x00190000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003f0000, - 0x01ffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000, - 0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000, - 0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000, - 0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000, - 0x00310000, 0x00320000, 0x00330000, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; -} // namespace base64_url - -namespace base64_default_or_url { -const uint32_t d0[256] = { - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x000000f8, 0x01ffffff, 0x000000f8, 0x01ffffff, 0x000000fc, - 0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4, - 0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, - 0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018, - 0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030, - 0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048, - 0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060, - 0x00000064, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000fc, - 0x01ffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078, - 0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090, - 0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8, - 0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0, - 0x000000c4, 0x000000c8, 0x000000cc, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; -const uint32_t d1[256] = { - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x0000e003, 0x01ffffff, 0x0000e003, 0x01ffffff, 0x0000f003, - 0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003, - 0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, - 0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000, - 0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000, - 0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001, - 0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001, - 0x00009001, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000f003, - 0x01ffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001, - 0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002, - 0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002, - 0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003, - 0x00001003, 0x00002003, 0x00003003, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; -const uint32_t d2[256] = { - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x00800f00, 0x01ffffff, 0x00800f00, 0x01ffffff, 0x00c00f00, - 0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00, - 0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, - 0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100, - 0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300, - 0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400, - 0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600, - 0x00400600, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00c00f00, - 0x01ffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700, - 0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900, - 0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00, - 0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00, - 0x00400c00, 0x00800c00, 0x00c00c00, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; -const uint32_t d3[256] = { - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x003e0000, 0x01ffffff, 0x003e0000, 0x01ffffff, 0x003f0000, - 0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000, - 0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, - 0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000, - 0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000, - 0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000, - 0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000, - 0x00190000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003f0000, - 0x01ffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000, - 0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000, - 0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000, - 0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000, - 0x00310000, 0x00320000, 0x00330000, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, - 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; -} // namespace base64_default_or_url -const uint64_t thintable_epi8[256] = { - 0x0706050403020100, 0x0007060504030201, 0x0007060504030200, - 0x0000070605040302, 0x0007060504030100, 0x0000070605040301, - 0x0000070605040300, 0x0000000706050403, 0x0007060504020100, - 0x0000070605040201, 0x0000070605040200, 0x0000000706050402, - 0x0000070605040100, 0x0000000706050401, 0x0000000706050400, - 0x0000000007060504, 0x0007060503020100, 0x0000070605030201, - 0x0000070605030200, 0x0000000706050302, 0x0000070605030100, - 0x0000000706050301, 0x0000000706050300, 0x0000000007060503, - 0x0000070605020100, 0x0000000706050201, 0x0000000706050200, - 0x0000000007060502, 0x0000000706050100, 0x0000000007060501, - 0x0000000007060500, 0x0000000000070605, 0x0007060403020100, - 0x0000070604030201, 0x0000070604030200, 0x0000000706040302, - 0x0000070604030100, 0x0000000706040301, 0x0000000706040300, - 0x0000000007060403, 0x0000070604020100, 0x0000000706040201, - 0x0000000706040200, 0x0000000007060402, 0x0000000706040100, - 0x0000000007060401, 0x0000000007060400, 0x0000000000070604, - 0x0000070603020100, 0x0000000706030201, 0x0000000706030200, - 0x0000000007060302, 0x0000000706030100, 0x0000000007060301, - 0x0000000007060300, 0x0000000000070603, 0x0000000706020100, - 0x0000000007060201, 0x0000000007060200, 0x0000000000070602, - 0x0000000007060100, 0x0000000000070601, 0x0000000000070600, - 0x0000000000000706, 0x0007050403020100, 0x0000070504030201, - 0x0000070504030200, 0x0000000705040302, 0x0000070504030100, - 0x0000000705040301, 0x0000000705040300, 0x0000000007050403, - 0x0000070504020100, 0x0000000705040201, 0x0000000705040200, - 0x0000000007050402, 0x0000000705040100, 0x0000000007050401, - 0x0000000007050400, 0x0000000000070504, 0x0000070503020100, - 0x0000000705030201, 0x0000000705030200, 0x0000000007050302, - 0x0000000705030100, 0x0000000007050301, 0x0000000007050300, - 0x0000000000070503, 0x0000000705020100, 0x0000000007050201, - 0x0000000007050200, 0x0000000000070502, 0x0000000007050100, - 0x0000000000070501, 0x0000000000070500, 0x0000000000000705, - 0x0000070403020100, 0x0000000704030201, 0x0000000704030200, - 0x0000000007040302, 0x0000000704030100, 0x0000000007040301, - 0x0000000007040300, 0x0000000000070403, 0x0000000704020100, - 0x0000000007040201, 0x0000000007040200, 0x0000000000070402, - 0x0000000007040100, 0x0000000000070401, 0x0000000000070400, - 0x0000000000000704, 0x0000000703020100, 0x0000000007030201, - 0x0000000007030200, 0x0000000000070302, 0x0000000007030100, - 0x0000000000070301, 0x0000000000070300, 0x0000000000000703, - 0x0000000007020100, 0x0000000000070201, 0x0000000000070200, - 0x0000000000000702, 0x0000000000070100, 0x0000000000000701, - 0x0000000000000700, 0x0000000000000007, 0x0006050403020100, - 0x0000060504030201, 0x0000060504030200, 0x0000000605040302, - 0x0000060504030100, 0x0000000605040301, 0x0000000605040300, - 0x0000000006050403, 0x0000060504020100, 0x0000000605040201, - 0x0000000605040200, 0x0000000006050402, 0x0000000605040100, - 0x0000000006050401, 0x0000000006050400, 0x0000000000060504, - 0x0000060503020100, 0x0000000605030201, 0x0000000605030200, - 0x0000000006050302, 0x0000000605030100, 0x0000000006050301, - 0x0000000006050300, 0x0000000000060503, 0x0000000605020100, - 0x0000000006050201, 0x0000000006050200, 0x0000000000060502, - 0x0000000006050100, 0x0000000000060501, 0x0000000000060500, - 0x0000000000000605, 0x0000060403020100, 0x0000000604030201, - 0x0000000604030200, 0x0000000006040302, 0x0000000604030100, - 0x0000000006040301, 0x0000000006040300, 0x0000000000060403, - 0x0000000604020100, 0x0000000006040201, 0x0000000006040200, - 0x0000000000060402, 0x0000000006040100, 0x0000000000060401, - 0x0000000000060400, 0x0000000000000604, 0x0000000603020100, - 0x0000000006030201, 0x0000000006030200, 0x0000000000060302, - 0x0000000006030100, 0x0000000000060301, 0x0000000000060300, - 0x0000000000000603, 0x0000000006020100, 0x0000000000060201, - 0x0000000000060200, 0x0000000000000602, 0x0000000000060100, - 0x0000000000000601, 0x0000000000000600, 0x0000000000000006, - 0x0000050403020100, 0x0000000504030201, 0x0000000504030200, - 0x0000000005040302, 0x0000000504030100, 0x0000000005040301, - 0x0000000005040300, 0x0000000000050403, 0x0000000504020100, - 0x0000000005040201, 0x0000000005040200, 0x0000000000050402, - 0x0000000005040100, 0x0000000000050401, 0x0000000000050400, - 0x0000000000000504, 0x0000000503020100, 0x0000000005030201, - 0x0000000005030200, 0x0000000000050302, 0x0000000005030100, - 0x0000000000050301, 0x0000000000050300, 0x0000000000000503, - 0x0000000005020100, 0x0000000000050201, 0x0000000000050200, - 0x0000000000000502, 0x0000000000050100, 0x0000000000000501, - 0x0000000000000500, 0x0000000000000005, 0x0000000403020100, - 0x0000000004030201, 0x0000000004030200, 0x0000000000040302, - 0x0000000004030100, 0x0000000000040301, 0x0000000000040300, - 0x0000000000000403, 0x0000000004020100, 0x0000000000040201, - 0x0000000000040200, 0x0000000000000402, 0x0000000000040100, - 0x0000000000000401, 0x0000000000000400, 0x0000000000000004, - 0x0000000003020100, 0x0000000000030201, 0x0000000000030200, - 0x0000000000000302, 0x0000000000030100, 0x0000000000000301, - 0x0000000000000300, 0x0000000000000003, 0x0000000000020100, - 0x0000000000000201, 0x0000000000000200, 0x0000000000000002, - 0x0000000000000100, 0x0000000000000001, 0x0000000000000000, - 0x0000000000000000, -}; - -const uint8_t pshufb_combine_table[272] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, - 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, - 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x00, 0x01, 0x02, 0x03, - 0x04, 0x05, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x08, 0x09, 0x0a, 0x0b, - 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x08, - 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x01, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x08, 0x09, 0x0a, 0x0b, - 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -}; - -const unsigned char BitsSetTable256mul2[256] = { - 0, 2, 2, 4, 2, 4, 4, 6, 2, 4, 4, 6, 4, 6, 6, 8, 2, 4, 4, - 6, 4, 6, 6, 8, 4, 6, 6, 8, 6, 8, 8, 10, 2, 4, 4, 6, 4, 6, - 6, 8, 4, 6, 6, 8, 6, 8, 8, 10, 4, 6, 6, 8, 6, 8, 8, 10, 6, - 8, 8, 10, 8, 10, 10, 12, 2, 4, 4, 6, 4, 6, 6, 8, 4, 6, 6, 8, - 6, 8, 8, 10, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 10, 8, 10, 10, - 12, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 10, 8, 10, 10, 12, 6, 8, - 8, 10, 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 12, 14, 2, 4, 4, 6, 4, - 6, 6, 8, 4, 6, 6, 8, 6, 8, 8, 10, 4, 6, 6, 8, 6, 8, 8, 10, - 6, 8, 8, 10, 8, 10, 10, 12, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, - 10, 8, 10, 10, 12, 6, 8, 8, 10, 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, - 12, 14, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 10, 8, 10, 10, 12, 6, - 8, 8, 10, 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 12, 14, 6, 8, 8, 10, - 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 12, 14, 8, 10, 10, 12, 10, 12, 12, - 14, 10, 12, 12, 14, 12, 14, 14, 16}; - -constexpr uint8_t to_base64_value[] = { - 255, 255, 255, 255, 255, 255, 255, 255, 255, 64, 64, 255, 64, 64, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 64, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, - 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, - 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, - 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255}; - -constexpr uint8_t to_base64_url_value[] = { - 255, 255, 255, 255, 255, 255, 255, 255, 255, 64, 64, 255, 64, 64, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 64, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 62, 255, 255, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, - 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 255, 255, 255, 255, 63, 255, 26, 27, 28, 29, 30, 31, 32, 33, - 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255}; - -constexpr uint8_t to_base64_default_or_url_value[] = { - 255, 255, 255, 255, 255, 255, 255, 255, 255, 64, 64, 255, 64, 64, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 64, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, - 62, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, - 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 255, 255, 255, 255, 63, 255, 26, 27, 28, 29, 30, 31, 32, 33, - 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255}; - -static_assert(sizeof(to_base64_value) == 256, - "to_base64_value must have 256 elements"); -static_assert(sizeof(to_base64_url_value) == 256, - "to_base64_url_value must have 256 elements"); -static_assert(to_base64_value[uint8_t(' ')] == 64, - "space must be == 64 in to_base64_value"); -static_assert(to_base64_url_value[uint8_t(' ')] == 64, - "space must be == 64 in to_base64_url_value"); -static_assert(to_base64_value[uint8_t('\t')] == 64, - "tab must be == 64 in to_base64_value"); -static_assert(to_base64_url_value[uint8_t('\t')] == 64, - "tab must be == 64 in to_base64_url_value"); -static_assert(to_base64_value[uint8_t('\r')] == 64, - "cr must be == 64 in to_base64_value"); -static_assert(to_base64_url_value[uint8_t('\r')] == 64, - "cr must be == 64 in to_base64_url_value"); -static_assert(to_base64_value[uint8_t('\n')] == 64, - "lf must be == 64 in to_base64_value"); -static_assert(to_base64_url_value[uint8_t('\n')] == 64, - "lf must be == 64 in to_base64_url_value"); -static_assert(to_base64_value[uint8_t('\f')] == 64, - "ff must be == 64 in to_base64_value"); -static_assert(to_base64_url_value[uint8_t('\f')] == 64, - "ff must be == 64 in to_base64_url_value"); -static_assert(to_base64_value[uint8_t('+')] == 62, - "+ must be == 62 in to_base64_value"); -static_assert(to_base64_url_value[uint8_t('-')] == 62, - "- must be == 62 in to_base64_url_value"); -static_assert(to_base64_value[uint8_t('/')] == 63, - "/ must be == 62 in to_base64_value"); -static_assert(to_base64_url_value[uint8_t('_')] == 63, - "_ must be == 62 in to_base64_url_value"); -} // namespace base64 -} // namespace tables -} // unnamed namespace -} // namespace simdutf - -#endif // SIMDUTF_BASE64_TABLES_H -/* end file src/tables/base64_tables.h */ - /* begin file src/encoding_types.cpp */ namespace simdutf { -bool match_system(endianness e) { -#if SIMDUTF_IS_BIG_ENDIAN - return e == endianness::BIG; -#else - return e == endianness::LITTLE; -#endif -} - -std::string to_string(encoding_type bom) { +std::string_view to_string(encoding_type bom) { switch (bom) { case UTF16_LE: return "UTF16 little-endian"; @@ -2688,27 +1788,35 @@ class implementation final : public simdutf::implementation { simdutf_warn_unused result base64_to_binary( const char *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; simdutf_warn_unused full_result base64_to_binary_details( const char *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; - simdutf_warn_unused result - base64_to_binary(const char16_t *input, size_t length, char *output, - base64_options options, - last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; + simdutf_warn_unused result base64_to_binary( + const char16_t *input, size_t length, char *output, + base64_options options, + last_chunk_handling_options last_chunk_options = + last_chunk_handling_options::loose) const noexcept override; simdutf_warn_unused full_result base64_to_binary_details( const char16_t *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; size_t binary_to_base64(const char *input, size_t length, char *output, - base64_options options) const noexcept; + base64_options options) const noexcept override; + size_t + binary_to_base64_with_lines(const char *input, size_t length, char *output, + size_t line_length, + base64_options options) const noexcept override; const char *find(const char *start, const char *end, - char character) const noexcept; + char character) const noexcept override; const char16_t *find(const char16_t *start, const char16_t *end, - char16_t character) const noexcept; + char16_t character) const noexcept override; + simdutf_warn_unused size_t binary_length_from_base64( + const char *input, size_t length) const noexcept override; + simdutf_warn_unused size_t binary_length_from_base64( + const char16_t *input, size_t length) const noexcept override; }; } // namespace arm64 @@ -2720,7 +1828,6 @@ class implementation final : public simdutf::implementation { /* begin file src/simdutf/arm64/begin.h */ // redefining SIMDUTF_IMPLEMENTATION to "arm64" // #define SIMDUTF_IMPLEMENTATION arm64 -#define SIMDUTF_SIMD_HAS_BYTEMASK 1 /* end file src/simdutf/arm64/begin.h */ // Declarations @@ -3082,9 +2189,10 @@ template <> struct simd8 { // ... template simdutf_really_inline void store_ascii_as_utf16(char16_t *p) const { - int8x16x2_t pair = match_system(big_endian) - ? int8x16x2_t{{this->value, vmovq_n_s8(0)}} - : int8x16x2_t{{vmovq_n_s8(0), this->value}}; + constexpr auto matches = match_system(big_endian); + const int8x16x2_t pair = matches + ? int8x16x2_t{{this->value, vmovq_n_s8(0)}} + : int8x16x2_t{{vmovq_n_s8(0), this->value}}; vst2q_s8(reinterpret_cast(p), pair); } @@ -3283,6 +2391,12 @@ template struct simd8x64 { this->chunks[2] > mask, this->chunks[3] > mask) .to_bitmask(); } + simdutf_really_inline uint64_t gteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64(this->chunks[0] >= mask, this->chunks[1] >= mask, + this->chunks[2] >= mask, this->chunks[3] >= mask) + .to_bitmask(); + } simdutf_really_inline uint64_t gteq_unsigned(const uint8_t m) const { const simd8 mask = simd8::splat(m); return simd8x64(simd8(uint8x16_t(this->chunks[0])) >= mask, @@ -3652,7 +2766,12 @@ template struct simd16x32 { this->chunks[2] > mask, this->chunks[3] > mask) .to_bitmask(); } - + simdutf_really_inline uint64_t gteq(const T m) const { + const simd16 mask = simd16::splat(m); + return simd16x32(this->chunks[0] >= mask, this->chunks[1] >= mask, + this->chunks[2] >= mask, this->chunks[3] >= mask) + .to_bitmask(); + } simdutf_really_inline uint64_t lteq(const T m) const { const simd16 mask = simd16::splat(m); return simd16x32(this->chunks[0] <= mask, this->chunks[1] <= mask, @@ -3871,7 +2990,6 @@ simdutf_really_inline simd64 sum_8bytes(const simd8 v) { /* end file src/simdutf/arm64/simd.h */ /* begin file src/simdutf/arm64/end.h */ -#undef SIMDUTF_SIMD_HAS_BYTEMASK /* end file src/simdutf/arm64/end.h */ #endif // SIMDUTF_IMPLEMENTATION_ARM64 @@ -4113,27 +3231,35 @@ class implementation final : public simdutf::implementation { simdutf_warn_unused result base64_to_binary( const char *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; simdutf_warn_unused full_result base64_to_binary_details( const char *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; - simdutf_warn_unused result - base64_to_binary(const char16_t *input, size_t length, char *output, - base64_options options, - last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; + simdutf_warn_unused result base64_to_binary( + const char16_t *input, size_t length, char *output, + base64_options options, + last_chunk_handling_options last_chunk_options = + last_chunk_handling_options::loose) const noexcept override; simdutf_warn_unused full_result base64_to_binary_details( const char16_t *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; size_t binary_to_base64(const char *input, size_t length, char *output, - base64_options options) const noexcept; + base64_options options) const noexcept override; + size_t + binary_to_base64_with_lines(const char *input, size_t length, char *output, + size_t line_length, + base64_options options) const noexcept override; const char *find(const char *start, const char *end, - char character) const noexcept; + char character) const noexcept override; const char16_t *find(const char16_t *start, const char16_t *end, - char16_t character) const noexcept; + char16_t character) const noexcept override; + simdutf_warn_unused size_t binary_length_from_base64( + const char *input, size_t length) const noexcept override; + simdutf_warn_unused size_t binary_length_from_base64( + const char16_t *input, size_t length) const noexcept override; }; } // namespace icelake @@ -4182,6 +3308,17 @@ simdutf_really_inline long long int count_ones(uint64_t input_num) { } #endif +#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO +simdutf_really_inline unsigned __int64 count_ones32(uint32_t input_num) { + // note: we do not support legacy 32-bit Windows + return __popcnt(input_num); // Visual Studio wants two underscores +} +#else +simdutf_really_inline long long int count_ones32(uint32_t input_num) { + return _popcnt32(input_num); +} +#endif + #if SIMDUTF_NEED_TRAILING_ZEROES // simdutf_really_inline int trailing_zeroes(uint64_t input_num) { // #if SIMDUTF_REGULAR_VISUAL_STUDIO @@ -4445,27 +3582,35 @@ class implementation final : public simdutf::implementation { simdutf_warn_unused result base64_to_binary( const char *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; simdutf_warn_unused full_result base64_to_binary_details( const char *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; - simdutf_warn_unused result - base64_to_binary(const char16_t *input, size_t length, char *output, - base64_options options, - last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; + simdutf_warn_unused result base64_to_binary( + const char16_t *input, size_t length, char *output, + base64_options options, + last_chunk_handling_options last_chunk_options = + last_chunk_handling_options::loose) const noexcept override; simdutf_warn_unused full_result base64_to_binary_details( const char16_t *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; size_t binary_to_base64(const char *input, size_t length, char *output, - base64_options options) const noexcept; + base64_options options) const noexcept override; + size_t + binary_to_base64_with_lines(const char *input, size_t length, char *output, + size_t line_length, + base64_options options) const noexcept override; const char *find(const char *start, const char *end, - char character) const noexcept; + char character) const noexcept override; const char16_t *find(const char16_t *start, const char16_t *end, - char16_t character) const noexcept; + char16_t character) const noexcept override; + simdutf_warn_unused size_t binary_length_from_base64( + const char *input, size_t length) const noexcept override; + simdutf_warn_unused size_t binary_length_from_base64( + const char16_t *input, size_t length) const noexcept override; }; } // namespace haswell @@ -5460,27 +4605,35 @@ class implementation final : public simdutf::implementation { simdutf_warn_unused result base64_to_binary( const char *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; simdutf_warn_unused full_result base64_to_binary_details( const char *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; - simdutf_warn_unused result - base64_to_binary(const char16_t *input, size_t length, char *output, - base64_options options, - last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; + simdutf_warn_unused result base64_to_binary( + const char16_t *input, size_t length, char *output, + base64_options options, + last_chunk_handling_options last_chunk_options = + last_chunk_handling_options::loose) const noexcept override; simdutf_warn_unused full_result base64_to_binary_details( const char16_t *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; size_t binary_to_base64(const char *input, size_t length, char *output, - base64_options options) const noexcept; + base64_options options) const noexcept override; + size_t + binary_to_base64_with_lines(const char *input, size_t length, char *output, + size_t line_length, + base64_options options) const noexcept override; const char *find(const char *start, const char *end, - char character) const noexcept; + char character) const noexcept override; const char16_t *find(const char16_t *start, const char16_t *end, - char16_t character) const noexcept; + char16_t character) const noexcept override; + simdutf_warn_unused size_t binary_length_from_base64( + const char *input, size_t length) const noexcept override; + simdutf_warn_unused size_t binary_length_from_base64( + const char16_t *input, size_t length) const noexcept override; }; } // namespace westmere @@ -5923,7 +5076,12 @@ template struct simd8x64 { this->chunks[2] > mask, this->chunks[3] > mask) .to_bitmask(); } - + simdutf_really_inline uint64_t gteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64(this->chunks[0] >= mask, this->chunks[1] >= mask, + this->chunks[2] >= mask, this->chunks[3] >= mask) + .to_bitmask(); + } simdutf_really_inline uint64_t eq(const T m) const { const simd8 mask = simd8::splat(m); return simd8x64(this->chunks[0] == mask, this->chunks[1] == mask, @@ -6155,6 +5313,13 @@ template struct simd16x32 { .to_bitmask(); } + simdutf_really_inline uint64_t gteq(const T m) const { + const simd16 mask = simd16::splat(m); + return simd16x32(this->chunks[0] >= mask, this->chunks[1] >= mask, + this->chunks[2] >= mask, this->chunks[3] >= mask) + .to_bitmask(); + } + simdutf_really_inline uint64_t eq(const T m) const { const simd16 mask = simd16::splat(m); return simd16x32(this->chunks[0] == mask, this->chunks[1] == mask, @@ -6448,29 +5613,33 @@ class implementation final : public simdutf::implementation { simdutf_warn_unused result base64_to_binary( const char *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; simdutf_warn_unused full_result base64_to_binary_details( const char *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; - simdutf_warn_unused result - base64_to_binary(const char16_t *input, size_t length, char *output, - base64_options options, - last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; + simdutf_warn_unused result base64_to_binary( + const char16_t *input, size_t length, char *output, + base64_options options, + last_chunk_handling_options last_chunk_options = + last_chunk_handling_options::loose) const noexcept override; simdutf_warn_unused full_result base64_to_binary_details( const char16_t *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; size_t binary_to_base64(const char *input, size_t length, char *output, - base64_options options) const noexcept; + base64_options options) const noexcept override; + size_t + binary_to_base64_with_lines(const char *input, size_t length, char *output, + size_t line_length, + base64_options options) const noexcept override; const char *find(const char *start, const char *end, - char character) const noexcept; + char character) const noexcept override; const char16_t *find(const char16_t *start, const char16_t *end, - char16_t character) const noexcept; + char16_t character) const noexcept override; #ifdef SIMDUTF_INTERNAL_TESTS virtual std::vector internal_tests() const override; @@ -7976,28 +7145,32 @@ class implementation final : public simdutf::implementation { simdutf_warn_unused result base64_to_binary( const char *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; simdutf_warn_unused full_result base64_to_binary_details( const char *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; - simdutf_warn_unused result - base64_to_binary(const char16_t *input, size_t length, char *output, - base64_options options, - last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; + simdutf_warn_unused result base64_to_binary( + const char16_t *input, size_t length, char *output, + base64_options options, + last_chunk_handling_options last_chunk_options = + last_chunk_handling_options::loose) const noexcept override; simdutf_warn_unused full_result base64_to_binary_details( const char16_t *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; size_t binary_to_base64(const char *input, size_t length, char *output, - base64_options options) const noexcept; + base64_options options) const noexcept override; + size_t + binary_to_base64_with_lines(const char *input, size_t length, char *output, + size_t line_length, + base64_options options) const noexcept override; const char *find(const char *start, const char *end, - char character) const noexcept; + char character) const noexcept override; const char16_t *find(const char16_t *start, const char16_t *end, - char16_t character) const noexcept; + char16_t character) const noexcept override; private: const bool _supports_zvbb; @@ -8170,42 +7343,47 @@ SIMDUTF_UNTARGET_REGION #endif // SIMDUTF_RVV_H /* end file src/simdutf/rvv.h */ -/* begin file src/simdutf/lsx.h */ -#ifndef SIMDUTF_LSX_H -#define SIMDUTF_LSX_H +/* begin file src/simdutf/lasx.h */ +#ifndef SIMDUTF_LASX_H +#define SIMDUTF_LASX_H #ifdef SIMDUTF_FALLBACK_H - #error "lsx.h must be included before fallback.h" + #error "lasx.h must be included before fallback.h" #endif -#ifndef SIMDUTF_IMPLEMENTATION_LSX - #define SIMDUTF_IMPLEMENTATION_LSX (SIMDUTF_IS_LSX) +#ifndef SIMDUTF_IMPLEMENTATION_LASX + #define SIMDUTF_IMPLEMENTATION_LASX (SIMDUTF_IS_LSX) #endif -#if SIMDUTF_IMPLEMENTATION_LSX && SIMDUTF_IS_LSX - #define SIMDUTF_CAN_ALWAYS_RUN_LSX 1 +#if SIMDUTF_IMPLEMENTATION_LASX && SIMDUTF_IS_LASX + #define SIMDUTF_CAN_ALWAYS_RUN_LASX 1 #else - #define SIMDUTF_CAN_ALWAYS_RUN_LSX 0 + #define SIMDUTF_CAN_ALWAYS_RUN_LASX 0 #endif #define SIMDUTF_CAN_ALWAYS_RUN_FALLBACK (SIMDUTF_IMPLEMENTATION_FALLBACK) -#if SIMDUTF_IMPLEMENTATION_LSX +#if SIMDUTF_IMPLEMENTATION_LASX + #define SIMDUTF_TARGET_LASX SIMDUTF_TARGET_REGION("lasx,lsx") + + // For runtime dispatching to work, we need the lsxintrin to appear + // before we call SIMDUTF_TARGET_LASX. It is unclear why. + #include namespace simdutf { /** - * Implementation for LoongArch SX. + * Implementation for LoongArch ASX. */ -namespace lsx {} // namespace lsx +namespace lasx {} // namespace lasx } // namespace simdutf -/* begin file src/simdutf/lsx/implementation.h */ -#ifndef SIMDUTF_LSX_IMPLEMENTATION_H -#define SIMDUTF_LSX_IMPLEMENTATION_H +/* begin file src/simdutf/lasx/implementation.h */ +#ifndef SIMDUTF_LASX_IMPLEMENTATION_H +#define SIMDUTF_LASX_IMPLEMENTATION_H namespace simdutf { -namespace lsx { +namespace lasx { namespace { using namespace simdutf; @@ -8214,56 +7392,162 @@ using namespace simdutf; class implementation final : public simdutf::implementation { public: simdutf_really_inline implementation() - : simdutf::implementation("lsx", "LOONGARCH SX", - internal::instruction_set::LSX) {} + : simdutf::implementation("lasx", "LOONGARCH ASX", + internal::instruction_set::LSX | + internal::instruction_set::LASX) {} simdutf_warn_unused result base64_to_binary( const char *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; simdutf_warn_unused full_result base64_to_binary_details( const char *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; - simdutf_warn_unused result - base64_to_binary(const char16_t *input, size_t length, char *output, - base64_options options, - last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; + simdutf_warn_unused result base64_to_binary( + const char16_t *input, size_t length, char *output, + base64_options options, + last_chunk_handling_options last_chunk_options = + last_chunk_handling_options::loose) const noexcept override; simdutf_warn_unused full_result base64_to_binary_details( const char16_t *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; size_t binary_to_base64(const char *input, size_t length, char *output, - base64_options options) const noexcept; + base64_options options) const noexcept override; + size_t + binary_to_base64_with_lines(const char *input, size_t length, char *output, + size_t line_length, + base64_options options) const noexcept override; const char *find(const char *start, const char *end, - char character) const noexcept; + char character) const noexcept override; const char16_t *find(const char16_t *start, const char16_t *end, - char16_t character) const noexcept; + char16_t character) const noexcept override; + simdutf_warn_unused size_t binary_length_from_base64( + const char *input, size_t length) const noexcept override; + simdutf_warn_unused size_t binary_length_from_base64( + const char16_t *input, size_t length) const noexcept override; }; -} // namespace lsx +} // namespace lasx } // namespace simdutf -#endif // SIMDUTF_LSX_IMPLEMENTATION_H -/* end file src/simdutf/lsx/implementation.h */ +#endif // SIMDUTF_LASX_IMPLEMENTATION_H +/* end file src/simdutf/lasx/implementation.h */ -/* begin file src/simdutf/lsx/begin.h */ -// redefining SIMDUTF_IMPLEMENTATION to "lsx" -// #define SIMDUTF_IMPLEMENTATION lsx +/* begin file src/simdutf/lasx/begin.h */ +// redefining SIMDUTF_IMPLEMENTATION to "lasx" +// #define SIMDUTF_IMPLEMENTATION lasx #define SIMDUTF_SIMD_HAS_UNSIGNED_CMP 1 -/* end file src/simdutf/lsx/begin.h */ + +#if SIMDUTF_CAN_ALWAYS_RUN_LASX +// nothing needed. +#else +SIMDUTF_TARGET_LASX +#endif +/* end file src/simdutf/lasx/begin.h */ // Declarations -/* begin file src/simdutf/lsx/intrinsics.h */ -#ifndef SIMDUTF_LSX_INTRINSICS_H -#define SIMDUTF_LSX_INTRINSICS_H +/* begin file src/simdutf/lasx/intrinsics.h */ +#ifndef SIMDUTF_LASX_INTRINSICS_H +#define SIMDUTF_LASX_INTRINSICS_H // This should be the correct header whether // you use visual studio or other compilers. #include +#include + +#if defined(__loongarch_asx) + #ifdef __clang__ + #define VREGS_PREFIX "$vr" + #define XREGS_PREFIX "$xr" + #else // GCC + #define VREGS_PREFIX "$f" + #define XREGS_PREFIX "$f" + #endif + #define __ALL_REGS \ + "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26," \ + "27,28,29,30,31" +// Convert __m128i to __m256i +static inline __m256i ____m256i(__m128i in) { + __m256i out = __lasx_xvldi(0); + __asm__ volatile(".irp i," __ALL_REGS "\n\t" + " .ifc %[out], " XREGS_PREFIX "\\i \n\t" + " .irp j," __ALL_REGS "\n\t" + " .ifc %[in], " VREGS_PREFIX "\\j \n\t" + " xvpermi.q $xr\\i, $xr\\j, 0x0 \n\t" + " .endif \n\t" + " .endr \n\t" + " .endif \n\t" + ".endr \n\t" + : [out] "+f"(out) + : [in] "f"(in)); + return out; +} +// Convert two __m128i to __m256i +static inline __m256i lasx_set_q(__m128i inhi, __m128i inlo) { + __m256i out; + __asm__ volatile(".irp i," __ALL_REGS "\n\t" + " .ifc %[hi], " VREGS_PREFIX "\\i \n\t" + " .irp j," __ALL_REGS "\n\t" + " .ifc %[lo], " VREGS_PREFIX "\\j \n\t" + " xvpermi.q $xr\\i, $xr\\j, 0x20 \n\t" + " .endif \n\t" + " .endr \n\t" + " .endif \n\t" + ".endr \n\t" + ".ifnc %[out], %[hi] \n\t" + ".irp i," __ALL_REGS "\n\t" + " .ifc %[out], " XREGS_PREFIX "\\i \n\t" + " .irp j," __ALL_REGS "\n\t" + " .ifc %[hi], " VREGS_PREFIX "\\j \n\t" + " xvori.b $xr\\i, $xr\\j, 0 \n\t" + " .endif \n\t" + " .endr \n\t" + " .endif \n\t" + ".endr \n\t" + ".endif \n\t" + : [out] "=f"(out), [hi] "+f"(inhi) + : [lo] "f"(inlo)); + return out; +} +// Convert __m256i low part to __m128i +static inline __m128i lasx_extracti128_lo(__m256i in) { + __m128i out; + __asm__ volatile(".ifnc %[out], %[in] \n\t" + ".irp i," __ALL_REGS "\n\t" + " .ifc %[out], " VREGS_PREFIX "\\i \n\t" + " .irp j," __ALL_REGS "\n\t" + " .ifc %[in], " XREGS_PREFIX "\\j \n\t" + " vori.b $vr\\i, $vr\\j, 0 \n\t" + " .endif \n\t" + " .endr \n\t" + " .endif \n\t" + ".endr \n\t" + ".endif \n\t" + : [out] "=f"(out) + : [in] "f"(in)); + return out; +} +// Convert __m256i high part to __m128i +static inline __m128i lasx_extracti128_hi(__m256i in) { + __m128i out; + __asm__ volatile(".irp i," __ALL_REGS "\n\t" + " .ifc %[out], " VREGS_PREFIX "\\i \n\t" + " .irp j," __ALL_REGS "\n\t" + " .ifc %[in], " XREGS_PREFIX "\\j \n\t" + " xvpermi.q $xr\\i, $xr\\j, 0x11 \n\t" + " .endif \n\t" + " .endr \n\t" + " .endif \n\t" + ".endr \n\t" + : [out] "=f"(out) + : [in] "f"(in)); + return out; +} +#endif /* Encoding of argument for LoongArch64 xvldi instruction. See: @@ -8293,7 +7577,7 @@ all lanes the result as 64-bit elements to all lanes */ -namespace vldi { +namespace lasx_vldi { template class const_u16 { constexpr static const uint8_t b0 = ((v >> 0 * 8) & 0xff); @@ -8410,7 +7694,8 @@ template class const_u64 { constexpr static int value = int((operation << 8) | byte) - 8192; constexpr static bool valid = operation != 0xffff; }; -} // namespace vldi + +} // namespace lasx_vldi // Uncomment when running under QEMU affected // by bug https://gitlab.com/qemu-project/qemu/-/issues/2865 @@ -8420,45 +7705,78 @@ template class const_u64 { #endif #ifdef QEMU_VLDI_BUG - #define lsx_splat_u16(v) __lsx_vreplgr2vr_h(v) - #define lsx_splat_u32(v) __lsx_vreplgr2vr_w(v) + #define lasx_splat_u16(v) __lasx_xvreplgr2vr_h(v) + #define lasx_splat_u32(v) __lasx_xvreplgr2vr_w(v) #else -template constexpr __m128i lsx_splat_u16_aux() { +template constexpr __m256i lasx_splat_u16_aux() { constexpr bool is_imm10 = (int16_t(x) < 512) && (int16_t(x) > -512); constexpr uint16_t imm10 = is_imm10 ? x : 0; - constexpr bool is_vldi = vldi::const_u16::valid; - constexpr int vldi_imm = is_vldi ? vldi::const_u16::value : 0; + constexpr bool is_vldi = lasx_vldi::const_u16::valid; + constexpr int vldi_imm = is_vldi ? lasx_vldi::const_u16::value : 0; - return is_imm10 ? __lsx_vrepli_h(int16_t(imm10)) - : is_vldi ? __lsx_vldi(vldi_imm) - : __lsx_vreplgr2vr_h(x); + return is_imm10 ? __lasx_xvrepli_h(int16_t(imm10)) + : is_vldi ? __lasx_xvldi(vldi_imm) + : __lasx_xvreplgr2vr_h(x); } -template constexpr __m128i lsx_splat_u32_aux() { +template constexpr __m256i lasx_splat_u32_aux() { constexpr bool is_imm10 = (int32_t(x) < 512) && (int32_t(x) > -512); constexpr uint32_t imm10 = is_imm10 ? x : 0; - constexpr bool is_vldi = vldi::const_u32::valid; - constexpr int vldi_imm = is_vldi ? vldi::const_u32::value : 0; + constexpr bool is_vldi = lasx_vldi::const_u32::valid; + constexpr int vldi_imm = is_vldi ? lasx_vldi::const_u32::value : 0; - return is_imm10 ? __lsx_vrepli_w(int32_t(imm10)) - : is_vldi ? __lsx_vldi(vldi_imm) - : __lsx_vreplgr2vr_w(x); + return is_imm10 ? __lasx_xvrepli_w(int32_t(imm10)) + : is_vldi ? __lasx_xvldi(vldi_imm) + : __lasx_xvreplgr2vr_w(x); } - #define lsx_splat_u16(v) lsx_splat_u16_aux<(v)>() - #define lsx_splat_u32(v) lsx_splat_u32_aux<(v)>() + #define lasx_splat_u16(v) lasx_splat_u16_aux<(v)>() + #define lasx_splat_u32(v) lasx_splat_u32_aux<(v)>() #endif // QEMU_VLDI_BUG -#endif // SIMDUTF_LSX_INTRINSICS_H -/* end file src/simdutf/lsx/intrinsics.h */ -/* begin file src/simdutf/lsx/bitmanipulation.h */ -#ifndef SIMDUTF_LSX_BITMANIPULATION_H -#define SIMDUTF_LSX_BITMANIPULATION_H +#ifndef lsx_splat_u16 + #ifdef QEMU_VLDI_BUG + #define lsx_splat_u16(v) __lsx_vreplgr2vr_h(v) + #define lsx_splat_u32(v) __lsx_vreplgr2vr_w(v) + #else +namespace { +template constexpr __m128i lsx_splat_u16_aux() { + return ((int16_t(x) < 512) && (int16_t(x) > -512)) + ? __lsx_vrepli_h( + ((int16_t(x) < 512) && (int16_t(x) > -512)) ? int16_t(x) : 0) + : (lasx_vldi::const_u16::valid + ? __lsx_vldi(lasx_vldi::const_u16::valid + ? lasx_vldi::const_u16::value + : 0) + : __lsx_vreplgr2vr_h(x)); +} + +template constexpr __m128i lsx_splat_u32_aux() { + return ((int32_t(x) < 512) && (int32_t(x) > -512)) + ? __lsx_vrepli_w( + ((int32_t(x) < 512) && (int32_t(x) > -512)) ? int32_t(x) : 0) + : (lasx_vldi::const_u32::valid + ? __lsx_vldi(lasx_vldi::const_u32::valid + ? lasx_vldi::const_u32::value + : 0) + : __lsx_vreplgr2vr_w(x)); +} +} // namespace + #define lsx_splat_u16(v) lsx_splat_u16_aux<(v)>() + #define lsx_splat_u32(v) lsx_splat_u32_aux<(v)>() + #endif // QEMU_VLDI_BUG +#endif // lsx_splat_u16 + +#endif // SIMDUTF_LASX_INTRINSICS_H +/* end file src/simdutf/lasx/intrinsics.h */ +/* begin file src/simdutf/lasx/bitmanipulation.h */ +#ifndef SIMDUTF_LASX_BITMANIPULATION_H +#define SIMDUTF_LASX_BITMANIPULATION_H #include namespace simdutf { -namespace lsx { +namespace lasx { namespace { simdutf_really_inline int count_ones(uint64_t input_num) { @@ -8472,171 +7790,349 @@ simdutf_really_inline int trailing_zeroes(uint64_t input_num) { #endif } // unnamed namespace -} // namespace lsx +} // namespace lasx } // namespace simdutf -#endif // SIMDUTF_LSX_BITMANIPULATION_H -/* end file src/simdutf/lsx/bitmanipulation.h */ -/* begin file src/simdutf/lsx/simd.h */ -#ifndef SIMDUTF_LSX_SIMD_H -#define SIMDUTF_LSX_SIMD_H +#endif // SIMDUTF_LASX_BITMANIPULATION_H +/* end file src/simdutf/lasx/bitmanipulation.h */ +/* begin file src/simdutf/lasx/simd.h */ +#ifndef SIMDUTF_LASX_SIMD_H +#define SIMDUTF_LASX_SIMD_H namespace simdutf { -namespace lsx { +namespace lasx { namespace { namespace simd { -template struct simd8; +__attribute__((aligned(32))) static const uint8_t prev_shuf_table[32][32] = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 31, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 30, 31, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, + {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, + {0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 28, 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, + {0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 27, 28, 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + {0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 26, 27, 28, 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, + 25, 26, 27, 28, 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, 7, 8}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, + 24, 25, 26, 27, 28, 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, 7}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1, 2, 3, 4, 5, 6}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1, 2, 3, 4, 5}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1, 2, 3, 4}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1, 2, 3}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1, 2}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0}, + {15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0}, + {7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0}, + {6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0}, + {5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0}, + {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0}, + {3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0}, + {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0}, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, +}; -// -// Base class of simd8 and simd8, both of which use __m128i -// internally. -// -template > struct base_u8 { - __m128i value; - static const int SIZE = sizeof(value); +__attribute__((aligned(32))) static const uint8_t bitsel_mask_table[32][32] = { + {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0}}; - // Conversion from/to SIMD register - simdutf_really_inline base_u8(const __m128i _value) : value(_value) {} - simdutf_really_inline operator const __m128i &() const { return this->value; } - simdutf_really_inline operator __m128i &() { return this->value; } +// Forward-declared so they can be used by splat and friends. +template struct base { + __m256i value; + + // Zero constructor + simdutf_really_inline base() : value{__m256i()} {} + + // Conversion from SIMD register + simdutf_really_inline base(const __m256i _value) : value(_value) {} + // Conversion to SIMD register + simdutf_really_inline operator const __m256i &() const { return this->value; } + simdutf_really_inline operator __m256i &() { return this->value; } + template + simdutf_really_inline void store_ascii_as_utf16(char16_t *ptr) const { + if (big_endian) { + __m256i zero = __lasx_xvldi(0); + __m256i in8 = __lasx_xvpermi_d(this->value, 0b11011000); + __m256i inlow = __lasx_xvilvl_b(in8, zero); + __m256i inhigh = __lasx_xvilvh_b(in8, zero); + __lasx_xvst(inlow, reinterpret_cast(ptr), 0); + __lasx_xvst(inhigh, reinterpret_cast(ptr), 32); + } else { + __m256i inlow = __lasx_vext2xv_hu_bu(this->value); + __m256i inhigh = __lasx_vext2xv_hu_bu( + __lasx_xvpermi_q(this->value, this->value, 0b00000001)); + __lasx_xvst(inlow, reinterpret_cast<__m256i *>(ptr), 0); + __lasx_xvst(inhigh, reinterpret_cast<__m256i *>(ptr), 32); + } + } + simdutf_really_inline void store_ascii_as_utf32(char32_t *ptr) const { + __m256i in32_0 = __lasx_vext2xv_wu_bu(this->value); + __lasx_xvst(in32_0, reinterpret_cast(ptr), 0); + __m256i in8_1 = __lasx_xvpermi_d(this->value, 0b00000001); + __m256i in32_1 = __lasx_vext2xv_wu_bu(in8_1); + __lasx_xvst(in32_1, reinterpret_cast(ptr), 32); + + __m256i in8_2 = __lasx_xvpermi_d(this->value, 0b00000010); + __m256i in32_2 = __lasx_vext2xv_wu_bu(in8_2); + __lasx_xvst(in32_2, reinterpret_cast(ptr), 64); + + __m256i in8_3 = __lasx_xvpermi_d(this->value, 0b00000011); + __m256i in32_3 = __lasx_vext2xv_wu_bu(in8_3); + __lasx_xvst(in32_3, reinterpret_cast(ptr), 96); + } // Bit operations - simdutf_really_inline simd8 operator|(const simd8 other) const { - return __lsx_vor_v(this->value, other); + simdutf_really_inline Child operator|(const Child other) const { + return __lasx_xvor_v(this->value, other); } - simdutf_really_inline simd8 operator&(const simd8 other) const { - return __lsx_vand_v(this->value, other); + simdutf_really_inline Child operator&(const Child other) const { + return __lasx_xvand_v(this->value, other); } - simdutf_really_inline simd8 operator^(const simd8 other) const { - return __lsx_vxor_v(this->value, other); + simdutf_really_inline Child operator^(const Child other) const { + return __lasx_xvxor_v(this->value, other); } - simdutf_really_inline simd8 operator~() const { return *this ^ 0xFFu; } - simdutf_really_inline simd8 &operator|=(const simd8 other) { - auto this_cast = static_cast *>(this); + simdutf_really_inline Child &operator|=(const Child other) { + auto this_cast = static_cast(this); *this_cast = *this_cast | other; return *this_cast; } +}; + +template struct simd8; +template > +struct base8 : base> { + simdutf_really_inline base8() : base>() {} + simdutf_really_inline base8(const __m256i _value) : base>(_value) {} friend simdutf_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { - return __lsx_vseq_b(lhs, rhs); + return __lasx_xvseq_b(lhs, rhs); } - template + static const int SIZE = sizeof(base::value); + + template simdutf_really_inline simd8 prev(const simd8 prev_chunk) const { - return __lsx_vor_v(__lsx_vbsll_v(this->value, N), - __lsx_vbsrl_v(prev_chunk.value, 16 - N)); - } -}; + static_assert(N <= 16, "unsupported shift value"); -// SIMD byte mask type (returned by things like eq and gt) -template <> struct simd8 : base_u8 { - typedef uint16_t bitmask_t; - typedef uint32_t bitmask2_t; + if (!N) + return this->value; - static simdutf_really_inline simd8 splat(bool _value) { - return __lsx_vreplgr2vr_b(uint8_t(-(!!_value))); - } + __m256i zero = __lasx_xvldi(0); + __m256i result, shuf; + if (N < 16) { + shuf = __lasx_xvld(prev_shuf_table[N], 0); - simdutf_really_inline simd8(const __m128i _value) : base_u8(_value) {} - // False constructor - simdutf_really_inline simd8() : simd8(__lsx_vldi(0)) {} - // Splat constructor - simdutf_really_inline simd8(bool _value) : simd8(splat(_value)) {} - simdutf_really_inline void store(uint8_t dst[16]) const { - return __lsx_vst(this->value, dst, 0); - } + result = __lasx_xvshuf_b( + __lasx_xvpermi_q(this->value, this->value, 0b00000001), this->value, + shuf); + __m256i srl_prev = __lasx_xvbsrl_v( + __lasx_xvpermi_q(zero, prev_chunk.value, 0b00110001), (16 - N)); + __m256i mask = __lasx_xvld(bitsel_mask_table[N], 0); + result = __lasx_xvbitsel_v(result, srl_prev, mask); - simdutf_really_inline uint32_t to_bitmask() const { - return __lsx_vpickve2gr_wu(__lsx_vmsknz_b(*this), 0); + return result; + } else if (N == 16) { + return __lasx_xvpermi_q(this->value, prev_chunk.value, 0b00100001); + } } }; -// Unsigned bytes -template <> struct simd8 : base_u8 { - static simdutf_really_inline simd8 splat(uint8_t _value) { - return __lsx_vreplgr2vr_b(_value); - } - static simdutf_really_inline simd8 zero() { return __lsx_vldi(0); } - static simdutf_really_inline simd8 load(const uint8_t *values) { - return __lsx_vld(values, 0); - } - simdutf_really_inline simd8(const __m128i _value) - : base_u8(_value) {} - // Zero constructor - simdutf_really_inline simd8() : simd8(zero()) {} - // Array constructor - simdutf_really_inline simd8(const uint8_t values[16]) : simd8(load(values)) {} - // Splat constructor - simdutf_really_inline simd8(uint8_t _value) : simd8(splat(_value)) {} - // Member-by-member initialization - simdutf_really_inline - simd8(uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, - uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9, uint8_t v10, - uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15) - : simd8((__m128i)v16u8{v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, - v12, v13, v14, v15}) {} - - // Repeat 16 values as many times as necessary (usually for lookup tables) - simdutf_really_inline static simd8 - repeat_16(uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, - uint8_t v5, uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9, - uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, - uint8_t v15) { - return simd8(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, - v13, v14, v15); +// SIMD byte mask type (returned by things like eq and gt) +template <> struct simd8 : base8 { + static simdutf_really_inline simd8 splat(bool _value) { + return __lasx_xvreplgr2vr_b(uint8_t(-(!!_value))); } - // Store to array - simdutf_really_inline void store(uint8_t dst[16]) const { - return __lsx_vst(this->value, dst, 0); - } + simdutf_really_inline simd8() : base8() {} + simdutf_really_inline simd8(const __m256i _value) : base8(_value) {} + // Splat constructor + simdutf_really_inline simd8(bool _value) : base8(splat(_value)) {} - // Order-specific operations - simdutf_really_inline simd8 - operator>=(const simd8 other) const { - return __lsx_vsle_bu(other, *this); - } - simdutf_really_inline simd8 - operator>(const simd8 other) const { - return __lsx_vslt_bu(other, *this); - } - simdutf_really_inline simd8 &operator-=(const simd8 other) { - value = __lsx_vsub_b(value, other.value); - return *this; + simdutf_really_inline uint32_t to_bitmask() const { + __m256i mask = __lasx_xvmsknz_b(this->value); + uint32_t mask0 = __lasx_xvpickve2gr_wu(mask, 0); + uint32_t mask1 = __lasx_xvpickve2gr_wu(mask, 4); + return (mask0 | (mask1 << 16)); } - // Same as >, but instead of guaranteeing all 1's == true, false = 0 and true - // = nonzero. For ARM, returns all 1's. - simdutf_really_inline simd8 - gt_bits(const simd8 other) const { - return simd8(*this > other); + simdutf_really_inline bool any() const { + if (__lasx_xbz_b(this->value)) + return false; + return true; } + simdutf_really_inline simd8 operator~() const { return *this ^ true; } +}; - // Bit-specific operations - simdutf_really_inline simd8 any_bits_set(simd8 bits) const { - return __lsx_vslt_bu(__lsx_vldi(0), __lsx_vand_v(this->value, bits)); - } - simdutf_really_inline bool is_ascii() const { - return __lsx_vpickve2gr_hu(__lsx_vmskgez_b(this->value), 0) == 0xFFFF; +template struct base8_numeric : base8 { + static simdutf_really_inline simd8 splat(T _value) { + return __lasx_xvreplgr2vr_b(_value); } - - simdutf_really_inline bool any_bits_set_anywhere() const { - return __lsx_vpickve2gr_hu(__lsx_vmsknz_b(this->value), 0) > 0; + static simdutf_really_inline simd8 zero() { return __lasx_xvldi(0); } + static simdutf_really_inline simd8 load(const T values[32]) { + return __lasx_xvld(reinterpret_cast(values), 0); } - template simdutf_really_inline simd8 shr() const { - return __lsx_vsrli_b(this->value, N); + // Repeat 16 values as many times as necessary (usually for lookup tables) + static simdutf_really_inline simd8 repeat_16(T v0, T v1, T v2, T v3, T v4, + T v5, T v6, T v7, T v8, T v9, + T v10, T v11, T v12, T v13, + T v14, T v15) { + return simd8(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14, v15, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, + v12, v13, v14, v15); } - template simdutf_really_inline simd8 shl() const { - return __lsx_vslli_b(this->value, N); + + simdutf_really_inline base8_numeric() : base8() {} + simdutf_really_inline base8_numeric(const __m256i _value) + : base8(_value) {} + + // Store to array + simdutf_really_inline void store(T dst[32]) const { + return __lasx_xvst(this->value, reinterpret_cast<__m256i *>(dst), 0); } + // Override to distinguish from bool version + simdutf_really_inline simd8 operator~() const { return *this ^ 0xFFu; } + // Perform a lookup assuming the value is between 0 and 16 (undefined behavior // for out of range values) template simdutf_really_inline simd8 lookup_16(simd8 lookup_table) const { - return lookup_table.apply_lookup_16_to(*this); + __m256i origin = __lasx_xvand_v(this->value, __lasx_xvldi(0x1f)); + return __lasx_xvshuf_b(__lasx_xvldi(0), lookup_table, origin); } template @@ -8650,150 +8146,119 @@ template <> struct simd8 : base_u8 { replace7, replace8, replace9, replace10, replace11, replace12, replace13, replace14, replace15)); } - - template - simdutf_really_inline simd8 - apply_lookup_16_to(const simd8 original) const { - __m128i original_tmp = __lsx_vand_v(original, __lsx_vldi(0x1f)); - return __lsx_vshuf_b(__lsx_vldi(0), *this, simd8(original_tmp)); - } - - simdutf_really_inline uint64_t sum_bytes() const { - const auto sum_u16 = __lsx_vhaddw_hu_bu(value, value); - const auto sum_u32 = __lsx_vhaddw_wu_hu(sum_u16, sum_u16); - const auto sum_u64 = __lsx_vhaddw_du_wu(sum_u32, sum_u32); - - return uint64_t(__lsx_vpickve2gr_du(sum_u64, 0)) + - uint64_t(__lsx_vpickve2gr_du(sum_u64, 1)); - } }; // Signed bytes -template <> struct simd8 { - __m128i value; - - static const int SIZE = sizeof(value); - - static simdutf_really_inline simd8 splat(int8_t _value) { - return __lsx_vreplgr2vr_b(_value); - } - static simdutf_really_inline simd8 zero() { return __lsx_vldi(0); } - static simdutf_really_inline simd8 load(const int8_t values[16]) { - return __lsx_vld(values, 0); - } +template <> struct simd8 : base8_numeric { + simdutf_really_inline simd8() : base8_numeric() {} + simdutf_really_inline simd8(const __m256i _value) + : base8_numeric(_value) {} - template - simdutf_really_inline void store_ascii_as_utf16(char16_t *p) const { - __m128i zero = __lsx_vldi(0); - if (match_system(big_endian)) { - __lsx_vst(__lsx_vilvl_b(zero, (__m128i)this->value), - reinterpret_cast(p), 0); - __lsx_vst(__lsx_vilvh_b(zero, (__m128i)this->value), - reinterpret_cast(p + 8), 0); - } else { - __lsx_vst(__lsx_vilvl_b((__m128i)this->value, zero), - reinterpret_cast(p), 0); - __lsx_vst(__lsx_vilvh_b((__m128i)this->value, zero), - reinterpret_cast(p + 8), 0); - } + // Splat constructor + simdutf_really_inline simd8(int8_t _value) : simd8(splat(_value)) {} + // Array constructor + simdutf_really_inline simd8(const int8_t values[32]) : simd8(load(values)) {} + simdutf_really_inline operator simd8() const; + simdutf_really_inline bool is_ascii() const { + __m256i ascii_mask = __lasx_xvslti_b(this->value, 0); + if (__lasx_xbnz_v(ascii_mask)) + return false; + return true; } - - simdutf_really_inline void store_ascii_as_utf32(char32_t *p) const { - __m128i zero = __lsx_vldi(0); - __m128i in16low = __lsx_vilvl_b(zero, (__m128i)this->value); - __m128i in16high = __lsx_vilvh_b(zero, (__m128i)this->value); - __m128i in32_0 = __lsx_vilvl_h(zero, in16low); - __m128i in32_1 = __lsx_vilvh_h(zero, in16low); - __m128i in32_2 = __lsx_vilvl_h(zero, in16high); - __m128i in32_3 = __lsx_vilvh_h(zero, in16high); - __lsx_vst(in32_0, reinterpret_cast(p), 0); - __lsx_vst(in32_1, reinterpret_cast(p + 4), 0); - __lsx_vst(in32_2, reinterpret_cast(p + 8), 0); - __lsx_vst(in32_3, reinterpret_cast(p + 12), 0); + // Order-sensitive comparisons + simdutf_really_inline simd8 operator>(const simd8 other) const { + return __lasx_xvslt_b(other, this->value); } - - // In places where the table can be reused, which is most uses in simdutf, it - // is worth it to do 4 table lookups, as there is no direct zero extension - // from u8 to u32. - simdutf_really_inline void store_ascii_as_utf32_tbl(char32_t *p) const { - const simd8 tb1{0, 255, 255, 255, 1, 255, 255, 255, - 2, 255, 255, 255, 3, 255, 255, 255}; - const simd8 tb2{4, 255, 255, 255, 5, 255, 255, 255, - 6, 255, 255, 255, 7, 255, 255, 255}; - const simd8 tb3{8, 255, 255, 255, 9, 255, 255, 255, - 10, 255, 255, 255, 11, 255, 255, 255}; - const simd8 tb4{12, 255, 255, 255, 13, 255, 255, 255, - 14, 255, 255, 255, 15, 255, 255, 255}; - - // encourage store pairing and interleaving - const auto shuf1 = this->apply_lookup_16_to(tb1); - const auto shuf2 = this->apply_lookup_16_to(tb2); - shuf1.store(reinterpret_cast(p)); - shuf2.store(reinterpret_cast(p + 4)); - - const auto shuf3 = this->apply_lookup_16_to(tb3); - const auto shuf4 = this->apply_lookup_16_to(tb4); - shuf3.store(reinterpret_cast(p + 8)); - shuf4.store(reinterpret_cast(p + 12)); + simdutf_really_inline simd8 operator<(const simd8 other) const { + return __lasx_xvslt_b(this->value, other); } - // Conversion from/to SIMD register - simdutf_really_inline simd8(const __m128i _value) : value(_value) {} +}; - // Zero constructor - simdutf_really_inline simd8() : simd8(zero()) {} +// Unsigned bytes +template <> struct simd8 : base8_numeric { + simdutf_really_inline simd8() : base8_numeric() {} + simdutf_really_inline simd8(const __m256i _value) + : base8_numeric(_value) {} // Splat constructor - simdutf_really_inline simd8(int8_t _value) : simd8(splat(_value)) {} + simdutf_really_inline simd8(uint8_t _value) : simd8(splat(_value)) {} // Array constructor - simdutf_really_inline simd8(const int8_t *values) : simd8(load(values)) {} + simdutf_really_inline simd8(const uint8_t values[32]) : simd8(load(values)) {} + // Member-by-member initialization + simdutf_really_inline + simd8(uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, + uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9, uint8_t v10, + uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15, + uint8_t v16, uint8_t v17, uint8_t v18, uint8_t v19, uint8_t v20, + uint8_t v21, uint8_t v22, uint8_t v23, uint8_t v24, uint8_t v25, + uint8_t v26, uint8_t v27, uint8_t v28, uint8_t v29, uint8_t v30, + uint8_t v31) + : simd8((__m256i)v32u8{v0, v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, + v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31}) {} - // Store to array - simdutf_really_inline void store(int8_t dst[16]) const { - return __lsx_vst(value, dst, 0); + // Saturated math + simdutf_really_inline simd8 + saturating_sub(const simd8 other) const { + return __lasx_xvssub_bu(this->value, other); } - simdutf_really_inline operator simd8() const { - return ((__m128i)this->value); + // Same as >, but only guarantees true is nonzero (< guarantees true = -1) + simdutf_really_inline simd8 + gt_bits(const simd8 other) const { + return this->saturating_sub(other); } - - simdutf_really_inline simd8 - operator|(const simd8 other) const { - return __lsx_vor_v((__m128i)value, (__m128i)other.value); + simdutf_really_inline simd8 + operator>=(const simd8 other) const { + return __lasx_xvsle_bu(other, *this); + } + simdutf_really_inline simd8 + operator>(const simd8 other) const { + return __lasx_xvslt_bu(other, *this); + } + simdutf_really_inline simd8 &operator-=(const simd8 other) { + value = __lasx_xvsub_b(value, other.value); + return *this; } + // Bit-specific operations simdutf_really_inline bool is_ascii() const { - return (__lsx_vpickve2gr_hu(__lsx_vmskgez_b((__m128i)this->value), 0) == - 0xffff); + __m256i ascii_mask = __lasx_xvslti_b(this->value, 0); + if (__lasx_xbnz_v(ascii_mask)) + return false; + return true; } - - // Order-sensitive comparisons - simdutf_really_inline simd8 operator>(const simd8 other) const { - return __lsx_vslt_b((__m128i)other.value, (__m128i)value); + simdutf_really_inline bool any_bits_set_anywhere() const { + if (__lasx_xbnz_v(this->value)) + return true; + return false; } - simdutf_really_inline simd8 operator<(const simd8 other) const { - return __lsx_vslt_b((__m128i)value, (__m128i)other.value); + template simdutf_really_inline simd8 shr() const { + return __lasx_xvsrli_b(this->value, N); } - - template - simdutf_really_inline simd8 - prev(const simd8 prev_chunk) const { - return __lsx_vor_v(__lsx_vbsll_v(this->value, N), - __lsx_vbsrl_v(prev_chunk.value, 16 - N)); + template simdutf_really_inline simd8 shl() const { + return __lasx_xvslli_b(this->value, N); } - template - simdutf_really_inline simd8 - apply_lookup_16_to(const simd8 original) const { - __m128i original_tmp = __lsx_vand_v(original, __lsx_vldi(0x1f)); - return __lsx_vshuf_b(__lsx_vldi(0), (__m128i)this->value, - simd8(original_tmp)); + simdutf_really_inline uint64_t sum_bytes() const { + const auto sum_u16 = __lasx_xvhaddw_hu_bu(value, value); + const auto sum_u32 = __lasx_xvhaddw_wu_hu(sum_u16, sum_u16); + const auto sum_u64 = __lasx_xvhaddw_du_wu(sum_u32, sum_u32); + + return uint64_t(__lasx_xvpickve2gr_du(sum_u64, 0)) + + uint64_t(__lasx_xvpickve2gr_du(sum_u64, 1)) + + uint64_t(__lasx_xvpickve2gr_du(sum_u64, 2)) + + uint64_t(__lasx_xvpickve2gr_du(sum_u64, 3)); } }; +simdutf_really_inline simd8::operator simd8() const { + return this->value; +} template struct simd8x64 { static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert( - NUM_CHUNKS == 4, - "LoongArch kernel should use four registers per 64-byte block."); + static_assert(NUM_CHUNKS == 2, + "LASX kernel should use two registers per 64-byte block."); simd8 chunks[NUM_CHUNKS]; simd8x64(const simd8x64 &o) = delete; // no copy allowed @@ -8801,36 +8266,36 @@ template struct simd8x64 { operator=(const simd8 other) = delete; // no assignment allowed simd8x64() = delete; // no default constructor allowed - simdutf_really_inline simd8x64(const simd8 chunk0, const simd8 chunk1, - const simd8 chunk2, const simd8 chunk3) - : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdutf_really_inline simd8x64(const simd8 chunk0, const simd8 chunk1) + : chunks{chunk0, chunk1} {} simdutf_really_inline simd8x64(const T *ptr) : chunks{simd8::load(ptr), - simd8::load(ptr + sizeof(simd8) / sizeof(T)), - simd8::load(ptr + 2 * sizeof(simd8) / sizeof(T)), - simd8::load(ptr + 3 * sizeof(simd8) / sizeof(T))} {} + simd8::load(ptr + sizeof(simd8) / sizeof(T))} {} simdutf_really_inline void store(T *ptr) const { this->chunks[0].store(ptr + sizeof(simd8) * 0 / sizeof(T)); this->chunks[1].store(ptr + sizeof(simd8) * 1 / sizeof(T)); - this->chunks[2].store(ptr + sizeof(simd8) * 2 / sizeof(T)); - this->chunks[3].store(ptr + sizeof(simd8) * 3 / sizeof(T)); + } + + simdutf_really_inline uint64_t to_bitmask() const { + uint64_t r_lo = uint32_t(this->chunks[0].to_bitmask()); + uint64_t r_hi = this->chunks[1].to_bitmask(); + return r_lo | (r_hi << 32); } simdutf_really_inline simd8x64 &operator|=(const simd8x64 &other) { this->chunks[0] |= other.chunks[0]; this->chunks[1] |= other.chunks[1]; - this->chunks[2] |= other.chunks[2]; - this->chunks[3] |= other.chunks[3]; return *this; } simdutf_really_inline simd8 reduce_or() const { - return (this->chunks[0] | this->chunks[1]) | - (this->chunks[2] | this->chunks[3]); + return this->chunks[0] | this->chunks[1]; } - simdutf_really_inline bool is_ascii() const { return reduce_or().is_ascii(); } + simdutf_really_inline bool is_ascii() const { + return this->reduce_or().is_ascii(); + } template simdutf_really_inline void store_ascii_as_utf16(char16_t *ptr) const { @@ -8838,152 +8303,123 @@ template struct simd8x64 { sizeof(simd8) * 0); this->chunks[1].template store_ascii_as_utf16(ptr + sizeof(simd8) * 1); - this->chunks[2].template store_ascii_as_utf16(ptr + - sizeof(simd8) * 2); - this->chunks[3].template store_ascii_as_utf16(ptr + - sizeof(simd8) * 3); } simdutf_really_inline void store_ascii_as_utf32(char32_t *ptr) const { - this->chunks[0].store_ascii_as_utf32_tbl(ptr + sizeof(simd8) * 0); - this->chunks[1].store_ascii_as_utf32_tbl(ptr + sizeof(simd8) * 1); - this->chunks[2].store_ascii_as_utf32_tbl(ptr + sizeof(simd8) * 2); - this->chunks[3].store_ascii_as_utf32_tbl(ptr + sizeof(simd8) * 3); + this->chunks[0].store_ascii_as_utf32(ptr + sizeof(simd8) * 0); + this->chunks[1].store_ascii_as_utf32(ptr + sizeof(simd8) * 1); } - simdutf_really_inline uint64_t to_bitmask() const { - __m128i mask = __lsx_vbsll_v(__lsx_vmsknz_b(this->chunks[3]), 6); - mask = __lsx_vor_v(mask, __lsx_vbsll_v(__lsx_vmsknz_b(this->chunks[2]), 4)); - mask = __lsx_vor_v(mask, __lsx_vbsll_v(__lsx_vmsknz_b(this->chunks[1]), 2)); - mask = __lsx_vor_v(mask, __lsx_vmsknz_b(this->chunks[0])); - return __lsx_vpickve2gr_du(mask, 0); + simdutf_really_inline uint64_t lt(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64(this->chunks[0] < mask, this->chunks[1] < mask) + .to_bitmask(); } - simdutf_really_inline uint64_t lt(const T m) const { + simdutf_really_inline uint64_t gteq(const T m) const { const simd8 mask = simd8::splat(m); - return simd8x64(this->chunks[0] < mask, this->chunks[1] < mask, - this->chunks[2] < mask, this->chunks[3] < mask) + return simd8x64(this->chunks[0] >= mask, this->chunks[1] >= mask) .to_bitmask(); } + simdutf_really_inline uint64_t gt(const T m) const { const simd8 mask = simd8::splat(m); - return simd8x64(this->chunks[0] > mask, this->chunks[1] > mask, - this->chunks[2] > mask, this->chunks[3] > mask) + return simd8x64(this->chunks[0] > mask, this->chunks[1] > mask) .to_bitmask(); } simdutf_really_inline uint64_t gteq_unsigned(const uint8_t m) const { const simd8 mask = simd8::splat(m); - return simd8x64(simd8(this->chunks[0].value) >= mask, - simd8(this->chunks[1].value) >= mask, - simd8(this->chunks[2].value) >= mask, - simd8(this->chunks[3].value) >= mask) + return simd8x64((simd8(__m256i(this->chunks[0])) >= mask), + (simd8(__m256i(this->chunks[1])) >= mask)) .to_bitmask(); } }; // struct simd8x64 -/* begin file src/simdutf/lsx/simd16-inl.h */ +/* begin file src/simdutf/lasx/simd16-inl.h */ template struct simd16; -template > struct base_u16 { - __m128i value; - static const size_t SIZE = sizeof(value); - static const size_t ELEMENTS = sizeof(value) / sizeof(T); - - // Conversion from/to SIMD register - simdutf_really_inline base_u16() = default; - simdutf_really_inline base_u16(const __m128i _value) : value(_value) {} - // Bit operations - simdutf_really_inline simd16 operator|(const simd16 other) const { - return __lsx_vor_v(this->value, other.value); - } - simdutf_really_inline simd16 operator&(const simd16 other) const { - return __lsx_vand_v(this->value, other.value); - } - simdutf_really_inline simd16 operator~() const { - return __lsx_vxori_b(this->value, 0xFF); - } - - friend simdutf_really_inline Mask operator==(const simd16 lhs, - const simd16 rhs) { - return __lsx_vseq_h(lhs.value, rhs.value); - } - - template - simdutf_really_inline simd16 byte_right_shift() const { - return __lsx_vbsrl_v(this->value, N); - } - - simdutf_really_inline uint16_t first() const { - return uint16_t(__lsx_vpickve2gr_w(value, 0)); - } -}; - template > -struct base16 : base_u16 { - using bitmask_type = uint16_t; +struct base16 : base> { + using bitmask_type = uint32_t; - simdutf_really_inline base16() : base_u16() {} - simdutf_really_inline base16(const __m128i _value) : base_u16(_value) {} + simdutf_really_inline base16() : base>() {} + simdutf_really_inline base16(const __m256i _value) + : base>(_value) {} template simdutf_really_inline base16(const Pointer *ptr) - : base16(__lsx_vld(ptr, 0)) {} + : base16(__lasx_xvld(reinterpret_cast(ptr), 0)) {} - static const int SIZE = sizeof(base_u16::value); + /// the size of vector in bytes + static const int SIZE = sizeof(base>::value); - template - simdutf_really_inline simd16 prev(const simd16 prev_chunk) const { - return __lsx_vor_v(__lsx_vbsll_v(*this, N * 2), - __lsx_vbsrl_v(prev_chunk, 16 - N * 2)); - } + /// the number of elements of type T a vector can hold + static const int ELEMENTS = SIZE / sizeof(T); }; // SIMD byte mask type (returned by things like eq and gt) template <> struct simd16 : base16 { static simdutf_really_inline simd16 splat(bool _value) { - return __lsx_vreplgr2vr_h(uint16_t(-(!!_value))); + return __lasx_xvreplgr2vr_h(uint16_t(-(!!_value))); } simdutf_really_inline simd16() : base16() {} - simdutf_really_inline simd16(const __m128i _value) : base16(_value) {} + simdutf_really_inline simd16(const __m256i _value) : base16(_value) {} + // Splat constructor + simdutf_really_inline simd16(bool _value) : base16(splat(_value)) {} simdutf_really_inline bitmask_type to_bitmask() const { - __m128i mask = __lsx_vmsknz_b(this->value); - bitmask_type mask0 = bitmask_type(__lsx_vpickve2gr_wu(mask, 0)); - return mask0; + __m256i mask = __lasx_xvmsknz_b(this->value); + bitmask_type mask0 = __lasx_xvpickve2gr_wu(mask, 0); + bitmask_type mask1 = __lasx_xvpickve2gr_wu(mask, 4); + return (mask0 | (mask1 << 16)); } + simdutf_really_inline simd16 operator~() const { return *this ^ true; } - simdutf_really_inline bool is_zero() const { return __lsx_bz_v(this->value); } + simdutf_really_inline bool is_zero() const { + return __lasx_xbz_v(this->value); + } + + template simdutf_really_inline simd16 byte_right_shift() const { + const auto t0 = __lasx_xvbsrl_v(this->value, N); + const auto t1 = __lasx_xvpermi_q(this->value, __lasx_xvldi(0), 0b00000011); + const auto t2 = __lasx_xvbsll_v(t1, 16 - N); + const auto t3 = __lasx_xvor_v(t0, t2); + return t3; + } + + simdutf_really_inline uint16_t first() const { + return uint16_t(__lasx_xvpickve2gr_w(value, 0)); + } }; template struct base16_numeric : base16 { static simdutf_really_inline simd16 splat(T _value) { - return __lsx_vreplgr2vr_h(_value); + return __lasx_xvreplgr2vr_h((uint16_t)_value); } - static simdutf_really_inline simd16 zero() { return __lsx_vldi(0); } - + static simdutf_really_inline simd16 zero() { return __lasx_xvldi(0); } template static simdutf_really_inline simd16 load(const Pointer values) { - return __lsx_vld(values, 0); + return __lasx_xvld(values, 0); } - simdutf_really_inline base16_numeric(const __m128i _value) + simdutf_really_inline base16_numeric() : base16() {} + simdutf_really_inline base16_numeric(const __m256i _value) : base16(_value) {} // Store to array simdutf_really_inline void store(T dst[8]) const { - return __lsx_vst(this->value, dst, 0); + return __lasx_xvst(this->value, reinterpret_cast<__m256i *>(dst), 0); } // Override to distinguish from bool version - simdutf_really_inline simd16 operator~() const { - return __lsx_vxori_b(this->value, 0xFF); - } + simdutf_really_inline simd16 operator~() const { return *this ^ 0xFFFFu; } }; -// Unsigned code unitstemplate<> +// Unsigned code units template <> struct simd16 : base16_numeric { - simdutf_really_inline simd16(const __m128i _value) - : base16_numeric((__m128i)_value) {} + simdutf_really_inline simd16() : base16_numeric() {} + simdutf_really_inline simd16(const __m256i _value) + : base16_numeric(_value) {} // Splat constructor simdutf_really_inline simd16(uint16_t _value) : simd16(splat(_value)) {} @@ -8993,67 +8429,71 @@ template <> struct simd16 : base16_numeric { simdutf_really_inline simd16(const char16_t *values) : simd16(load(reinterpret_cast(values))) {} - // Copy constructor - simdutf_really_inline simd16(const simd16 mask) : simd16(mask.value) {} - // Order-specific operations simdutf_really_inline simd16 &operator+=(const simd16 other) { - value = __lsx_vadd_h(value, other.value); + value = __lasx_xvadd_h(value, other.value); return *this; } + // Change the endianness + simdutf_really_inline simd16 swap_bytes() const { + return __lasx_xvshuf4i_b(this->value, 0b10110001); + } + template static simdutf_really_inline simd8 pack_shifted_right(const simd16 &v0, const simd16 &v1) { - return __lsx_vssrlni_bu_h(v1.value, v0.value, N); + return __lasx_xvpermi_d(__lasx_xvssrlni_bu_h(v1.value, v0.value, N), + 0b11011000); } // Pack with the unsigned saturation of two uint16_t code units into single // uint8_t vector static simdutf_really_inline simd8 pack(const simd16 &v0, const simd16 &v1) { - return pack_shifted_right<0>(v0, v1); - } - // Change the endianness - simdutf_really_inline simd16 swap_bytes() const { - return __lsx_vshuf4i_b(this->value, 0b10110001); + return pack_shifted_right<0>(v0, v1); } simdutf_really_inline uint64_t sum() const { - const auto sum_u32 = __lsx_vhaddw_wu_hu(value, value); - const auto sum_u64 = __lsx_vhaddw_du_wu(sum_u32, sum_u32); + const auto sum_u32 = __lasx_xvhaddw_wu_hu(value, value); + const auto sum_u64 = __lasx_xvhaddw_du_wu(sum_u32, sum_u32); - return uint64_t(__lsx_vpickve2gr_du(sum_u64, 0)) + - uint64_t(__lsx_vpickve2gr_du(sum_u64, 1)); + return uint64_t(__lasx_xvpickve2gr_du(sum_u64, 0)) + + uint64_t(__lasx_xvpickve2gr_du(sum_u64, 1)) + + uint64_t(__lasx_xvpickve2gr_du(sum_u64, 2)) + + uint64_t(__lasx_xvpickve2gr_du(sum_u64, 3)); + } + + template simdutf_really_inline simd16 byte_right_shift() const { + return __lasx_xvbsrl_v(this->value, N); } }; simdutf_really_inline simd16 operator<(const simd16 a, const simd16 b) { - return __lsx_vslt_hu(a.value, b.value); + return __lasx_xvslt_hu(a.value, b.value); } simdutf_really_inline simd16 operator>(const simd16 a, const simd16 b) { - return __lsx_vslt_hu(b.value, a.value); + return __lasx_xvslt_hu(b.value, a.value); } simdutf_really_inline simd16 operator<=(const simd16 a, const simd16 b) { - return __lsx_vsle_hu(a.value, b.value); + return __lasx_xvsle_hu(a.value, b.value); } simdutf_really_inline simd16 operator>=(const simd16 a, const simd16 b) { - return __lsx_vsle_hu(b.value, a.value); + return __lasx_xvsle_hu(b.value, a.value); } template struct simd16x32 { static constexpr int NUM_CHUNKS = 64 / sizeof(simd16); - static_assert( - NUM_CHUNKS == 4, - "LOONGARCH kernel should use four registers per 64-byte block."); + static_assert(NUM_CHUNKS == 2, + "LASX kernel should use two registers per 64-byte block."); simd16 chunks[NUM_CHUNKS]; simd16x32(const simd16x32 &o) = delete; // no copy allowed @@ -9061,127 +8501,144 @@ template struct simd16x32 { operator=(const simd16 other) = delete; // no assignment allowed simd16x32() = delete; // no default constructor allowed - simdutf_really_inline - simd16x32(const simd16 chunk0, const simd16 chunk1, - const simd16 chunk2, const simd16 chunk3) - : chunks{chunk0, chunk1, chunk2, chunk3} {} + simdutf_really_inline simd16x32(const simd16 chunk0, + const simd16 chunk1) + : chunks{chunk0, chunk1} {} simdutf_really_inline simd16x32(const T *ptr) : chunks{simd16::load(ptr), - simd16::load(ptr + sizeof(simd16) / sizeof(T)), - simd16::load(ptr + 2 * sizeof(simd16) / sizeof(T)), - simd16::load(ptr + 3 * sizeof(simd16) / sizeof(T))} {} + simd16::load(ptr + sizeof(simd16) / sizeof(T))} {} simdutf_really_inline void store(T *ptr) const { this->chunks[0].store(ptr + sizeof(simd16) * 0 / sizeof(T)); this->chunks[1].store(ptr + sizeof(simd16) * 1 / sizeof(T)); - this->chunks[2].store(ptr + sizeof(simd16) * 2 / sizeof(T)); - this->chunks[3].store(ptr + sizeof(simd16) * 3 / sizeof(T)); } simdutf_really_inline void swap_bytes() { this->chunks[0] = this->chunks[0].swap_bytes(); this->chunks[1] = this->chunks[1].swap_bytes(); - this->chunks[2] = this->chunks[2].swap_bytes(); - this->chunks[3] = this->chunks[3].swap_bytes(); } simdutf_really_inline uint64_t to_bitmask() const { - uint64_t r0 = uint32_t(this->chunks[0].to_bitmask()); - uint64_t r1 = this->chunks[1].to_bitmask(); - uint64_t r2 = this->chunks[2].to_bitmask(); - uint64_t r3 = this->chunks[3].to_bitmask(); - return r0 | (r1 << 16) | (r2 << 32) | (r3 << 48); + uint64_t r_lo = uint32_t(this->chunks[0].to_bitmask()); + uint64_t r_hi = this->chunks[1].to_bitmask(); + return r_lo | (r_hi << 32); + } + simdutf_really_inline uint64_t gteq(const T m) const { + const simd16 mask = simd16::splat(m); + return simd16x32(this->chunks[0] >= mask, this->chunks[1] >= mask) + .to_bitmask(); } simdutf_really_inline uint64_t lteq(const T m) const { const simd16 mask = simd16::splat(m); - return simd16x32(this->chunks[0] <= mask, this->chunks[1] <= mask, - this->chunks[2] <= mask, this->chunks[3] <= mask) + return simd16x32(this->chunks[0] <= mask, this->chunks[1] <= mask) .to_bitmask(); } }; // struct simd16x32 -simdutf_really_inline simd16 operator^(const simd16 a, +simdutf_really_inline simd16 min(const simd16 a, + const simd16 b) { + return __lasx_xvmin_hu(a.value, b.value); +} + +simdutf_really_inline simd16 operator==(const simd16 a, + uint16_t b) { + const auto bv = __lasx_xvreplgr2vr_h(b); + return __lasx_xvseq_h(a.value, bv); +} + +simdutf_really_inline simd16 as_vector_u16(const simd16 x) { + return x.value; +} + +simdutf_really_inline simd16 operator&(const simd16 a, uint16_t b) { - const auto bv = __lsx_vreplgr2vr_h(b); - return __lsx_vxor_v(a.value, bv); + const auto bv = __lasx_xvreplgr2vr_h(b); + return __lasx_xvand_v(a.value, bv); } -simdutf_really_inline simd16 operator^(const simd16 a, - const simd16 b) { - return __lsx_vxor_v(a.value, b.value); +simdutf_really_inline simd16 operator&(const simd16 a, + const simd16 b) { + return __lasx_xvand_v(a.value, b.value); } -simdutf_really_inline simd16 min(const simd16 a, - const simd16 b) { - return __lsx_vmin_hu(a.value, b.value); +simdutf_really_inline simd16 operator^(const simd16 a, + uint16_t b) { + const auto bv = __lasx_xvreplgr2vr_h(b); + return __lasx_xvxor_v(a.value, bv); } -simdutf_really_inline simd16 as_vector_u16(const simd16 x) { - return x.value; +simdutf_really_inline simd16 operator^(const simd16 a, + const simd16 b) { + return __lasx_xvxor_v(a.value, b.value); } -/* end file src/simdutf/lsx/simd16-inl.h */ -/* begin file src/simdutf/lsx/simd32-inl.h */ +/* end file src/simdutf/lasx/simd16-inl.h */ +/* begin file src/simdutf/lasx/simd32-inl.h */ template struct simd32; template <> struct simd32 { - __m128i value; + __m256i value; static const int SIZE = sizeof(value); static const int ELEMENTS = SIZE / sizeof(uint32_t); // constructors - simdutf_really_inline simd32(__m128i v) : value(v) {} + simdutf_really_inline simd32(__m256i v) : value(v) {} template - simdutf_really_inline simd32(Ptr *ptr) : value(__lsx_vld(ptr, 0)) {} + simdutf_really_inline simd32(Ptr *ptr) : value(__lasx_xvld(ptr, 0)) {} // in-place operators simdutf_really_inline simd32 &operator-=(const simd32 other) { - value = __lsx_vsub_w(value, other.value); + value = __lasx_xvsub_w(value, other.value); return *this; } // members simdutf_really_inline uint64_t sum() const { - return uint64_t(__lsx_vpickve2gr_wu(value, 0)) + - uint64_t(__lsx_vpickve2gr_wu(value, 1)) + - uint64_t(__lsx_vpickve2gr_wu(value, 2)) + - uint64_t(__lsx_vpickve2gr_wu(value, 3)); + const auto odd = __lasx_xvsrli_d(value, 32); + const auto even = __lasx_xvand_v(value, __lasx_xvreplgr2vr_d(0xffffffff)); + + const auto sum64 = __lasx_xvadd_d(odd, even); + + return uint64_t(__lasx_xvpickve2gr_du(sum64, 0)) + + uint64_t(__lasx_xvpickve2gr_du(sum64, 1)) + + uint64_t(__lasx_xvpickve2gr_du(sum64, 2)) + + uint64_t(__lasx_xvpickve2gr_du(sum64, 3)); } // static members static simdutf_really_inline simd32 splat(uint32_t x) { - return __lsx_vreplgr2vr_w(x); + return __lasx_xvreplgr2vr_w(x); } static simdutf_really_inline simd32 zero() { - return __lsx_vrepli_w(0); + return __lasx_xvrepli_w(0); } }; // ------------------------------------------------------------ template <> struct simd32 { - __m128i value; + __m256i value; static const int SIZE = sizeof(value); // constructors - simdutf_really_inline simd32(__m128i v) : value(v) {} + simdutf_really_inline simd32(__m256i v) : value(v) {} }; // ------------------------------------------------------------ simdutf_really_inline simd32 operator&(const simd32 a, const simd32 b) { - return __lsx_vor_v(a.value, b.value); + return __lasx_xvor_v(a.value, b.value); } simdutf_really_inline simd32 operator<(const simd32 a, const simd32 b) { - return __lsx_vslt_wu(a.value, b.value); + return __lasx_xvslt_wu(a.value, b.value); } simdutf_really_inline simd32 operator>(const simd32 a, const simd32 b) { - return __lsx_vslt_wu(b.value, a.value); + return __lasx_xvslt_wu(b.value, a.value); } // ------------------------------------------------------------ @@ -9189,112 +8646,128 @@ simdutf_really_inline simd32 operator>(const simd32 a, simdutf_really_inline simd32 as_vector_u32(const simd32 v) { return v.value; } -/* end file src/simdutf/lsx/simd32-inl.h */ -/* begin file src/simdutf/lsx/simd64-inl.h */ +/* end file src/simdutf/lasx/simd32-inl.h */ +/* begin file src/simdutf/lasx/simd64-inl.h */ template struct simd64; template <> struct simd64 { - __m128i value; + __m256i value; static const int SIZE = sizeof(value); static const int ELEMENTS = SIZE / sizeof(uint64_t); // constructors - simdutf_really_inline simd64(__m128i v) : value(v) {} + simdutf_really_inline simd64(__m256i v) : value(v) {} template - simdutf_really_inline simd64(Ptr *ptr) : value(__lsx_vld(ptr, 0)) {} + simdutf_really_inline simd64(Ptr *ptr) : value(__lasx_xvld(ptr, 0)) {} // in-place operators simdutf_really_inline simd64 &operator+=(const simd64 other) { - value = __lsx_vadd_d(value, other.value); + value = __lasx_xvadd_d(value, other.value); return *this; } // members simdutf_really_inline uint64_t sum() const { - return uint64_t(__lsx_vpickve2gr_du(value, 0)) + - uint64_t(__lsx_vpickve2gr_du(value, 1)); + return uint64_t(__lasx_xvpickve2gr_du(value, 0)) + + uint64_t(__lasx_xvpickve2gr_du(value, 1)) + + uint64_t(__lasx_xvpickve2gr_du(value, 2)) + + uint64_t(__lasx_xvpickve2gr_du(value, 3)); } // static members static simdutf_really_inline simd64 zero() { - return __lsx_vrepli_d(0); + return __lasx_xvrepli_d(0); } }; // ------------------------------------------------------------ template <> struct simd64 { - __m128i value; + __m256i value; static const int SIZE = sizeof(value); // constructors - simdutf_really_inline simd64(__m128i v) : value(v) {} + simdutf_really_inline simd64(__m256i v) : value(v) {} }; // ------------------------------------------------------------ simd64 sum_8bytes(const simd8 v) { - const auto sum_u16 = __lsx_vhaddw_hu_bu(v, v); - const auto sum_u32 = __lsx_vhaddw_wu_hu(sum_u16, sum_u16); - const auto sum_u64 = __lsx_vhaddw_du_wu(sum_u32, sum_u32); + const auto sum_u16 = __lasx_xvhaddw_hu_bu(v, v); + const auto sum_u32 = __lasx_xvhaddw_wu_hu(sum_u16, sum_u16); + const auto sum_u64 = __lasx_xvhaddw_du_wu(sum_u32, sum_u32); return simd64(sum_u64); } -/* end file src/simdutf/lsx/simd64-inl.h */ +/* end file src/simdutf/lasx/simd64-inl.h */ } // namespace simd } // unnamed namespace -} // namespace lsx +} // namespace lasx } // namespace simdutf -#endif // SIMDUTF_LSX_SIMD_H -/* end file src/simdutf/lsx/simd.h */ +#endif // SIMDUTF_LASX_SIMD_H +/* end file src/simdutf/lasx/simd.h */ -/* begin file src/simdutf/lsx/end.h */ +/* begin file src/simdutf/lasx/end.h */ #undef SIMDUTF_SIMD_HAS_UNSIGNED_CMP -/* end file src/simdutf/lsx/end.h */ -#endif // SIMDUTF_IMPLEMENTATION_LSX +#if SIMDUTF_CAN_ALWAYS_RUN_LASX +// nothing needed. +#else +SIMDUTF_UNTARGET_REGION +#endif +/* end file src/simdutf/lasx/end.h */ -#endif // SIMDUTF_LSX_H -/* end file src/simdutf/lsx.h */ -/* begin file src/simdutf/lasx.h */ -#ifndef SIMDUTF_LASX_H -#define SIMDUTF_LASX_H +#endif // SIMDUTF_IMPLEMENTATION_LASX + +#endif // SIMDUTF_LASX_H +/* end file src/simdutf/lasx.h */ +/* begin file src/simdutf/lsx.h */ +#ifndef SIMDUTF_LSX_H +#define SIMDUTF_LSX_H #ifdef SIMDUTF_FALLBACK_H - #error "lasx.h must be included before fallback.h" + #error "lsx.h must be included before fallback.h" +#endif + +#ifndef SIMDUTF_CAN_ALWAYS_RUN_LASX + #error "lsx.h must be included after lasx.h" #endif -#ifndef SIMDUTF_IMPLEMENTATION_LASX - #define SIMDUTF_IMPLEMENTATION_LASX (SIMDUTF_IS_LASX) +#ifndef SIMDUTF_IMPLEMENTATION_LSX + #if SIMDUTF_CAN_ALWAYS_RUN_LASX + #define SIMDUTF_IMPLEMENTATION_LSX 0 + #else + #define SIMDUTF_IMPLEMENTATION_LSX (SIMDUTF_IS_LSX) + #endif #endif -#if SIMDUTF_IMPLEMENTATION_LASX && SIMDUTF_IS_LASX - #define SIMDUTF_CAN_ALWAYS_RUN_LASX 1 +#if SIMDUTF_IMPLEMENTATION_LSX && SIMDUTF_IS_LSX + #define SIMDUTF_CAN_ALWAYS_RUN_LSX 1 #else - #define SIMDUTF_CAN_ALWAYS_RUN_LASX 0 + #define SIMDUTF_CAN_ALWAYS_RUN_LSX 0 #endif #define SIMDUTF_CAN_ALWAYS_RUN_FALLBACK (SIMDUTF_IMPLEMENTATION_FALLBACK) -#if SIMDUTF_IMPLEMENTATION_LASX +#if SIMDUTF_IMPLEMENTATION_LSX namespace simdutf { /** - * Implementation for LoongArch ASX. + * Implementation for LoongArch SX. */ -namespace lasx {} // namespace lasx +namespace lsx {} // namespace lsx } // namespace simdutf -/* begin file src/simdutf/lasx/implementation.h */ -#ifndef SIMDUTF_LASX_IMPLEMENTATION_H -#define SIMDUTF_LASX_IMPLEMENTATION_H +/* begin file src/simdutf/lsx/implementation.h */ +#ifndef SIMDUTF_LSX_IMPLEMENTATION_H +#define SIMDUTF_LSX_IMPLEMENTATION_H namespace simdutf { -namespace lasx { +namespace lsx { namespace { using namespace simdutf; @@ -9303,148 +8776,64 @@ using namespace simdutf; class implementation final : public simdutf::implementation { public: simdutf_really_inline implementation() - : simdutf::implementation("lasx", "LOONGARCH ASX", - internal::instruction_set::LSX | - internal::instruction_set::LASX) {} + : simdutf::implementation("lsx", "LOONGARCH SX", + internal::instruction_set::LSX) {} simdutf_warn_unused result base64_to_binary( const char *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; simdutf_warn_unused full_result base64_to_binary_details( const char *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; - simdutf_warn_unused result - base64_to_binary(const char16_t *input, size_t length, char *output, - base64_options options, - last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; + simdutf_warn_unused result base64_to_binary( + const char16_t *input, size_t length, char *output, + base64_options options, + last_chunk_handling_options last_chunk_options = + last_chunk_handling_options::loose) const noexcept override; simdutf_warn_unused full_result base64_to_binary_details( const char16_t *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; size_t binary_to_base64(const char *input, size_t length, char *output, - base64_options options) const noexcept; + base64_options options) const noexcept override; + size_t + binary_to_base64_with_lines(const char *input, size_t length, char *output, + size_t line_length, + base64_options options) const noexcept override; const char *find(const char *start, const char *end, - char character) const noexcept; + char character) const noexcept override; const char16_t *find(const char16_t *start, const char16_t *end, - char16_t character) const noexcept; + char16_t character) const noexcept override; + simdutf_warn_unused size_t binary_length_from_base64( + const char *input, size_t length) const noexcept override; + simdutf_warn_unused size_t binary_length_from_base64( + const char16_t *input, size_t length) const noexcept override; }; -} // namespace lasx +} // namespace lsx } // namespace simdutf -#endif // SIMDUTF_LASX_IMPLEMENTATION_H -/* end file src/simdutf/lasx/implementation.h */ - -/* begin file src/simdutf/lasx/begin.h */ -// redefining SIMDUTF_IMPLEMENTATION to "lasx" -// #define SIMDUTF_IMPLEMENTATION lasx -#define SIMDUTF_SIMD_HAS_UNSIGNED_CMP 1 -/* end file src/simdutf/lasx/begin.h */ - - // Declarations -/* begin file src/simdutf/lasx/intrinsics.h */ -#ifndef SIMDUTF_LASX_INTRINSICS_H -#define SIMDUTF_LASX_INTRINSICS_H - - -// This should be the correct header whether -// you use visual studio or other compilers. -#include -#include +#endif // SIMDUTF_LSX_IMPLEMENTATION_H +/* end file src/simdutf/lsx/implementation.h */ + +/* begin file src/simdutf/lsx/begin.h */ +// redefining SIMDUTF_IMPLEMENTATION to "lsx" +// #define SIMDUTF_IMPLEMENTATION lsx +#define SIMDUTF_SIMD_HAS_UNSIGNED_CMP 1 +/* end file src/simdutf/lsx/begin.h */ + + // Declarations +/* begin file src/simdutf/lsx/intrinsics.h */ +#ifndef SIMDUTF_LSX_INTRINSICS_H +#define SIMDUTF_LSX_INTRINSICS_H -#if defined(__loongarch_asx) - #ifdef __clang__ - #define VREGS_PREFIX "$vr" - #define XREGS_PREFIX "$xr" - #else // GCC - #define VREGS_PREFIX "$f" - #define XREGS_PREFIX "$f" - #endif - #define __ALL_REGS \ - "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26," \ - "27,28,29,30,31" -// Convert __m128i to __m256i -static inline __m256i ____m256i(__m128i in) { - __m256i out = __lasx_xvldi(0); - __asm__ volatile(".irp i," __ALL_REGS "\n\t" - " .ifc %[out], " XREGS_PREFIX "\\i \n\t" - " .irp j," __ALL_REGS "\n\t" - " .ifc %[in], " VREGS_PREFIX "\\j \n\t" - " xvpermi.q $xr\\i, $xr\\j, 0x0 \n\t" - " .endif \n\t" - " .endr \n\t" - " .endif \n\t" - ".endr \n\t" - : [out] "+f"(out) - : [in] "f"(in)); - return out; -} -// Convert two __m128i to __m256i -static inline __m256i lasx_set_q(__m128i inhi, __m128i inlo) { - __m256i out; - __asm__ volatile(".irp i," __ALL_REGS "\n\t" - " .ifc %[hi], " VREGS_PREFIX "\\i \n\t" - " .irp j," __ALL_REGS "\n\t" - " .ifc %[lo], " VREGS_PREFIX "\\j \n\t" - " xvpermi.q $xr\\i, $xr\\j, 0x20 \n\t" - " .endif \n\t" - " .endr \n\t" - " .endif \n\t" - ".endr \n\t" - ".ifnc %[out], %[hi] \n\t" - ".irp i," __ALL_REGS "\n\t" - " .ifc %[out], " XREGS_PREFIX "\\i \n\t" - " .irp j," __ALL_REGS "\n\t" - " .ifc %[hi], " VREGS_PREFIX "\\j \n\t" - " xvori.b $xr\\i, $xr\\j, 0 \n\t" - " .endif \n\t" - " .endr \n\t" - " .endif \n\t" - ".endr \n\t" - ".endif \n\t" - : [out] "=f"(out), [hi] "+f"(inhi) - : [lo] "f"(inlo)); - return out; -} -// Convert __m256i low part to __m128i -static inline __m128i lasx_extracti128_lo(__m256i in) { - __m128i out; - __asm__ volatile(".ifnc %[out], %[in] \n\t" - ".irp i," __ALL_REGS "\n\t" - " .ifc %[out], " VREGS_PREFIX "\\i \n\t" - " .irp j," __ALL_REGS "\n\t" - " .ifc %[in], " XREGS_PREFIX "\\j \n\t" - " vori.b $vr\\i, $vr\\j, 0 \n\t" - " .endif \n\t" - " .endr \n\t" - " .endif \n\t" - ".endr \n\t" - ".endif \n\t" - : [out] "=f"(out) - : [in] "f"(in)); - return out; -} -// Convert __m256i high part to __m128i -static inline __m128i lasx_extracti128_hi(__m256i in) { - __m128i out; - __asm__ volatile(".irp i," __ALL_REGS "\n\t" - " .ifc %[out], " VREGS_PREFIX "\\i \n\t" - " .irp j," __ALL_REGS "\n\t" - " .ifc %[in], " XREGS_PREFIX "\\j \n\t" - " xvpermi.q $xr\\i, $xr\\j, 0x11 \n\t" - " .endif \n\t" - " .endr \n\t" - " .endif \n\t" - ".endr \n\t" - : [out] "=f"(out) - : [in] "f"(in)); - return out; -} -#endif + +// This should be the correct header whether +// you use visual studio or other compilers. +#include /* Encoding of argument for LoongArch64 xvldi instruction. See: @@ -9474,7 +8863,7 @@ all lanes the result as 64-bit elements to all lanes */ -namespace lasx_vldi { +namespace vldi { template class const_u16 { constexpr static const uint8_t b0 = ((v >> 0 * 8) & 0xff); @@ -9591,412 +8980,235 @@ template class const_u64 { constexpr static int value = int((operation << 8) | byte) - 8192; constexpr static bool valid = operation != 0xffff; }; +} // namespace vldi -} // namespace lasx_vldi - -// Uncomment when running under QEMU affected -// by bug https://gitlab.com/qemu-project/qemu/-/issues/2865 -// Versions <= 9.2.2 are affected, likely anything newer is correct. -#ifndef QEMU_VLDI_BUG -// #define QEMU_VLDI_BUG 1 -#endif - -#ifdef QEMU_VLDI_BUG - #define lasx_splat_u16(v) __lasx_xvreplgr2vr_h(v) - #define lasx_splat_u32(v) __lasx_xvreplgr2vr_w(v) -#else -template constexpr __m256i lasx_splat_u16_aux() { - constexpr bool is_imm10 = (int16_t(x) < 512) && (int16_t(x) > -512); - constexpr uint16_t imm10 = is_imm10 ? x : 0; - constexpr bool is_vldi = lasx_vldi::const_u16::valid; - constexpr int vldi_imm = is_vldi ? lasx_vldi::const_u16::value : 0; - - return is_imm10 ? __lasx_xvrepli_h(int16_t(imm10)) - : is_vldi ? __lasx_xvldi(vldi_imm) - : __lasx_xvreplgr2vr_h(x); -} - -template constexpr __m256i lasx_splat_u32_aux() { - constexpr bool is_imm10 = (int32_t(x) < 512) && (int32_t(x) > -512); - constexpr uint32_t imm10 = is_imm10 ? x : 0; - constexpr bool is_vldi = lasx_vldi::const_u32::valid; - constexpr int vldi_imm = is_vldi ? lasx_vldi::const_u32::value : 0; - - return is_imm10 ? __lasx_xvrepli_w(int32_t(imm10)) - : is_vldi ? __lasx_xvldi(vldi_imm) - : __lasx_xvreplgr2vr_w(x); -} - - #define lasx_splat_u16(v) lasx_splat_u16_aux<(v)>() - #define lasx_splat_u32(v) lasx_splat_u32_aux<(v)>() -#endif // QEMU_VLDI_BUG - -#endif // SIMDUTF_LASX_INTRINSICS_H -/* end file src/simdutf/lasx/intrinsics.h */ -/* begin file src/simdutf/lasx/bitmanipulation.h */ -#ifndef SIMDUTF_LASX_BITMANIPULATION_H -#define SIMDUTF_LASX_BITMANIPULATION_H - -#include - -namespace simdutf { -namespace lasx { -namespace { - -simdutf_really_inline int count_ones(uint64_t input_num) { - return __lsx_vpickve2gr_w(__lsx_vpcnt_d(__lsx_vreplgr2vr_d(input_num)), 0); -} - -#if SIMDUTF_NEED_TRAILING_ZEROES -simdutf_really_inline int trailing_zeroes(uint64_t input_num) { - return __builtin_ctzll(input_num); -} -#endif - -} // unnamed namespace -} // namespace lasx -} // namespace simdutf - -#endif // SIMDUTF_LASX_BITMANIPULATION_H -/* end file src/simdutf/lasx/bitmanipulation.h */ -/* begin file src/simdutf/lasx/simd.h */ -#ifndef SIMDUTF_LASX_SIMD_H -#define SIMDUTF_LASX_SIMD_H - - -namespace simdutf { -namespace lasx { -namespace { -namespace simd { - -__attribute__((aligned(32))) static const uint8_t prev_shuf_table[32][32] = { - {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, - {0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 31, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, - {0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, - 30, 31, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, - {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, - 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, - {0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, - 28, 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, - {0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 27, 28, 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, - {0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 26, 27, 28, 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, - {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, - 25, 26, 27, 28, 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, 7, 8}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, - 24, 25, 26, 27, 28, 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, 7}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, - 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1, 2, 3, 4, 5, 6}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, - 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1, 2, 3, 4, 5}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, - 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1, 2, 3, 4}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1, 2, 3}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, - 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1, 2}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0}, - {15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, - 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, - 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, - 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, - 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, - 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0}, - {7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, - 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0}, - {6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, - 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0}, - {5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0}, - {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0}, - {3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, - 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0}, - {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, - 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0}, - {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0}, - {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, -}; - -__attribute__((aligned(32))) static const uint8_t bitsel_mask_table[32][32] = { - {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0}, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0}}; +// Uncomment when running under QEMU affected +// by bug https://gitlab.com/qemu-project/qemu/-/issues/2865 +// Versions <= 9.2.2 are affected, likely anything newer is correct. +#ifndef QEMU_VLDI_BUG +// #define QEMU_VLDI_BUG 1 +#endif -// Forward-declared so they can be used by splat and friends. -template struct base { - __m256i value; +#ifndef lsx_splat_u16 + #ifdef QEMU_VLDI_BUG + #define lsx_splat_u16(v) __lsx_vreplgr2vr_h(v) + #define lsx_splat_u32(v) __lsx_vreplgr2vr_w(v) + #else +namespace { +template constexpr __m128i lsx_splat_u16_aux() { + return ((int16_t(x) < 512) && (int16_t(x) > -512)) + ? __lsx_vrepli_h( + ((int16_t(x) < 512) && (int16_t(x) > -512)) ? int16_t(x) : 0) + : (vldi::const_u16::valid + ? __lsx_vldi(vldi::const_u16::valid + ? vldi::const_u16::value + : 0) + : __lsx_vreplgr2vr_h(x)); +} - // Zero constructor - simdutf_really_inline base() : value{__m256i()} {} +template constexpr __m128i lsx_splat_u32_aux() { + return ((int32_t(x) < 512) && (int32_t(x) > -512)) + ? __lsx_vrepli_w( + ((int32_t(x) < 512) && (int32_t(x) > -512)) ? int32_t(x) : 0) + : (vldi::const_u32::valid + ? __lsx_vldi(vldi::const_u32::valid + ? vldi::const_u32::value + : 0) + : __lsx_vreplgr2vr_w(x)); +} +} // namespace + #define lsx_splat_u16(v) lsx_splat_u16_aux<(v)>() + #define lsx_splat_u32(v) lsx_splat_u32_aux<(v)>() + #endif // QEMU_VLDI_BUG +#endif // lsx_splat_u16 +#endif // SIMDUTF_LSX_INTRINSICS_H +/* end file src/simdutf/lsx/intrinsics.h */ +/* begin file src/simdutf/lsx/bitmanipulation.h */ +#ifndef SIMDUTF_LSX_BITMANIPULATION_H +#define SIMDUTF_LSX_BITMANIPULATION_H - // Conversion from SIMD register - simdutf_really_inline base(const __m256i _value) : value(_value) {} - // Conversion to SIMD register - simdutf_really_inline operator const __m256i &() const { return this->value; } - simdutf_really_inline operator __m256i &() { return this->value; } - template - simdutf_really_inline void store_ascii_as_utf16(char16_t *ptr) const { - if (big_endian) { - __m256i zero = __lasx_xvldi(0); - __m256i in8 = __lasx_xvpermi_d(this->value, 0b11011000); - __m256i inlow = __lasx_xvilvl_b(in8, zero); - __m256i inhigh = __lasx_xvilvh_b(in8, zero); - __lasx_xvst(inlow, reinterpret_cast(ptr), 0); - __lasx_xvst(inhigh, reinterpret_cast(ptr), 32); - } else { - __m256i inlow = __lasx_vext2xv_hu_bu(this->value); - __m256i inhigh = __lasx_vext2xv_hu_bu( - __lasx_xvpermi_q(this->value, this->value, 0b00000001)); - __lasx_xvst(inlow, reinterpret_cast<__m256i *>(ptr), 0); - __lasx_xvst(inhigh, reinterpret_cast<__m256i *>(ptr), 32); - } - } - simdutf_really_inline void store_ascii_as_utf32(char32_t *ptr) const { - __m256i in32_0 = __lasx_vext2xv_wu_bu(this->value); - __lasx_xvst(in32_0, reinterpret_cast(ptr), 0); +#include - __m256i in8_1 = __lasx_xvpermi_d(this->value, 0b00000001); - __m256i in32_1 = __lasx_vext2xv_wu_bu(in8_1); - __lasx_xvst(in32_1, reinterpret_cast(ptr), 32); +namespace simdutf { +namespace lsx { +namespace { - __m256i in8_2 = __lasx_xvpermi_d(this->value, 0b00000010); - __m256i in32_2 = __lasx_vext2xv_wu_bu(in8_2); - __lasx_xvst(in32_2, reinterpret_cast(ptr), 64); +simdutf_really_inline int count_ones(uint64_t input_num) { + return __lsx_vpickve2gr_w(__lsx_vpcnt_d(__lsx_vreplgr2vr_d(input_num)), 0); +} + +#if SIMDUTF_NEED_TRAILING_ZEROES +simdutf_really_inline int trailing_zeroes(uint64_t input_num) { + return __builtin_ctzll(input_num); +} +#endif + +} // unnamed namespace +} // namespace lsx +} // namespace simdutf + +#endif // SIMDUTF_LSX_BITMANIPULATION_H +/* end file src/simdutf/lsx/bitmanipulation.h */ +/* begin file src/simdutf/lsx/simd.h */ +#ifndef SIMDUTF_LSX_SIMD_H +#define SIMDUTF_LSX_SIMD_H + + +namespace simdutf { +namespace lsx { +namespace { +namespace simd { + +template struct simd8; + +// +// Base class of simd8 and simd8, both of which use __m128i +// internally. +// +template > struct base_u8 { + __m128i value; + static const int SIZE = sizeof(value); + + // Conversion from/to SIMD register + simdutf_really_inline base_u8(const __m128i _value) : value(_value) {} + simdutf_really_inline operator const __m128i &() const { return this->value; } + simdutf_really_inline operator __m128i &() { return this->value; } - __m256i in8_3 = __lasx_xvpermi_d(this->value, 0b00000011); - __m256i in32_3 = __lasx_vext2xv_wu_bu(in8_3); - __lasx_xvst(in32_3, reinterpret_cast(ptr), 96); - } // Bit operations - simdutf_really_inline Child operator|(const Child other) const { - return __lasx_xvor_v(this->value, other); + simdutf_really_inline simd8 operator|(const simd8 other) const { + return __lsx_vor_v(this->value, other); } - simdutf_really_inline Child operator&(const Child other) const { - return __lasx_xvand_v(this->value, other); + simdutf_really_inline simd8 operator&(const simd8 other) const { + return __lsx_vand_v(this->value, other); } - simdutf_really_inline Child operator^(const Child other) const { - return __lasx_xvxor_v(this->value, other); + simdutf_really_inline simd8 operator^(const simd8 other) const { + return __lsx_vxor_v(this->value, other); } - simdutf_really_inline Child &operator|=(const Child other) { - auto this_cast = static_cast(this); + simdutf_really_inline simd8 operator~() const { return *this ^ 0xFFu; } + simdutf_really_inline simd8 &operator|=(const simd8 other) { + auto this_cast = static_cast *>(this); *this_cast = *this_cast | other; return *this_cast; } -}; - -template struct simd8; -template > -struct base8 : base> { - simdutf_really_inline base8() : base>() {} - simdutf_really_inline base8(const __m256i _value) : base>(_value) {} friend simdutf_really_inline Mask operator==(const simd8 lhs, const simd8 rhs) { - return __lasx_xvseq_b(lhs, rhs); + return __lsx_vseq_b(lhs, rhs); } - static const int SIZE = sizeof(base::value); - - template + template simdutf_really_inline simd8 prev(const simd8 prev_chunk) const { - static_assert(N <= 16, "unsupported shift value"); - - if (!N) - return this->value; - - __m256i zero = __lasx_xvldi(0); - __m256i result, shuf; - if (N < 16) { - shuf = __lasx_xvld(prev_shuf_table[N], 0); - - result = __lasx_xvshuf_b( - __lasx_xvpermi_q(this->value, this->value, 0b00000001), this->value, - shuf); - __m256i srl_prev = __lasx_xvbsrl_v( - __lasx_xvpermi_q(zero, prev_chunk.value, 0b00110001), (16 - N)); - __m256i mask = __lasx_xvld(bitsel_mask_table[N], 0); - result = __lasx_xvbitsel_v(result, srl_prev, mask); - - return result; - } else if (N == 16) { - return __lasx_xvpermi_q(this->value, prev_chunk.value, 0b00100001); - } + return __lsx_vor_v(__lsx_vbsll_v(this->value, N), + __lsx_vbsrl_v(prev_chunk.value, 16 - N)); } }; // SIMD byte mask type (returned by things like eq and gt) -template <> struct simd8 : base8 { +template <> struct simd8 : base_u8 { + typedef uint16_t bitmask_t; + typedef uint32_t bitmask2_t; + static simdutf_really_inline simd8 splat(bool _value) { - return __lasx_xvreplgr2vr_b(uint8_t(-(!!_value))); + return __lsx_vreplgr2vr_b(uint8_t(-(!!_value))); } - simdutf_really_inline simd8() : base8() {} - simdutf_really_inline simd8(const __m256i _value) : base8(_value) {} + simdutf_really_inline simd8(const __m128i _value) : base_u8(_value) {} + // False constructor + simdutf_really_inline simd8() : simd8(__lsx_vldi(0)) {} // Splat constructor - simdutf_really_inline simd8(bool _value) : base8(splat(_value)) {} + simdutf_really_inline simd8(bool _value) : simd8(splat(_value)) {} + simdutf_really_inline void store(uint8_t dst[16]) const { + return __lsx_vst(this->value, dst, 0); + } simdutf_really_inline uint32_t to_bitmask() const { - __m256i mask = __lasx_xvmsknz_b(this->value); - uint32_t mask0 = __lasx_xvpickve2gr_wu(mask, 0); - uint32_t mask1 = __lasx_xvpickve2gr_wu(mask, 4); - return (mask0 | (mask1 << 16)); - } - simdutf_really_inline bool any() const { - if (__lasx_xbz_b(this->value)) - return false; - return true; + return __lsx_vpickve2gr_wu(__lsx_vmsknz_b(*this), 0); } - simdutf_really_inline simd8 operator~() const { return *this ^ true; } }; -template struct base8_numeric : base8 { - static simdutf_really_inline simd8 splat(T _value) { - return __lasx_xvreplgr2vr_b(_value); +// Unsigned bytes +template <> struct simd8 : base_u8 { + static simdutf_really_inline simd8 splat(uint8_t _value) { + return __lsx_vreplgr2vr_b(_value); } - static simdutf_really_inline simd8 zero() { return __lasx_xvldi(0); } - static simdutf_really_inline simd8 load(const T values[32]) { - return __lasx_xvld(reinterpret_cast(values), 0); + static simdutf_really_inline simd8 zero() { return __lsx_vldi(0); } + static simdutf_really_inline simd8 load(const uint8_t *values) { + return __lsx_vld(values, 0); } + simdutf_really_inline simd8(const __m128i _value) + : base_u8(_value) {} + // Zero constructor + simdutf_really_inline simd8() : simd8(zero()) {} + // Array constructor + simdutf_really_inline simd8(const uint8_t values[16]) : simd8(load(values)) {} + // Splat constructor + simdutf_really_inline simd8(uint8_t _value) : simd8(splat(_value)) {} + // Member-by-member initialization + simdutf_really_inline + simd8(uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, + uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9, uint8_t v10, + uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15) + : simd8((__m128i)v16u8{v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, + v12, v13, v14, v15}) {} + // Repeat 16 values as many times as necessary (usually for lookup tables) - static simdutf_really_inline simd8 repeat_16(T v0, T v1, T v2, T v3, T v4, - T v5, T v6, T v7, T v8, T v9, - T v10, T v11, T v12, T v13, - T v14, T v15) { - return simd8(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, - v14, v15, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, - v12, v13, v14, v15); + simdutf_really_inline static simd8 + repeat_16(uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, + uint8_t v5, uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9, + uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, + uint8_t v15) { + return simd8(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15); } - simdutf_really_inline base8_numeric() : base8() {} - simdutf_really_inline base8_numeric(const __m256i _value) - : base8(_value) {} - // Store to array - simdutf_really_inline void store(T dst[32]) const { - return __lasx_xvst(this->value, reinterpret_cast<__m256i *>(dst), 0); + simdutf_really_inline void store(uint8_t dst[16]) const { + return __lsx_vst(this->value, dst, 0); + } + + // Order-specific operations + simdutf_really_inline simd8 + operator>=(const simd8 other) const { + return __lsx_vsle_bu(other, *this); + } + simdutf_really_inline simd8 + operator>(const simd8 other) const { + return __lsx_vslt_bu(other, *this); + } + simdutf_really_inline simd8 &operator-=(const simd8 other) { + value = __lsx_vsub_b(value, other.value); + return *this; + } + // Same as >, but instead of guaranteeing all 1's == true, false = 0 and true + // = nonzero. For ARM, returns all 1's. + simdutf_really_inline simd8 + gt_bits(const simd8 other) const { + return simd8(*this > other); + } + + // Bit-specific operations + simdutf_really_inline simd8 any_bits_set(simd8 bits) const { + return __lsx_vslt_bu(__lsx_vldi(0), __lsx_vand_v(this->value, bits)); + } + simdutf_really_inline bool is_ascii() const { + return __lsx_vpickve2gr_hu(__lsx_vmskgez_b(this->value), 0) == 0xFFFF; } - // Override to distinguish from bool version - simdutf_really_inline simd8 operator~() const { return *this ^ 0xFFu; } + simdutf_really_inline bool any_bits_set_anywhere() const { + return __lsx_vpickve2gr_hu(__lsx_vmsknz_b(this->value), 0) > 0; + } + template simdutf_really_inline simd8 shr() const { + return __lsx_vsrli_b(this->value, N); + } + template simdutf_really_inline simd8 shl() const { + return __lsx_vslli_b(this->value, N); + } // Perform a lookup assuming the value is between 0 and 16 (undefined behavior // for out of range values) template simdutf_really_inline simd8 lookup_16(simd8 lookup_table) const { - __m256i origin = __lasx_xvand_v(this->value, __lasx_xvldi(0x1f)); - return __lasx_xvshuf_b(__lasx_xvldi(0), lookup_table, origin); + return lookup_table.apply_lookup_16_to(*this); } template @@ -10010,115 +9222,150 @@ template struct base8_numeric : base8 { replace7, replace8, replace9, replace10, replace11, replace12, replace13, replace14, replace15)); } + + template + simdutf_really_inline simd8 + apply_lookup_16_to(const simd8 original) const { + __m128i original_tmp = __lsx_vand_v(original, __lsx_vldi(0x1f)); + return __lsx_vshuf_b(__lsx_vldi(0), *this, simd8(original_tmp)); + } + + simdutf_really_inline uint64_t sum_bytes() const { + const auto sum_u16 = __lsx_vhaddw_hu_bu(value, value); + const auto sum_u32 = __lsx_vhaddw_wu_hu(sum_u16, sum_u16); + const auto sum_u64 = __lsx_vhaddw_du_wu(sum_u32, sum_u32); + + return uint64_t(__lsx_vpickve2gr_du(sum_u64, 0)) + + uint64_t(__lsx_vpickve2gr_du(sum_u64, 1)); + } }; // Signed bytes -template <> struct simd8 : base8_numeric { - simdutf_really_inline simd8() : base8_numeric() {} - simdutf_really_inline simd8(const __m256i _value) - : base8_numeric(_value) {} +template <> struct simd8 { + __m128i value; - // Splat constructor - simdutf_really_inline simd8(int8_t _value) : simd8(splat(_value)) {} - // Array constructor - simdutf_really_inline simd8(const int8_t values[32]) : simd8(load(values)) {} - simdutf_really_inline operator simd8() const; - simdutf_really_inline bool is_ascii() const { - __m256i ascii_mask = __lasx_xvslti_b(this->value, 0); - if (__lasx_xbnz_v(ascii_mask)) - return false; - return true; + static const int SIZE = sizeof(value); + + static simdutf_really_inline simd8 splat(int8_t _value) { + return __lsx_vreplgr2vr_b(_value); } - // Order-sensitive comparisons - simdutf_really_inline simd8 operator>(const simd8 other) const { - return __lasx_xvslt_b(other, this->value); + static simdutf_really_inline simd8 zero() { return __lsx_vldi(0); } + static simdutf_really_inline simd8 load(const int8_t values[16]) { + return __lsx_vld(values, 0); } - simdutf_really_inline simd8 operator<(const simd8 other) const { - return __lasx_xvslt_b(this->value, other); + + template + simdutf_really_inline void store_ascii_as_utf16(char16_t *p) const { + __m128i zero = __lsx_vldi(0); + if constexpr (match_system(big_endian)) { + __lsx_vst(__lsx_vilvl_b(zero, (__m128i)this->value), + reinterpret_cast(p), 0); + __lsx_vst(__lsx_vilvh_b(zero, (__m128i)this->value), + reinterpret_cast(p + 8), 0); + } else { + __lsx_vst(__lsx_vilvl_b((__m128i)this->value, zero), + reinterpret_cast(p), 0); + __lsx_vst(__lsx_vilvh_b((__m128i)this->value, zero), + reinterpret_cast(p + 8), 0); + } } -}; -// Unsigned bytes -template <> struct simd8 : base8_numeric { - simdutf_really_inline simd8() : base8_numeric() {} - simdutf_really_inline simd8(const __m256i _value) - : base8_numeric(_value) {} + simdutf_really_inline void store_ascii_as_utf32(char32_t *p) const { + __m128i zero = __lsx_vldi(0); + __m128i in16low = __lsx_vilvl_b(zero, (__m128i)this->value); + __m128i in16high = __lsx_vilvh_b(zero, (__m128i)this->value); + __m128i in32_0 = __lsx_vilvl_h(zero, in16low); + __m128i in32_1 = __lsx_vilvh_h(zero, in16low); + __m128i in32_2 = __lsx_vilvl_h(zero, in16high); + __m128i in32_3 = __lsx_vilvh_h(zero, in16high); + __lsx_vst(in32_0, reinterpret_cast(p), 0); + __lsx_vst(in32_1, reinterpret_cast(p + 4), 0); + __lsx_vst(in32_2, reinterpret_cast(p + 8), 0); + __lsx_vst(in32_3, reinterpret_cast(p + 12), 0); + } + + // In places where the table can be reused, which is most uses in simdutf, it + // is worth it to do 4 table lookups, as there is no direct zero extension + // from u8 to u32. + simdutf_really_inline void store_ascii_as_utf32_tbl(char32_t *p) const { + const simd8 tb1{0, 255, 255, 255, 1, 255, 255, 255, + 2, 255, 255, 255, 3, 255, 255, 255}; + const simd8 tb2{4, 255, 255, 255, 5, 255, 255, 255, + 6, 255, 255, 255, 7, 255, 255, 255}; + const simd8 tb3{8, 255, 255, 255, 9, 255, 255, 255, + 10, 255, 255, 255, 11, 255, 255, 255}; + const simd8 tb4{12, 255, 255, 255, 13, 255, 255, 255, + 14, 255, 255, 255, 15, 255, 255, 255}; + + // encourage store pairing and interleaving + const auto shuf1 = this->apply_lookup_16_to(tb1); + const auto shuf2 = this->apply_lookup_16_to(tb2); + shuf1.store(reinterpret_cast(p)); + shuf2.store(reinterpret_cast(p + 4)); + + const auto shuf3 = this->apply_lookup_16_to(tb3); + const auto shuf4 = this->apply_lookup_16_to(tb4); + shuf3.store(reinterpret_cast(p + 8)); + shuf4.store(reinterpret_cast(p + 12)); + } + // Conversion from/to SIMD register + simdutf_really_inline simd8(const __m128i _value) : value(_value) {} + + // Zero constructor + simdutf_really_inline simd8() : simd8(zero()) {} // Splat constructor - simdutf_really_inline simd8(uint8_t _value) : simd8(splat(_value)) {} + simdutf_really_inline simd8(int8_t _value) : simd8(splat(_value)) {} // Array constructor - simdutf_really_inline simd8(const uint8_t values[32]) : simd8(load(values)) {} - // Member-by-member initialization - simdutf_really_inline - simd8(uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, - uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9, uint8_t v10, - uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15, - uint8_t v16, uint8_t v17, uint8_t v18, uint8_t v19, uint8_t v20, - uint8_t v21, uint8_t v22, uint8_t v23, uint8_t v24, uint8_t v25, - uint8_t v26, uint8_t v27, uint8_t v28, uint8_t v29, uint8_t v30, - uint8_t v31) - : simd8((__m256i)v32u8{v0, v1, v2, v3, v4, v5, v6, v7, - v8, v9, v10, v11, v12, v13, v14, v15, - v16, v17, v18, v19, v20, v21, v22, v23, - v24, v25, v26, v27, v28, v29, v30, v31}) {} + simdutf_really_inline simd8(const int8_t *values) : simd8(load(values)) {} - // Saturated math - simdutf_really_inline simd8 - saturating_sub(const simd8 other) const { - return __lasx_xvssub_bu(this->value, other); + // Store to array + simdutf_really_inline void store(int8_t dst[16]) const { + return __lsx_vst(value, dst, 0); } - // Same as >, but only guarantees true is nonzero (< guarantees true = -1) - simdutf_really_inline simd8 - gt_bits(const simd8 other) const { - return this->saturating_sub(other); - } - simdutf_really_inline simd8 - operator>=(const simd8 other) const { - return __lasx_xvsle_bu(other, *this); + simdutf_really_inline operator simd8() const { + return ((__m128i)this->value); } - simdutf_really_inline simd8 &operator-=(const simd8 other) { - value = __lasx_xvsub_b(value, other.value); - return *this; + + simdutf_really_inline simd8 + operator|(const simd8 other) const { + return __lsx_vor_v((__m128i)value, (__m128i)other.value); } - // Bit-specific operations simdutf_really_inline bool is_ascii() const { - __m256i ascii_mask = __lasx_xvslti_b(this->value, 0); - if (__lasx_xbnz_v(ascii_mask)) - return false; - return true; - } - simdutf_really_inline bool any_bits_set_anywhere() const { - if (__lasx_xbnz_v(this->value)) - return true; - return false; + return (__lsx_vpickve2gr_hu(__lsx_vmskgez_b((__m128i)this->value), 0) == + 0xffff); } - template simdutf_really_inline simd8 shr() const { - return __lasx_xvsrli_b(this->value, N); + + // Order-sensitive comparisons + simdutf_really_inline simd8 operator>(const simd8 other) const { + return __lsx_vslt_b((__m128i)other.value, (__m128i)value); } - template simdutf_really_inline simd8 shl() const { - return __lasx_xvslli_b(this->value, N); + simdutf_really_inline simd8 operator<(const simd8 other) const { + return __lsx_vslt_b((__m128i)value, (__m128i)other.value); } - simdutf_really_inline uint64_t sum_bytes() const { - const auto sum_u16 = __lasx_xvhaddw_hu_bu(value, value); - const auto sum_u32 = __lasx_xvhaddw_wu_hu(sum_u16, sum_u16); - const auto sum_u64 = __lasx_xvhaddw_du_wu(sum_u32, sum_u32); + template + simdutf_really_inline simd8 + prev(const simd8 prev_chunk) const { + return __lsx_vor_v(__lsx_vbsll_v(this->value, N), + __lsx_vbsrl_v(prev_chunk.value, 16 - N)); + } - return uint64_t(__lasx_xvpickve2gr_du(sum_u64, 0)) + - uint64_t(__lasx_xvpickve2gr_du(sum_u64, 1)) + - uint64_t(__lasx_xvpickve2gr_du(sum_u64, 2)) + - uint64_t(__lasx_xvpickve2gr_du(sum_u64, 3)); + template + simdutf_really_inline simd8 + apply_lookup_16_to(const simd8 original) const { + __m128i original_tmp = __lsx_vand_v(original, __lsx_vldi(0x1f)); + return __lsx_vshuf_b(__lsx_vldi(0), (__m128i)this->value, + simd8(original_tmp)); } }; -simdutf_really_inline simd8::operator simd8() const { - return this->value; -} template struct simd8x64 { static constexpr int NUM_CHUNKS = 64 / sizeof(simd8); - static_assert(NUM_CHUNKS == 2, - "LASX kernel should use two registers per 64-byte block."); + static_assert( + NUM_CHUNKS == 4, + "LoongArch kernel should use four registers per 64-byte block."); simd8 chunks[NUM_CHUNKS]; simd8x64(const simd8x64 &o) = delete; // no copy allowed @@ -10126,36 +9373,36 @@ template struct simd8x64 { operator=(const simd8 other) = delete; // no assignment allowed simd8x64() = delete; // no default constructor allowed - simdutf_really_inline simd8x64(const simd8 chunk0, const simd8 chunk1) - : chunks{chunk0, chunk1} {} + simdutf_really_inline simd8x64(const simd8 chunk0, const simd8 chunk1, + const simd8 chunk2, const simd8 chunk3) + : chunks{chunk0, chunk1, chunk2, chunk3} {} simdutf_really_inline simd8x64(const T *ptr) : chunks{simd8::load(ptr), - simd8::load(ptr + sizeof(simd8) / sizeof(T))} {} + simd8::load(ptr + sizeof(simd8) / sizeof(T)), + simd8::load(ptr + 2 * sizeof(simd8) / sizeof(T)), + simd8::load(ptr + 3 * sizeof(simd8) / sizeof(T))} {} simdutf_really_inline void store(T *ptr) const { this->chunks[0].store(ptr + sizeof(simd8) * 0 / sizeof(T)); this->chunks[1].store(ptr + sizeof(simd8) * 1 / sizeof(T)); - } - - simdutf_really_inline uint64_t to_bitmask() const { - uint64_t r_lo = uint32_t(this->chunks[0].to_bitmask()); - uint64_t r_hi = this->chunks[1].to_bitmask(); - return r_lo | (r_hi << 32); + this->chunks[2].store(ptr + sizeof(simd8) * 2 / sizeof(T)); + this->chunks[3].store(ptr + sizeof(simd8) * 3 / sizeof(T)); } simdutf_really_inline simd8x64 &operator|=(const simd8x64 &other) { this->chunks[0] |= other.chunks[0]; this->chunks[1] |= other.chunks[1]; + this->chunks[2] |= other.chunks[2]; + this->chunks[3] |= other.chunks[3]; return *this; } simdutf_really_inline simd8 reduce_or() const { - return this->chunks[0] | this->chunks[1]; + return (this->chunks[0] | this->chunks[1]) | + (this->chunks[2] | this->chunks[3]); } - simdutf_really_inline bool is_ascii() const { - return this->reduce_or().is_ascii(); - } + simdutf_really_inline bool is_ascii() const { return reduce_or().is_ascii(); } template simdutf_really_inline void store_ascii_as_utf16(char16_t *ptr) const { @@ -10163,117 +9410,158 @@ template struct simd8x64 { sizeof(simd8) * 0); this->chunks[1].template store_ascii_as_utf16(ptr + sizeof(simd8) * 1); + this->chunks[2].template store_ascii_as_utf16(ptr + + sizeof(simd8) * 2); + this->chunks[3].template store_ascii_as_utf16(ptr + + sizeof(simd8) * 3); } simdutf_really_inline void store_ascii_as_utf32(char32_t *ptr) const { - this->chunks[0].store_ascii_as_utf32(ptr + sizeof(simd8) * 0); - this->chunks[1].store_ascii_as_utf32(ptr + sizeof(simd8) * 1); + this->chunks[0].store_ascii_as_utf32_tbl(ptr + sizeof(simd8) * 0); + this->chunks[1].store_ascii_as_utf32_tbl(ptr + sizeof(simd8) * 1); + this->chunks[2].store_ascii_as_utf32_tbl(ptr + sizeof(simd8) * 2); + this->chunks[3].store_ascii_as_utf32_tbl(ptr + sizeof(simd8) * 3); + } + + simdutf_really_inline uint64_t to_bitmask() const { + __m128i mask = __lsx_vbsll_v(__lsx_vmsknz_b(this->chunks[3]), 6); + mask = __lsx_vor_v(mask, __lsx_vbsll_v(__lsx_vmsknz_b(this->chunks[2]), 4)); + mask = __lsx_vor_v(mask, __lsx_vbsll_v(__lsx_vmsknz_b(this->chunks[1]), 2)); + mask = __lsx_vor_v(mask, __lsx_vmsknz_b(this->chunks[0])); + return __lsx_vpickve2gr_du(mask, 0); } simdutf_really_inline uint64_t lt(const T m) const { const simd8 mask = simd8::splat(m); - return simd8x64(this->chunks[0] < mask, this->chunks[1] < mask) + return simd8x64(this->chunks[0] < mask, this->chunks[1] < mask, + this->chunks[2] < mask, this->chunks[3] < mask) .to_bitmask(); } - simdutf_really_inline uint64_t gt(const T m) const { const simd8 mask = simd8::splat(m); - return simd8x64(this->chunks[0] > mask, this->chunks[1] > mask) + return simd8x64(this->chunks[0] > mask, this->chunks[1] > mask, + this->chunks[2] > mask, this->chunks[3] > mask) + .to_bitmask(); + } + simdutf_really_inline uint64_t gteq(const T m) const { + const simd8 mask = simd8::splat(m); + return simd8x64(this->chunks[0] >= mask, this->chunks[1] >= mask, + this->chunks[2] >= mask, this->chunks[3] >= mask) .to_bitmask(); } simdutf_really_inline uint64_t gteq_unsigned(const uint8_t m) const { const simd8 mask = simd8::splat(m); - return simd8x64((simd8(__m256i(this->chunks[0])) >= mask), - (simd8(__m256i(this->chunks[1])) >= mask)) + return simd8x64(simd8(this->chunks[0].value) >= mask, + simd8(this->chunks[1].value) >= mask, + simd8(this->chunks[2].value) >= mask, + simd8(this->chunks[3].value) >= mask) .to_bitmask(); } }; // struct simd8x64 -/* begin file src/simdutf/lasx/simd16-inl.h */ +/* begin file src/simdutf/lsx/simd16-inl.h */ template struct simd16; +template > struct base_u16 { + __m128i value; + static const size_t SIZE = sizeof(value); + static const size_t ELEMENTS = sizeof(value) / sizeof(T); + + // Conversion from/to SIMD register + simdutf_really_inline base_u16() = default; + simdutf_really_inline base_u16(const __m128i _value) : value(_value) {} + // Bit operations + simdutf_really_inline simd16 operator|(const simd16 other) const { + return __lsx_vor_v(this->value, other.value); + } + simdutf_really_inline simd16 operator&(const simd16 other) const { + return __lsx_vand_v(this->value, other.value); + } + simdutf_really_inline simd16 operator~() const { + return __lsx_vxori_b(this->value, 0xFF); + } + + friend simdutf_really_inline Mask operator==(const simd16 lhs, + const simd16 rhs) { + return __lsx_vseq_h(lhs.value, rhs.value); + } + + template + simdutf_really_inline simd16 byte_right_shift() const { + return __lsx_vbsrl_v(this->value, N); + } + + simdutf_really_inline uint16_t first() const { + return uint16_t(__lsx_vpickve2gr_w(value, 0)); + } +}; + template > -struct base16 : base> { - using bitmask_type = uint32_t; +struct base16 : base_u16 { + using bitmask_type = uint16_t; - simdutf_really_inline base16() : base>() {} - simdutf_really_inline base16(const __m256i _value) - : base>(_value) {} + simdutf_really_inline base16() : base_u16() {} + simdutf_really_inline base16(const __m128i _value) : base_u16(_value) {} template simdutf_really_inline base16(const Pointer *ptr) - : base16(__lasx_xvld(reinterpret_cast(ptr), 0)) {} + : base16(__lsx_vld(ptr, 0)) {} - /// the size of vector in bytes - static const int SIZE = sizeof(base>::value); + static const int SIZE = sizeof(base_u16::value); - /// the number of elements of type T a vector can hold - static const int ELEMENTS = SIZE / sizeof(T); + template + simdutf_really_inline simd16 prev(const simd16 prev_chunk) const { + return __lsx_vor_v(__lsx_vbsll_v(*this, N * 2), + __lsx_vbsrl_v(prev_chunk, 16 - N * 2)); + } }; // SIMD byte mask type (returned by things like eq and gt) template <> struct simd16 : base16 { static simdutf_really_inline simd16 splat(bool _value) { - return __lasx_xvreplgr2vr_h(uint16_t(-(!!_value))); + return __lsx_vreplgr2vr_h(uint16_t(-(!!_value))); } simdutf_really_inline simd16() : base16() {} - simdutf_really_inline simd16(const __m256i _value) : base16(_value) {} - // Splat constructor - simdutf_really_inline simd16(bool _value) : base16(splat(_value)) {} + simdutf_really_inline simd16(const __m128i _value) : base16(_value) {} simdutf_really_inline bitmask_type to_bitmask() const { - __m256i mask = __lasx_xvmsknz_b(this->value); - bitmask_type mask0 = __lasx_xvpickve2gr_wu(mask, 0); - bitmask_type mask1 = __lasx_xvpickve2gr_wu(mask, 4); - return (mask0 | (mask1 << 16)); - } - simdutf_really_inline simd16 operator~() const { return *this ^ true; } - - simdutf_really_inline bool is_zero() const { - return __lasx_xbz_v(this->value); - } - - template simdutf_really_inline simd16 byte_right_shift() const { - const auto t0 = __lasx_xvbsrl_v(this->value, N); - const auto t1 = __lasx_xvpermi_q(this->value, __lasx_xvldi(0), 0b00000011); - const auto t2 = __lasx_xvbsll_v(t1, 16 - N); - const auto t3 = __lasx_xvor_v(t0, t2); - return t3; + __m128i mask = __lsx_vmsknz_b(this->value); + bitmask_type mask0 = bitmask_type(__lsx_vpickve2gr_wu(mask, 0)); + return mask0; } - simdutf_really_inline uint16_t first() const { - return uint16_t(__lasx_xvpickve2gr_w(value, 0)); - } + simdutf_really_inline bool is_zero() const { return __lsx_bz_v(this->value); } }; template struct base16_numeric : base16 { static simdutf_really_inline simd16 splat(T _value) { - return __lasx_xvreplgr2vr_h((uint16_t)_value); + return __lsx_vreplgr2vr_h(_value); } - static simdutf_really_inline simd16 zero() { return __lasx_xvldi(0); } + static simdutf_really_inline simd16 zero() { return __lsx_vldi(0); } + template static simdutf_really_inline simd16 load(const Pointer values) { - return __lasx_xvld(values, 0); + return __lsx_vld(values, 0); } - simdutf_really_inline base16_numeric() : base16() {} - simdutf_really_inline base16_numeric(const __m256i _value) + simdutf_really_inline base16_numeric(const __m128i _value) : base16(_value) {} // Store to array simdutf_really_inline void store(T dst[8]) const { - return __lasx_xvst(this->value, reinterpret_cast<__m256i *>(dst), 0); + return __lsx_vst(this->value, dst, 0); } // Override to distinguish from bool version - simdutf_really_inline simd16 operator~() const { return *this ^ 0xFFFFu; } + simdutf_really_inline simd16 operator~() const { + return __lsx_vxori_b(this->value, 0xFF); + } }; -// Unsigned code units +// Unsigned code unitstemplate<> template <> struct simd16 : base16_numeric { - simdutf_really_inline simd16() : base16_numeric() {} - simdutf_really_inline simd16(const __m256i _value) - : base16_numeric(_value) {} + simdutf_really_inline simd16(const __m128i _value) + : base16_numeric((__m128i)_value) {} // Splat constructor simdutf_really_inline simd16(uint16_t _value) : simd16(splat(_value)) {} @@ -10283,71 +9571,67 @@ template <> struct simd16 : base16_numeric { simdutf_really_inline simd16(const char16_t *values) : simd16(load(reinterpret_cast(values))) {} + // Copy constructor + simdutf_really_inline simd16(const simd16 mask) : simd16(mask.value) {} + // Order-specific operations simdutf_really_inline simd16 &operator+=(const simd16 other) { - value = __lasx_xvadd_h(value, other.value); + value = __lsx_vadd_h(value, other.value); return *this; } - // Change the endianness - simdutf_really_inline simd16 swap_bytes() const { - return __lasx_xvshuf4i_b(this->value, 0b10110001); - } - template static simdutf_really_inline simd8 pack_shifted_right(const simd16 &v0, const simd16 &v1) { - return __lasx_xvpermi_d(__lasx_xvssrlni_bu_h(v1.value, v0.value, N), - 0b11011000); + return __lsx_vssrlni_bu_h(v1.value, v0.value, N); } // Pack with the unsigned saturation of two uint16_t code units into single // uint8_t vector static simdutf_really_inline simd8 pack(const simd16 &v0, const simd16 &v1) { - return pack_shifted_right<0>(v0, v1); } - simdutf_really_inline uint64_t sum() const { - const auto sum_u32 = __lasx_xvhaddw_wu_hu(value, value); - const auto sum_u64 = __lasx_xvhaddw_du_wu(sum_u32, sum_u32); - - return uint64_t(__lasx_xvpickve2gr_du(sum_u64, 0)) + - uint64_t(__lasx_xvpickve2gr_du(sum_u64, 1)) + - uint64_t(__lasx_xvpickve2gr_du(sum_u64, 2)) + - uint64_t(__lasx_xvpickve2gr_du(sum_u64, 3)); + // Change the endianness + simdutf_really_inline simd16 swap_bytes() const { + return __lsx_vshuf4i_b(this->value, 0b10110001); } - template simdutf_really_inline simd16 byte_right_shift() const { - return __lasx_xvbsrl_v(this->value, N); + simdutf_really_inline uint64_t sum() const { + const auto sum_u32 = __lsx_vhaddw_wu_hu(value, value); + const auto sum_u64 = __lsx_vhaddw_du_wu(sum_u32, sum_u32); + + return uint64_t(__lsx_vpickve2gr_du(sum_u64, 0)) + + uint64_t(__lsx_vpickve2gr_du(sum_u64, 1)); } }; simdutf_really_inline simd16 operator<(const simd16 a, const simd16 b) { - return __lasx_xvslt_hu(a.value, b.value); + return __lsx_vslt_hu(a.value, b.value); } simdutf_really_inline simd16 operator>(const simd16 a, const simd16 b) { - return __lasx_xvslt_hu(b.value, a.value); + return __lsx_vslt_hu(b.value, a.value); } simdutf_really_inline simd16 operator<=(const simd16 a, const simd16 b) { - return __lasx_xvsle_hu(a.value, b.value); + return __lsx_vsle_hu(a.value, b.value); } simdutf_really_inline simd16 operator>=(const simd16 a, const simd16 b) { - return __lasx_xvsle_hu(b.value, a.value); + return __lsx_vsle_hu(b.value, a.value); } template struct simd16x32 { static constexpr int NUM_CHUNKS = 64 / sizeof(simd16); - static_assert(NUM_CHUNKS == 2, - "LASX kernel should use two registers per 64-byte block."); + static_assert( + NUM_CHUNKS == 4, + "LOONGARCH kernel should use four registers per 64-byte block."); simd16 chunks[NUM_CHUNKS]; simd16x32(const simd16x32 &o) = delete; // no copy allowed @@ -10355,139 +9639,133 @@ template struct simd16x32 { operator=(const simd16 other) = delete; // no assignment allowed simd16x32() = delete; // no default constructor allowed - simdutf_really_inline simd16x32(const simd16 chunk0, - const simd16 chunk1) - : chunks{chunk0, chunk1} {} + simdutf_really_inline + simd16x32(const simd16 chunk0, const simd16 chunk1, + const simd16 chunk2, const simd16 chunk3) + : chunks{chunk0, chunk1, chunk2, chunk3} {} simdutf_really_inline simd16x32(const T *ptr) : chunks{simd16::load(ptr), - simd16::load(ptr + sizeof(simd16) / sizeof(T))} {} + simd16::load(ptr + sizeof(simd16) / sizeof(T)), + simd16::load(ptr + 2 * sizeof(simd16) / sizeof(T)), + simd16::load(ptr + 3 * sizeof(simd16) / sizeof(T))} {} simdutf_really_inline void store(T *ptr) const { this->chunks[0].store(ptr + sizeof(simd16) * 0 / sizeof(T)); this->chunks[1].store(ptr + sizeof(simd16) * 1 / sizeof(T)); + this->chunks[2].store(ptr + sizeof(simd16) * 2 / sizeof(T)); + this->chunks[3].store(ptr + sizeof(simd16) * 3 / sizeof(T)); } simdutf_really_inline void swap_bytes() { this->chunks[0] = this->chunks[0].swap_bytes(); this->chunks[1] = this->chunks[1].swap_bytes(); + this->chunks[2] = this->chunks[2].swap_bytes(); + this->chunks[3] = this->chunks[3].swap_bytes(); } simdutf_really_inline uint64_t to_bitmask() const { - uint64_t r_lo = uint32_t(this->chunks[0].to_bitmask()); - uint64_t r_hi = this->chunks[1].to_bitmask(); - return r_lo | (r_hi << 32); + uint64_t r0 = uint32_t(this->chunks[0].to_bitmask()); + uint64_t r1 = this->chunks[1].to_bitmask(); + uint64_t r2 = this->chunks[2].to_bitmask(); + uint64_t r3 = this->chunks[3].to_bitmask(); + return r0 | (r1 << 16) | (r2 << 32) | (r3 << 48); + } + simdutf_really_inline uint64_t gteq(const T m) const { + const simd16 mask = simd16::splat(m); + return simd16x32(this->chunks[0] >= mask, this->chunks[1] >= mask, + this->chunks[2] >= mask, this->chunks[3] >= mask) + .to_bitmask(); } simdutf_really_inline uint64_t lteq(const T m) const { const simd16 mask = simd16::splat(m); - return simd16x32(this->chunks[0] <= mask, this->chunks[1] <= mask) + return simd16x32(this->chunks[0] <= mask, this->chunks[1] <= mask, + this->chunks[2] <= mask, this->chunks[3] <= mask) .to_bitmask(); } }; // struct simd16x32 -simdutf_really_inline simd16 min(const simd16 a, - const simd16 b) { - return __lasx_xvmin_hu(a.value, b.value); -} - -simdutf_really_inline simd16 operator==(const simd16 a, - uint16_t b) { - const auto bv = __lasx_xvreplgr2vr_h(b); - return __lasx_xvseq_h(a.value, bv); -} - -simdutf_really_inline simd16 as_vector_u16(const simd16 x) { - return x.value; -} - -simdutf_really_inline simd16 operator&(const simd16 a, +simdutf_really_inline simd16 operator^(const simd16 a, uint16_t b) { - const auto bv = __lasx_xvreplgr2vr_h(b); - return __lasx_xvand_v(a.value, bv); + const auto bv = __lsx_vreplgr2vr_h(b); + return __lsx_vxor_v(a.value, bv); } -simdutf_really_inline simd16 operator&(const simd16 a, - const simd16 b) { - return __lasx_xvand_v(a.value, b.value); +simdutf_really_inline simd16 operator^(const simd16 a, + const simd16 b) { + return __lsx_vxor_v(a.value, b.value); } -simdutf_really_inline simd16 operator^(const simd16 a, - uint16_t b) { - const auto bv = __lasx_xvreplgr2vr_h(b); - return __lasx_xvxor_v(a.value, bv); +simdutf_really_inline simd16 min(const simd16 a, + const simd16 b) { + return __lsx_vmin_hu(a.value, b.value); } -simdutf_really_inline simd16 operator^(const simd16 a, - const simd16 b) { - return __lasx_xvxor_v(a.value, b.value); +simdutf_really_inline simd16 as_vector_u16(const simd16 x) { + return x.value; } -/* end file src/simdutf/lasx/simd16-inl.h */ -/* begin file src/simdutf/lasx/simd32-inl.h */ +/* end file src/simdutf/lsx/simd16-inl.h */ +/* begin file src/simdutf/lsx/simd32-inl.h */ template struct simd32; template <> struct simd32 { - __m256i value; + __m128i value; static const int SIZE = sizeof(value); static const int ELEMENTS = SIZE / sizeof(uint32_t); // constructors - simdutf_really_inline simd32(__m256i v) : value(v) {} + simdutf_really_inline simd32(__m128i v) : value(v) {} template - simdutf_really_inline simd32(Ptr *ptr) : value(__lasx_xvld(ptr, 0)) {} + simdutf_really_inline simd32(Ptr *ptr) : value(__lsx_vld(ptr, 0)) {} // in-place operators simdutf_really_inline simd32 &operator-=(const simd32 other) { - value = __lasx_xvsub_w(value, other.value); + value = __lsx_vsub_w(value, other.value); return *this; } // members simdutf_really_inline uint64_t sum() const { - const auto odd = __lasx_xvsrli_d(value, 32); - const auto even = __lasx_xvand_v(value, __lasx_xvreplgr2vr_d(0xffffffff)); - - const auto sum64 = __lasx_xvadd_d(odd, even); - - return uint64_t(__lasx_xvpickve2gr_du(sum64, 0)) + - uint64_t(__lasx_xvpickve2gr_du(sum64, 1)) + - uint64_t(__lasx_xvpickve2gr_du(sum64, 2)) + - uint64_t(__lasx_xvpickve2gr_du(sum64, 3)); + return uint64_t(__lsx_vpickve2gr_wu(value, 0)) + + uint64_t(__lsx_vpickve2gr_wu(value, 1)) + + uint64_t(__lsx_vpickve2gr_wu(value, 2)) + + uint64_t(__lsx_vpickve2gr_wu(value, 3)); } // static members static simdutf_really_inline simd32 splat(uint32_t x) { - return __lasx_xvreplgr2vr_w(x); + return __lsx_vreplgr2vr_w(x); } static simdutf_really_inline simd32 zero() { - return __lasx_xvrepli_w(0); + return __lsx_vrepli_w(0); } }; // ------------------------------------------------------------ template <> struct simd32 { - __m256i value; + __m128i value; static const int SIZE = sizeof(value); // constructors - simdutf_really_inline simd32(__m256i v) : value(v) {} + simdutf_really_inline simd32(__m128i v) : value(v) {} }; // ------------------------------------------------------------ simdutf_really_inline simd32 operator&(const simd32 a, const simd32 b) { - return __lasx_xvor_v(a.value, b.value); + return __lsx_vor_v(a.value, b.value); } simdutf_really_inline simd32 operator<(const simd32 a, const simd32 b) { - return __lasx_xvslt_wu(a.value, b.value); + return __lsx_vslt_wu(a.value, b.value); } simdutf_really_inline simd32 operator>(const simd32 a, const simd32 b) { - return __lasx_xvslt_wu(b.value, a.value); + return __lsx_vslt_wu(b.value, a.value); } // ------------------------------------------------------------ @@ -10495,78 +9773,76 @@ simdutf_really_inline simd32 operator>(const simd32 a, simdutf_really_inline simd32 as_vector_u32(const simd32 v) { return v.value; } -/* end file src/simdutf/lasx/simd32-inl.h */ -/* begin file src/simdutf/lasx/simd64-inl.h */ +/* end file src/simdutf/lsx/simd32-inl.h */ +/* begin file src/simdutf/lsx/simd64-inl.h */ template struct simd64; template <> struct simd64 { - __m256i value; + __m128i value; static const int SIZE = sizeof(value); static const int ELEMENTS = SIZE / sizeof(uint64_t); // constructors - simdutf_really_inline simd64(__m256i v) : value(v) {} + simdutf_really_inline simd64(__m128i v) : value(v) {} template - simdutf_really_inline simd64(Ptr *ptr) : value(__lasx_xvld(ptr, 0)) {} + simdutf_really_inline simd64(Ptr *ptr) : value(__lsx_vld(ptr, 0)) {} // in-place operators simdutf_really_inline simd64 &operator+=(const simd64 other) { - value = __lasx_xvadd_d(value, other.value); + value = __lsx_vadd_d(value, other.value); return *this; } // members simdutf_really_inline uint64_t sum() const { - return uint64_t(__lasx_xvpickve2gr_du(value, 0)) + - uint64_t(__lasx_xvpickve2gr_du(value, 1)) + - uint64_t(__lasx_xvpickve2gr_du(value, 2)) + - uint64_t(__lasx_xvpickve2gr_du(value, 3)); + return uint64_t(__lsx_vpickve2gr_du(value, 0)) + + uint64_t(__lsx_vpickve2gr_du(value, 1)); } // static members static simdutf_really_inline simd64 zero() { - return __lasx_xvrepli_d(0); + return __lsx_vrepli_d(0); } }; // ------------------------------------------------------------ template <> struct simd64 { - __m256i value; + __m128i value; static const int SIZE = sizeof(value); // constructors - simdutf_really_inline simd64(__m256i v) : value(v) {} + simdutf_really_inline simd64(__m128i v) : value(v) {} }; // ------------------------------------------------------------ simd64 sum_8bytes(const simd8 v) { - const auto sum_u16 = __lasx_xvhaddw_hu_bu(v, v); - const auto sum_u32 = __lasx_xvhaddw_wu_hu(sum_u16, sum_u16); - const auto sum_u64 = __lasx_xvhaddw_du_wu(sum_u32, sum_u32); + const auto sum_u16 = __lsx_vhaddw_hu_bu(v, v); + const auto sum_u32 = __lsx_vhaddw_wu_hu(sum_u16, sum_u16); + const auto sum_u64 = __lsx_vhaddw_du_wu(sum_u32, sum_u32); return simd64(sum_u64); } -/* end file src/simdutf/lasx/simd64-inl.h */ +/* end file src/simdutf/lsx/simd64-inl.h */ } // namespace simd } // unnamed namespace -} // namespace lasx +} // namespace lsx } // namespace simdutf -#endif // SIMDUTF_LASX_SIMD_H -/* end file src/simdutf/lasx/simd.h */ +#endif // SIMDUTF_LSX_SIMD_H +/* end file src/simdutf/lsx/simd.h */ -/* begin file src/simdutf/lasx/end.h */ +/* begin file src/simdutf/lsx/end.h */ #undef SIMDUTF_SIMD_HAS_UNSIGNED_CMP -/* end file src/simdutf/lasx/end.h */ +/* end file src/simdutf/lsx/end.h */ -#endif // SIMDUTF_IMPLEMENTATION_LASX +#endif // SIMDUTF_IMPLEMENTATION_LSX -#endif // SIMDUTF_LASX_H -/* end file src/simdutf/lasx.h */ +#endif // SIMDUTF_LSX_H +/* end file src/simdutf/lsx.h */ /* begin file src/simdutf/fallback.h */ #ifndef SIMDUTF_FALLBACK_H #define SIMDUTF_FALLBACK_H @@ -10619,890 +9895,123 @@ class implementation final : public simdutf::implementation { simdutf_warn_unused result base64_to_binary( const char *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; + last_chunk_handling_options::loose) const noexcept override; simdutf_warn_unused full_result base64_to_binary_details( const char *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; - simdutf_warn_unused result - base64_to_binary(const char16_t *input, size_t length, char *output, - base64_options options, - last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; - simdutf_warn_unused full_result base64_to_binary_details( - const char16_t *input, size_t length, char *output, - base64_options options, - last_chunk_handling_options last_chunk_options = - last_chunk_handling_options::loose) const noexcept; - size_t binary_to_base64(const char *input, size_t length, char *output, - base64_options options) const noexcept; - const char *find(const char *start, const char *end, - char character) const noexcept; - const char16_t *find(const char16_t *start, const char16_t *end, - char16_t character) const noexcept; - -}; -} // namespace fallback -} // namespace simdutf - -#endif // SIMDUTF_FALLBACK_IMPLEMENTATION_H -/* end file src/simdutf/fallback/implementation.h */ - -/* begin file src/simdutf/fallback/begin.h */ -// redefining SIMDUTF_IMPLEMENTATION to "fallback" -// #define SIMDUTF_IMPLEMENTATION fallback -/* end file src/simdutf/fallback/begin.h */ - - // Declarations -/* begin file src/simdutf/fallback/bitmanipulation.h */ -#ifndef SIMDUTF_FALLBACK_BITMANIPULATION_H -#define SIMDUTF_FALLBACK_BITMANIPULATION_H - -#include - -namespace simdutf { -namespace fallback { -namespace {} // unnamed namespace -} // namespace fallback -} // namespace simdutf - -#endif // SIMDUTF_FALLBACK_BITMANIPULATION_H -/* end file src/simdutf/fallback/bitmanipulation.h */ - -/* begin file src/simdutf/fallback/end.h */ -/* end file src/simdutf/fallback/end.h */ - -#endif // SIMDUTF_IMPLEMENTATION_FALLBACK -#endif // SIMDUTF_FALLBACK_H -/* end file src/simdutf/fallback.h */ -#ifndef SIMDUTF_REGULAR_VISUAL_STUDIO -SIMDUTF_POP_DISABLE_WARNINGS -#endif - -// The scalar routines should be included once. -/* begin file src/scalar/swap_bytes.h */ -#ifndef SIMDUTF_SWAP_BYTES_H -#define SIMDUTF_SWAP_BYTES_H - -namespace simdutf { -namespace scalar { - -inline simdutf_warn_unused uint16_t u16_swap_bytes(const uint16_t word) { - return uint16_t((word >> 8) | (word << 8)); -} - -inline simdutf_warn_unused uint32_t u32_swap_bytes(const uint32_t word) { - return ((word >> 24) & 0xff) | // move byte 3 to byte 0 - ((word << 8) & 0xff0000) | // move byte 1 to byte 2 - ((word >> 8) & 0xff00) | // move byte 2 to byte 1 - ((word << 24) & 0xff000000); // byte 0 to byte 3 -} - -} // namespace scalar -} // namespace simdutf - -#endif -/* end file src/scalar/swap_bytes.h */ -/* begin file src/scalar/base64.h */ -#ifndef SIMDUTF_BASE64_H -#define SIMDUTF_BASE64_H - -#include -#include -#include -#include -#include - -namespace simdutf { -namespace scalar { -namespace { -namespace base64 { - -// This function is not expected to be fast. Do not use in long loops. -// In most instances you should be using is_ignorable. -template bool is_ascii_white_space(char_type c) { - return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f'; -} - -template bool is_eight_byte(char_type c) { - if (sizeof(char_type) == 1) { - return true; - } - return uint8_t(c) == c; -} - -template -bool is_ignorable(char_type c, simdutf::base64_options options) { - const uint8_t *to_base64 = - (options & base64_default_or_url) - ? tables::base64::to_base64_default_or_url_value - : ((options & base64_url) ? tables::base64::to_base64_url_value - : tables::base64::to_base64_value); - const bool ignore_garbage = - (options == base64_options::base64_url_accept_garbage) || - (options == base64_options::base64_default_accept_garbage) || - (options == base64_options::base64_default_or_url_accept_garbage); - uint8_t code = to_base64[uint8_t(c)]; - if (is_eight_byte(c) && code <= 63) { - return false; - } - if (is_eight_byte(c) && code == 64) { - return true; - } - return ignore_garbage; -} -template -bool is_base64(char_type c, simdutf::base64_options options) { - const uint8_t *to_base64 = - (options & base64_default_or_url) - ? tables::base64::to_base64_default_or_url_value - : ((options & base64_url) ? tables::base64::to_base64_url_value - : tables::base64::to_base64_value); - uint8_t code = to_base64[uint8_t(c)]; - if (is_eight_byte(c) && code <= 63) { - return true; - } - return false; -} - -template -bool is_base64_or_padding(char_type c, simdutf::base64_options options) { - const uint8_t *to_base64 = - (options & base64_default_or_url) - ? tables::base64::to_base64_default_or_url_value - : ((options & base64_url) ? tables::base64::to_base64_url_value - : tables::base64::to_base64_value); - if (c == '=') { - return true; - } - uint8_t code = to_base64[uint8_t(c)]; - if (is_eight_byte(c) && code <= 63) { - return true; - } - return false; -} - -template -bool is_ignorable_or_padding(char_type c, simdutf::base64_options options) { - return is_ignorable(c, options) || c == '='; -} - -struct reduced_input { - size_t equalsigns; // number of padding characters '=', typically 0, 1, 2. - size_t equallocation; // location of the first padding character if any - size_t srclen; // length of the input buffer before padding - size_t full_input_length; // length of the input buffer with padding but - // without ignorable characters -}; - -// find the end of the base64 input buffer -// It returns the number of padding characters, the location of the first -// padding character if any, the length of the input buffer before padding -// and the length of the input buffer with padding. The input buffer is not -// modified. The function assumes that there are at most two padding characters. -template -reduced_input find_end(const char_type *src, size_t srclen, - simdutf::base64_options options) { - const uint8_t *to_base64 = - (options & base64_default_or_url) - ? tables::base64::to_base64_default_or_url_value - : ((options & base64_url) ? tables::base64::to_base64_url_value - : tables::base64::to_base64_value); - const bool ignore_garbage = - (options == base64_options::base64_url_accept_garbage) || - (options == base64_options::base64_default_accept_garbage) || - (options == base64_options::base64_default_or_url_accept_garbage); - - size_t equalsigns = 0; - // We intentionally include trailing spaces in the full input length. - // See https://github.com/simdutf/simdutf/issues/824 - size_t full_input_length = srclen; - // skip trailing spaces - while (!ignore_garbage && srclen > 0 && - scalar::base64::is_eight_byte(src[srclen - 1]) && - to_base64[uint8_t(src[srclen - 1])] == 64) { - srclen--; - } - size_t equallocation = - srclen; // location of the first padding character if any - if (ignore_garbage) { - // Technically, we don't need to find the first padding character, we can - // just change our algorithms, but it adds substantial complexity. - auto it = simdutf::find(src, src + srclen, '='); - if (it != src + srclen) { - equallocation = it - src; - equalsigns = 1; - srclen = equallocation; - full_input_length = equallocation + 1; - } - return {equalsigns, equallocation, srclen, full_input_length}; - } - if (!ignore_garbage && srclen > 0 && src[srclen - 1] == '=') { - // This is the last '=' sign. - equallocation = srclen - 1; - srclen--; - equalsigns = 1; - // skip trailing spaces - while (srclen > 0 && scalar::base64::is_eight_byte(src[srclen - 1]) && - to_base64[uint8_t(src[srclen - 1])] == 64) { - srclen--; - } - if (srclen > 0 && src[srclen - 1] == '=') { - // This is the second '=' sign. - equallocation = srclen - 1; - srclen--; - equalsigns = 2; - } - } - return {equalsigns, equallocation, srclen, full_input_length}; -} - -// Returns true upon success. The destination buffer must be large enough. -// This functions assumes that the padding (=) has been removed. -// if check_capacity is true, it will check that the destination buffer is -// large enough. If it is not, it will return OUTPUT_BUFFER_TOO_SMALL. -template -full_result base64_tail_decode_impl( - char *dst, size_t outlen, const char_type *src, size_t length, - size_t padding_characters, // number of padding characters - // '=', typically 0, 1, 2. - base64_options options, last_chunk_handling_options last_chunk_options) { - char *dstend = dst + outlen; - (void)dstend; - // This looks like 10 branches, but we expect the compiler to resolve this to - // two branches (easily predicted): - const uint8_t *to_base64 = - (options & base64_default_or_url) - ? tables::base64::to_base64_default_or_url_value - : ((options & base64_url) ? tables::base64::to_base64_url_value - : tables::base64::to_base64_value); - const uint32_t *d0 = - (options & base64_default_or_url) - ? tables::base64::base64_default_or_url::d0 - : ((options & base64_url) ? tables::base64::base64_url::d0 - : tables::base64::base64_default::d0); - const uint32_t *d1 = - (options & base64_default_or_url) - ? tables::base64::base64_default_or_url::d1 - : ((options & base64_url) ? tables::base64::base64_url::d1 - : tables::base64::base64_default::d1); - const uint32_t *d2 = - (options & base64_default_or_url) - ? tables::base64::base64_default_or_url::d2 - : ((options & base64_url) ? tables::base64::base64_url::d2 - : tables::base64::base64_default::d2); - const uint32_t *d3 = - (options & base64_default_or_url) - ? tables::base64::base64_default_or_url::d3 - : ((options & base64_url) ? tables::base64::base64_url::d3 - : tables::base64::base64_default::d3); - const bool ignore_garbage = - (options == base64_options::base64_url_accept_garbage) || - (options == base64_options::base64_default_accept_garbage) || - (options == base64_options::base64_default_or_url_accept_garbage); - - const char_type *srcend = src + length; - const char_type *srcinit = src; - const char *dstinit = dst; - - uint32_t x; - size_t idx; - uint8_t buffer[4]; - while (true) { - while (src + 4 <= srcend && is_eight_byte(src[0]) && - is_eight_byte(src[1]) && is_eight_byte(src[2]) && - is_eight_byte(src[3]) && - (x = d0[uint8_t(src[0])] | d1[uint8_t(src[1])] | - d2[uint8_t(src[2])] | d3[uint8_t(src[3])]) < 0x01FFFFFF) { - if (match_system(endianness::BIG)) { - x = scalar::u32_swap_bytes(x); - } - if (check_capacity && dstend - dst < 3) { - return {OUTPUT_BUFFER_TOO_SMALL, size_t(src - srcinit), - size_t(dst - dstinit)}; - } - std::memcpy(dst, &x, 3); // optimization opportunity: copy 4 bytes - dst += 3; - src += 4; - } - const char_type *srccur = src; - idx = 0; - // we need at least four characters. -#ifdef __clang__ - // If possible, we read four characters at a time. (It is an optimization.) - if (ignore_garbage && src + 4 <= srcend) { - char_type c0 = src[0]; - char_type c1 = src[1]; - char_type c2 = src[2]; - char_type c3 = src[3]; - - uint8_t code0 = to_base64[uint8_t(c0)]; - uint8_t code1 = to_base64[uint8_t(c1)]; - uint8_t code2 = to_base64[uint8_t(c2)]; - uint8_t code3 = to_base64[uint8_t(c3)]; - - buffer[idx] = code0; - idx += (is_eight_byte(c0) && code0 <= 63); - buffer[idx] = code1; - idx += (is_eight_byte(c1) && code1 <= 63); - buffer[idx] = code2; - idx += (is_eight_byte(c2) && code2 <= 63); - buffer[idx] = code3; - idx += (is_eight_byte(c3) && code3 <= 63); - src += 4; - } -#endif - while ((idx < 4) && (src < srcend)) { - char_type c = *src; - - uint8_t code = to_base64[uint8_t(c)]; - buffer[idx] = uint8_t(code); - if (is_eight_byte(c) && code <= 63) { - idx++; - } else if (!ignore_garbage && - (code > 64 || !scalar::base64::is_eight_byte(c))) { - return {INVALID_BASE64_CHARACTER, size_t(src - srcinit), - size_t(dst - dstinit)}; - } else { - // We have a space or a newline or garbage. We ignore it. - } - src++; - } - if (idx != 4) { - simdutf_log_assert(idx < 4, "idx should be less than 4"); - // We never should have that the number of base64 characters + the - // number of padding characters is more than 4. - if (!ignore_garbage && (idx + padding_characters > 4)) { - return {INVALID_BASE64_CHARACTER, size_t(src - srcinit), - size_t(dst - dstinit), true}; - } - - // The idea here is that in loose mode, - // if there is padding at all, it must be used - // to form 4-wise chunk. However, in loose mode, - // we do accept no padding at all. - if (!ignore_garbage && - last_chunk_options == last_chunk_handling_options::loose && - (idx >= 2) && padding_characters > 0 && - ((idx + padding_characters) & 3) != 0) { - return {INVALID_BASE64_CHARACTER, size_t(src - srcinit), - size_t(dst - dstinit), true}; - } else - - // The idea here is that in strict mode, we do not want to accept - // incomplete base64 chunks. So if the chunk was otherwise valid, we - // return BASE64_INPUT_REMAINDER. - if (!ignore_garbage && - last_chunk_options == last_chunk_handling_options::strict && - (idx >= 2) && ((idx + padding_characters) & 3) != 0) { - // The partial chunk was at src - idx - return {BASE64_INPUT_REMAINDER, size_t(src - srcinit), - size_t(dst - dstinit), true}; - } else - // If there is a partial chunk with insufficient padding, with - // stop_before_partial, we need to just ignore it. In "only full" - // mode, skip the minute there are padding characters. - if ((last_chunk_options == - last_chunk_handling_options::stop_before_partial && - (padding_characters + idx < 4) && (idx != 0) && - (idx >= 2 || padding_characters == 0)) || - (last_chunk_options == - last_chunk_handling_options::only_full_chunks && - (idx >= 2 || padding_characters == 0))) { - // partial means that we are *not* going to consume the read - // characters. We need to rewind the src pointer. - src = srccur; - return {SUCCESS, size_t(src - srcinit), size_t(dst - dstinit)}; - } else { - if (idx == 2) { - uint32_t triple = (uint32_t(buffer[0]) << 3 * 6) + - (uint32_t(buffer[1]) << 2 * 6); - if (!ignore_garbage && - (last_chunk_options == last_chunk_handling_options::strict) && - (triple & 0xffff)) { - return {BASE64_EXTRA_BITS, size_t(src - srcinit), - size_t(dst - dstinit)}; - } - if (check_capacity && dstend - dst < 1) { - return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit), - size_t(dst - dstinit)}; - } - if (match_system(endianness::BIG)) { - triple <<= 8; - std::memcpy(dst, &triple, 1); - } else { - triple = scalar::u32_swap_bytes(triple); - triple >>= 8; - std::memcpy(dst, &triple, 1); - } - dst += 1; - } else if (idx == 3) { - uint32_t triple = (uint32_t(buffer[0]) << 3 * 6) + - (uint32_t(buffer[1]) << 2 * 6) + - (uint32_t(buffer[2]) << 1 * 6); - if (!ignore_garbage && - (last_chunk_options == last_chunk_handling_options::strict) && - (triple & 0xff)) { - return {BASE64_EXTRA_BITS, size_t(src - srcinit), - size_t(dst - dstinit)}; - } - if (check_capacity && dstend - dst < 2) { - return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit), - size_t(dst - dstinit)}; - } - if (match_system(endianness::BIG)) { - triple <<= 8; - std::memcpy(dst, &triple, 2); - } else { - triple = scalar::u32_swap_bytes(triple); - triple >>= 8; - std::memcpy(dst, &triple, 2); - } - dst += 2; - } else if (!ignore_garbage && idx == 1 && - (!is_partial(last_chunk_options) || - (is_partial(last_chunk_options) && - padding_characters > 0))) { - return {BASE64_INPUT_REMAINDER, size_t(src - srcinit), - size_t(dst - dstinit)}; - } else if (!ignore_garbage && idx == 0 && padding_characters > 0) { - return {INVALID_BASE64_CHARACTER, size_t(src - srcinit), - size_t(dst - dstinit), true}; - } - return {SUCCESS, size_t(src - srcinit), size_t(dst - dstinit)}; - } - } - if (check_capacity && dstend - dst < 3) { - return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit), - size_t(dst - dstinit)}; - } - uint32_t triple = - (uint32_t(buffer[0]) << 3 * 6) + (uint32_t(buffer[1]) << 2 * 6) + - (uint32_t(buffer[2]) << 1 * 6) + (uint32_t(buffer[3]) << 0 * 6); - if (match_system(endianness::BIG)) { - triple <<= 8; - std::memcpy(dst, &triple, 3); - } else { - triple = scalar::u32_swap_bytes(triple); - triple >>= 8; - std::memcpy(dst, &triple, 3); - } - dst += 3; - } -} - -template -full_result -base64_tail_decode(char *dst, const char_type *src, size_t length, - size_t padding_characters, // number of padding characters - // '=', typically 0, 1, 2. - base64_options options, - last_chunk_handling_options last_chunk_options) { - return base64_tail_decode_impl(dst, 0, src, length, padding_characters, - options, last_chunk_options); -} - -// like base64_tail_decode, but it will not write past the end of the output -// buffer. The outlen parameter is modified to reflect the number of bytes -// written. This functions assumes that the padding (=) has been removed. -// -template -full_result base64_tail_decode_safe( - char *dst, size_t outlen, const char_type *src, size_t length, - size_t padding_characters, // number of padding characters - // '=', typically 0, 1, 2. - base64_options options, last_chunk_handling_options last_chunk_options) { - return base64_tail_decode_impl(dst, outlen, src, length, - padding_characters, options, - last_chunk_options); -} - -inline full_result -patch_tail_result(full_result r, size_t previous_input, size_t previous_output, - size_t equallocation, size_t full_input_length, - last_chunk_handling_options last_chunk_options) { - r.input_count += previous_input; - r.output_count += previous_output; - if (r.padding_error) { - r.input_count = equallocation; - } - - if (r.error == error_code::SUCCESS) { - if (!is_partial(last_chunk_options)) { - // A success when we are not in stop_before_partial mode. - // means that we have consumed the whole input buffer. - r.input_count = full_input_length; - } else if (r.output_count % 3 != 0) { - r.input_count = full_input_length; - } - } - return r; -} - -// Returns the number of bytes written. The destination buffer must be large -// enough. It will add padding (=) if needed. -size_t tail_encode_base64(char *dst, const char *src, size_t srclen, - base64_options options) { - // By default, we use padding if we are not using the URL variant. - // This is check with ((options & base64_url) == 0) which returns true if we - // are not using the URL variant. However, we also allow 'inversion' of the - // convention with the base64_reverse_padding option. If the - // base64_reverse_padding option is set, we use padding if we are using the - // URL variant, and we omit it if we are not using the URL variant. This is - // checked with - // ((options & base64_reverse_padding) == base64_reverse_padding). - bool use_padding = - ((options & base64_url) == 0) ^ - ((options & base64_reverse_padding) == base64_reverse_padding); - // This looks like 3 branches, but we expect the compiler to resolve this to - // a single branch: - const char *e0 = (options & base64_url) ? tables::base64::base64_url::e0 - : tables::base64::base64_default::e0; - const char *e1 = (options & base64_url) ? tables::base64::base64_url::e1 - : tables::base64::base64_default::e1; - const char *e2 = (options & base64_url) ? tables::base64::base64_url::e2 - : tables::base64::base64_default::e2; - char *out = dst; - size_t i = 0; - uint8_t t1, t2, t3; - for (; i + 2 < srclen; i += 3) { - t1 = uint8_t(src[i]); - t2 = uint8_t(src[i + 1]); - t3 = uint8_t(src[i + 2]); - *out++ = e0[t1]; - *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; - *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)]; - *out++ = e2[t3]; - } - switch (srclen - i) { - case 0: - break; - case 1: - t1 = uint8_t(src[i]); - *out++ = e0[t1]; - *out++ = e1[(t1 & 0x03) << 4]; - if (use_padding) { - *out++ = '='; - *out++ = '='; - } - break; - default: /* case 2 */ - t1 = uint8_t(src[i]); - t2 = uint8_t(src[i + 1]); - *out++ = e0[t1]; - *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; - *out++ = e2[(t2 & 0x0F) << 2]; - if (use_padding) { - *out++ = '='; - } - } - return (size_t)(out - dst); -} - -template -simdutf_warn_unused size_t maximal_binary_length_from_base64( - const char_type *input, size_t length) noexcept { - // We follow https://infra.spec.whatwg.org/#forgiving-base64-decode - size_t padding = 0; - if (length > 0) { - if (input[length - 1] == '=') { - padding++; - if (length > 1 && input[length - 2] == '=') { - padding++; - } - } - } - size_t actual_length = length - padding; - if (actual_length % 4 <= 1) { - return actual_length / 4 * 3; - } - // if we have a valid input, then the remainder must be 2 or 3 adding one or - // two extra bytes. - return actual_length / 4 * 3 + (actual_length % 4) - 1; -} - -template -simdutf_warn_unused full_result base64_to_binary_details_impl( - const char_type *input, size_t length, char *output, base64_options options, - last_chunk_handling_options last_chunk_options) noexcept { - const bool ignore_garbage = - (options == base64_options::base64_url_accept_garbage) || - (options == base64_options::base64_default_accept_garbage) || - (options == base64_options::base64_default_or_url_accept_garbage); - auto ri = simdutf::scalar::base64::find_end(input, length, options); - size_t equallocation = ri.equallocation; - size_t equalsigns = ri.equalsigns; - length = ri.srclen; - size_t full_input_length = ri.full_input_length; - if (length == 0) { - if (!ignore_garbage && equalsigns > 0) { - return {INVALID_BASE64_CHARACTER, equallocation, 0}; - } - return {SUCCESS, full_input_length, 0}; - } - full_result r = scalar::base64::base64_tail_decode( - output, input, length, equalsigns, options, last_chunk_options); - r = scalar::base64::patch_tail_result(r, 0, 0, equallocation, - full_input_length, last_chunk_options); - if (!is_partial(last_chunk_options) && r.error == error_code::SUCCESS && - equalsigns > 0 && !ignore_garbage) { - // additional checks - if ((r.output_count % 3 == 0) || - ((r.output_count % 3) + 1 + equalsigns != 4)) { - return {INVALID_BASE64_CHARACTER, equallocation, r.output_count}; - } - } - // When is_partial(last_chunk_options) is true, we must either end with - // the end of the stream (beyond whitespace) or right after a non-ignorable - // character or at the very beginning of the stream. - // See https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64 - if (is_partial(last_chunk_options) && r.error == error_code::SUCCESS && - r.input_count < full_input_length) { - // First check if we can extend the input to the end of the stream - while (r.input_count < full_input_length && - base64_ignorable(*(input + r.input_count), options)) { - r.input_count++; - } - // If we are still not at the end of the stream, then we must backtrack - // to the last non-ignorable character. - if (r.input_count < full_input_length) { - while (r.input_count > 0 && - base64_ignorable(*(input + r.input_count - 1), options)) { - r.input_count--; - } - } - } - return r; -} + last_chunk_handling_options::loose) const noexcept override; + simdutf_warn_unused result base64_to_binary( + const char16_t *input, size_t length, char *output, + base64_options options, + last_chunk_handling_options last_chunk_options = + last_chunk_handling_options::loose) const noexcept override; + simdutf_warn_unused full_result base64_to_binary_details( + const char16_t *input, size_t length, char *output, + base64_options options, + last_chunk_handling_options last_chunk_options = + last_chunk_handling_options::loose) const noexcept override; + size_t binary_to_base64(const char *input, size_t length, char *output, + base64_options options) const noexcept override; + size_t + binary_to_base64_with_lines(const char *input, size_t length, char *output, + size_t line_length, + base64_options options) const noexcept override; + const char *find(const char *start, const char *end, + char character) const noexcept override; + const char16_t *find(const char16_t *start, const char16_t *end, + char16_t character) const noexcept override; -template -simdutf_warn_unused full_result base64_to_binary_details_safe_impl( - const char_type *input, size_t length, char *output, size_t outlen, - base64_options options, - last_chunk_handling_options last_chunk_options) noexcept { - const bool ignore_garbage = - (options == base64_options::base64_url_accept_garbage) || - (options == base64_options::base64_default_accept_garbage) || - (options == base64_options::base64_default_or_url_accept_garbage); - auto ri = simdutf::scalar::base64::find_end(input, length, options); - size_t equallocation = ri.equallocation; - size_t equalsigns = ri.equalsigns; - length = ri.srclen; - size_t full_input_length = ri.full_input_length; - if (length == 0) { - if (!ignore_garbage && equalsigns > 0) { - return {INVALID_BASE64_CHARACTER, equallocation, 0}; - } - return {SUCCESS, full_input_length, 0}; - } - full_result r = scalar::base64::base64_tail_decode_safe( - output, outlen, input, length, equalsigns, options, last_chunk_options); - r = scalar::base64::patch_tail_result(r, 0, 0, equallocation, - full_input_length, last_chunk_options); - if (!is_partial(last_chunk_options) && r.error == error_code::SUCCESS && - equalsigns > 0 && !ignore_garbage) { - // additional checks - if ((r.output_count % 3 == 0) || - ((r.output_count % 3) + 1 + equalsigns != 4)) { - return {INVALID_BASE64_CHARACTER, equallocation, r.output_count}; - } - } +}; +} // namespace fallback +} // namespace simdutf - // When is_partial(last_chunk_options) is true, we must either end with - // the end of the stream (beyond whitespace) or right after a non-ignorable - // character or at the very beginning of the stream. - // See https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64 - if (is_partial(last_chunk_options) && r.error == error_code::SUCCESS && - r.input_count < full_input_length) { - // First check if we can extend the input to the end of the stream - while (r.input_count < full_input_length && - base64_ignorable(*(input + r.input_count), options)) { - r.input_count++; - } - // If we are still not at the end of the stream, then we must backtrack - // to the last non-ignorable character. - if (r.input_count < full_input_length) { - while (r.input_count > 0 && - base64_ignorable(*(input + r.input_count - 1), options)) { - r.input_count--; - } - } - } - return r; -} +#endif // SIMDUTF_FALLBACK_IMPLEMENTATION_H +/* end file src/simdutf/fallback/implementation.h */ -simdutf_warn_unused size_t -base64_length_from_binary(size_t length, base64_options options) noexcept { - // By default, we use padding if we are not using the URL variant. - // This is check with ((options & base64_url) == 0) which returns true if we - // are not using the URL variant. However, we also allow 'inversion' of the - // convention with the base64_reverse_padding option. If the - // base64_reverse_padding option is set, we use padding if we are using the - // URL variant, and we omit it if we are not using the URL variant. This is - // checked with - // ((options & base64_reverse_padding) == base64_reverse_padding). - bool use_padding = - ((options & base64_url) == 0) ^ - ((options & base64_reverse_padding) == base64_reverse_padding); - if (!use_padding) { - return length / 3 * 4 + ((length % 3) ? (length % 3) + 1 : 0); - } - return (length + 2) / 3 * - 4; // We use padding to make the length a multiple of 4. -} - -// Return the length of the prefix that contains count base64 characters. -// Thus, if count is 3, the function returns the length of the prefix -// that contains 3 base64 characters. -// The function returns (size_t)-1 if there is not enough base64 characters in -// the input. -template -simdutf_warn_unused size_t prefix_length(size_t count, - simdutf::base64_options options, - const char_type *input, - size_t length) noexcept { - size_t i = 0; - while (i < length && is_ignorable(input[i], options)) { - i++; - } - if (count == 0) { - return i; // duh! - } - for (; i < length; i++) { - if (is_ignorable(input[i], options)) { - continue; - } - // We have a base64 character or a padding character. - count--; - if (count == 0) { - return i + 1; - } - } - simdutf_log_assert(false, "You never get here"); +/* begin file src/simdutf/fallback/begin.h */ +// redefining SIMDUTF_IMPLEMENTATION to "fallback" +// #define SIMDUTF_IMPLEMENTATION fallback +/* end file src/simdutf/fallback/begin.h */ - return -1; // should never happen -} + // Declarations +/* begin file src/simdutf/fallback/bitmanipulation.h */ +#ifndef SIMDUTF_FALLBACK_BITMANIPULATION_H +#define SIMDUTF_FALLBACK_BITMANIPULATION_H -} // namespace base64 -} // unnamed namespace -} // namespace scalar +#include + +namespace simdutf { +namespace fallback { +namespace {} // unnamed namespace +} // namespace fallback } // namespace simdutf +#endif // SIMDUTF_FALLBACK_BITMANIPULATION_H +/* end file src/simdutf/fallback/bitmanipulation.h */ + +/* begin file src/simdutf/fallback/end.h */ +/* end file src/simdutf/fallback/end.h */ + +#endif // SIMDUTF_IMPLEMENTATION_FALLBACK +#endif // SIMDUTF_FALLBACK_H +/* end file src/simdutf/fallback.h */ +#ifndef SIMDUTF_REGULAR_VISUAL_STUDIO +SIMDUTF_POP_DISABLE_WARNINGS #endif -/* end file src/scalar/base64.h */ + +// The scalar routines should be included once. /* begin file src/implementation.cpp */ -#include +#include #include #include +#include #if SIMDUTF_ATOMIC_REF -/* begin file src/scalar/atomic_util.h */ -#ifndef SIMDUTF_ATOMIC_UTIL_H -#define SIMDUTF_ATOMIC_UTIL_H -#if SIMDUTF_ATOMIC_REF - #include -namespace simdutf { -namespace scalar { - -// This function is a memcpy that uses atomic operations to read from the -// source. -inline void memcpy_atomic_read(char *dst, const char *src, size_t len) { - static_assert(std::atomic_ref::required_alignment == sizeof(char), - "std::atomic_ref requires the same alignment as char_type"); - // We expect all 64-bit systems to be able to read 64-bit words from an - // aligned memory region atomically. You might be able to do better on - // specific systems, e.g., x64 systems can read 128-bit words atomically. - constexpr size_t alignment = sizeof(uint64_t); - - // Lambda for atomic byte-by-byte copy - auto bbb_memcpy_atomic_read = [](char *bytedst, const char *bytesrc, - size_t bytelen) noexcept { - char *mutable_src = const_cast(bytesrc); - for (size_t j = 0; j < bytelen; ++j) { - bytedst[j] = - std::atomic_ref(mutable_src[j]).load(std::memory_order_relaxed); - } - }; - - // Handle unaligned start - size_t offset = reinterpret_cast(src) % alignment; - if (offset) { - size_t to_align = std::min(len, alignment - offset); - bbb_memcpy_atomic_read(dst, src, to_align); - src += to_align; - dst += to_align; - len -= to_align; - } - - // Process aligned 64-bit chunks - while (len >= alignment) { - auto *src_aligned = reinterpret_cast(const_cast(src)); - const auto dst_value = - std::atomic_ref(*src_aligned).load(std::memory_order_relaxed); - std::memcpy(dst, &dst_value, sizeof(uint64_t)); - src += alignment; - dst += alignment; - len -= alignment; - } - - // Handle remaining bytes - if (len) { - bbb_memcpy_atomic_read(dst, src, len); - } -} - -// This function is a memcpy that uses atomic operations to write to the -// destination. -inline void memcpy_atomic_write(char *dst, const char *src, size_t len) { - static_assert(std::atomic_ref::required_alignment == sizeof(char), - "std::atomic_ref requires the same alignment as char"); - // We expect all 64-bit systems to be able to write 64-bit words to an aligned - // memory region atomically. - // You might be able to do better on specific systems, e.g., x64 systems can - // write 128-bit words atomically. - constexpr size_t alignment = sizeof(uint64_t); - - // Lambda for atomic byte-by-byte write - auto bbb_memcpy_atomic_write = [](char *bytedst, const char *bytesrc, - size_t bytelen) noexcept { - for (size_t j = 0; j < bytelen; ++j) { - std::atomic_ref(bytedst[j]) - .store(bytesrc[j], std::memory_order_relaxed); - } - }; - - // Handle unaligned start - size_t offset = reinterpret_cast(dst) % alignment; - if (offset) { - size_t to_align = std::min(len, alignment - offset); - bbb_memcpy_atomic_write(dst, src, to_align); - dst += to_align; - src += to_align; - len -= to_align; - } - - // Process aligned 64-bit chunks - while (len >= alignment) { - auto *dst_aligned = reinterpret_cast(dst); - uint64_t src_val; - std::memcpy(&src_val, src, sizeof(uint64_t)); // Non-atomic read from src - std::atomic_ref(*dst_aligned) - .store(src_val, std::memory_order_relaxed); - dst += alignment; - src += alignment; - len -= alignment; - } + #include +#endif - // Handle remaining bytes - if (len) { - bbb_memcpy_atomic_write(dst, src, len); - } -} -} // namespace scalar -} // namespace simdutf -#endif // SIMDUTF_ATOMIC_REF -#endif // SIMDUTF_ATOMIC_UTIL_H -/* end file src/scalar/atomic_util.h */ +// The macro SIMDUTF_USE_STATIC_INITIALIZATION, when set to 1, means that we +// will use translation-unit-scope variables to hold our implementations. +// +// The downside of a translation-unit-scope variable is that the initialization +// order is not well defined, thus if someone uses simdutf before main() starts, +// they might get a crash. Thus setting SIMDUTF_USE_STATIC_INITIALIZATION to 1 +// is not recommended if you are using simdutf in a library that might be used +// by other code before main() starts. However, the upside is that there is no +// synchronization overhead on every call to get_active_implementation(). When +// compiling without the c++ standard library, we use static initialization, +// because C++ relies on the standard library for thread-safe initialization of +// function-scope static variables. +// +// By default, we avoid translation-unit-scope static initialization, so we set +// SIMDUTF_USE_STATIC_INITIALIZATION to 0. It comes with a small performance +// cost on the first call to get_active_implementation(), and a smaller cost on +// subsequent calls but it is then safe to use the simdutf library in static +// initialization. +// +// Further reading: https://en.cppreference.com/cpp/language/siof +#ifndef SIMDUTF_USE_STATIC_INITIALIZATION + #if SIMDUTF_NO_LIBCXX + #define SIMDUTF_USE_STATIC_INITIALIZATION 1 + #else // SIMDUTF_NO_LIBCXX + #define SIMDUTF_USE_STATIC_INITIALIZATION 0 + #endif // SIMDUTF_NO_LIBCXX +#endif // SIMDUTF_USE_STATIC_INITIALIZATION + +// When building without libc++abi (SIMDUTF_NO_LIBCXX=1) on GCC/Clang, provide +// a weak stub for __cxa_pure_virtual so the abstract implementation vtable +// does not drag in libc++abi just for this unreachable hook. Kept weak so a +// real libc++abi definition wins if one is linked in anyway. +#if SIMDUTF_NO_LIBCXX +extern "C" __attribute__((weak, noreturn)) void __cxa_pure_virtual() { + __builtin_trap(); +} +namespace std { +__attribute__((weak, noreturn)) void +__glibcxx_assert_fail(const char *, int, const char *, const char *) noexcept { + __builtin_trap(); +} +} // namespace std #endif static_assert(sizeof(uint8_t) == sizeof(char), @@ -11514,22 +10023,6 @@ static_assert(sizeof(uint32_t) == sizeof(char32_t), // next line is redundant, but it is kept to catch defective systems. static_assert(CHAR_BIT == 8, "simdutf requires 8-bit bytes"); -// Useful for debugging purposes -namespace simdutf { -namespace { - -template std::string toBinaryString(T b) { - std::string binary = ""; - T mask = T(1) << (sizeof(T) * CHAR_BIT - 1); - while (mask > 0) { - binary += ((b & mask) == 0) ? '0' : '1'; - mask >>= 1; - } - return binary; -} -} // namespace -} // namespace simdutf - namespace simdutf { bool implementation::supported_by_runtime_system() const { uint32_t required_instruction_sets = this->required_instruction_sets(); @@ -11549,6 +10042,16 @@ simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64( return scalar::base64::maximal_binary_length_from_base64(input, length); } +simdutf_warn_unused size_t implementation::binary_length_from_base64( + const char *input, size_t length) const noexcept { + return scalar::base64::binary_length_from_base64(input, length); +} + +simdutf_warn_unused size_t implementation::binary_length_from_base64( + const char16_t *input, size_t length) const noexcept { + return scalar::base64::binary_length_from_base64(input, length); +} + simdutf_warn_unused size_t implementation::base64_length_from_binary( size_t length, base64_options options) const noexcept { return scalar::base64::base64_length_from_binary(length, options); @@ -11565,66 +10068,108 @@ namespace internal { SIMDUTF_IMPLEMENTATION_LASX + SIMDUTF_IMPLEMENTATION_FALLBACK == \ 1) -// Static array of known implementations. We are hoping these get baked into the -// executable without requiring a static initializer. - #if SIMDUTF_IMPLEMENTATION_ICELAKE + #if SIMDUTF_USE_STATIC_INITIALIZATION +static const icelake::implementation icelake_singleton{}; + #endif static const icelake::implementation *get_icelake_singleton() { + #if !SIMDUTF_USE_STATIC_INITIALIZATION static const icelake::implementation icelake_singleton{}; + #endif return &icelake_singleton; } #endif #if SIMDUTF_IMPLEMENTATION_HASWELL + #if SIMDUTF_USE_STATIC_INITIALIZATION +static const haswell::implementation haswell_singleton{}; + #endif static const haswell::implementation *get_haswell_singleton() { + #if !SIMDUTF_USE_STATIC_INITIALIZATION static const haswell::implementation haswell_singleton{}; + #endif return &haswell_singleton; } #endif #if SIMDUTF_IMPLEMENTATION_WESTMERE + #if SIMDUTF_USE_STATIC_INITIALIZATION +static const westmere::implementation westmere_singleton{}; + #endif static const westmere::implementation *get_westmere_singleton() { + #if !SIMDUTF_USE_STATIC_INITIALIZATION static const westmere::implementation westmere_singleton{}; + #endif return &westmere_singleton; } #endif #if SIMDUTF_IMPLEMENTATION_ARM64 + #if SIMDUTF_USE_STATIC_INITIALIZATION +static const arm64::implementation arm64_singleton{}; + #endif static const arm64::implementation *get_arm64_singleton() { + #if !SIMDUTF_USE_STATIC_INITIALIZATION static const arm64::implementation arm64_singleton{}; + #endif return &arm64_singleton; } #endif #if SIMDUTF_IMPLEMENTATION_PPC64 + #if SIMDUTF_USE_STATIC_INITIALIZATION +static const ppc64::implementation ppc64_singleton{}; + #endif static const ppc64::implementation *get_ppc64_singleton() { + #if !SIMDUTF_USE_STATIC_INITIALIZATION static const ppc64::implementation ppc64_singleton{}; + #endif return &ppc64_singleton; } #endif #if SIMDUTF_IMPLEMENTATION_RVV + #if SIMDUTF_USE_STATIC_INITIALIZATION +static const rvv::implementation rvv_singleton{}; + #endif static const rvv::implementation *get_rvv_singleton() { + #if !SIMDUTF_USE_STATIC_INITIALIZATION static const rvv::implementation rvv_singleton{}; + #endif return &rvv_singleton; } #endif -#if SIMDUTF_IMPLEMENTATION_LSX -static const lsx::implementation *get_lsx_singleton() { - static const lsx::implementation lsx_singleton{}; - return &lsx_singleton; -} -#endif #if SIMDUTF_IMPLEMENTATION_LASX + #if SIMDUTF_USE_STATIC_INITIALIZATION +static const lasx::implementation lasx_singleton{}; + #endif static const lasx::implementation *get_lasx_singleton() { + #if !SIMDUTF_USE_STATIC_INITIALIZATION static const lasx::implementation lasx_singleton{}; + #endif return &lasx_singleton; } #endif +#if SIMDUTF_IMPLEMENTATION_LSX + #if SIMDUTF_USE_STATIC_INITIALIZATION +static const lsx::implementation lsx_singleton{}; + #endif +static const lsx::implementation *get_lsx_singleton() { + #if !SIMDUTF_USE_STATIC_INITIALIZATION + static const lsx::implementation lsx_singleton{}; + #endif + return &lsx_singleton; +} +#endif #if SIMDUTF_IMPLEMENTATION_FALLBACK + #if SIMDUTF_USE_STATIC_INITIALIZATION +static const fallback::implementation fallback_singleton{}; + #endif static const fallback::implementation *get_fallback_singleton() { + #if !SIMDUTF_USE_STATIC_INITIALIZATION static const fallback::implementation fallback_singleton{}; + #endif return &fallback_singleton; } #endif #if SIMDUTF_SINGLE_IMPLEMENTATION -static const implementation *get_single_implementation() { +simdutf_really_inline static const implementation *get_single_implementation() { return #if SIMDUTF_IMPLEMENTATION_ICELAKE get_icelake_singleton(); @@ -11641,12 +10186,12 @@ static const implementation *get_single_implementation() { #if SIMDUTF_IMPLEMENTATION_PPC64 get_ppc64_singleton(); #endif - #if SIMDUTF_IMPLEMENTATION_LSX - get_lsx_singleton(); - #endif #if SIMDUTF_IMPLEMENTATION_LASX get_lasx_singleton(); #endif + #if SIMDUTF_IMPLEMENTATION_LSX + get_lsx_singleton(); + #endif #if SIMDUTF_IMPLEMENTATION_FALLBACK get_fallback_singleton(); #endif @@ -11659,8 +10204,8 @@ static const implementation *get_single_implementation() { class detect_best_supported_implementation_on_first_use final : public implementation { public: - std::string name() const noexcept final { return set_best()->name(); } - std::string description() const noexcept final { + std::string_view name() const noexcept final { return set_best()->name(); } + std::string_view description() const noexcept final { return set_best()->description(); } uint32_t required_instruction_sets() const noexcept final { @@ -11706,6 +10251,14 @@ class detect_best_supported_implementation_on_first_use final return set_best()->binary_to_base64(input, length, output, options); } + size_t + binary_to_base64_with_lines(const char *input, size_t length, char *output, + size_t line_length, + base64_options options) const noexcept override { + return set_best()->binary_to_base64_with_lines(input, length, output, + line_length, options); + } + const char *find(const char *start, const char *end, char character) const noexcept override { return set_best()->find(start, end, character); @@ -11716,6 +10269,16 @@ class detect_best_supported_implementation_on_first_use final return set_best()->find(start, end, character); } + simdutf_warn_unused size_t binary_length_from_base64( + const char *input, size_t length) const noexcept override { + return set_best()->binary_length_from_base64(input, length); + } + + simdutf_warn_unused size_t binary_length_from_base64( + const char16_t *input, size_t length) const noexcept override { + return set_best()->binary_length_from_base64(input, length); + } + simdutf_really_inline detect_best_supported_implementation_on_first_use() noexcept : implementation("best_supported_detector", @@ -11731,38 +10294,72 @@ static_assert(std::is_trivially_destructible< "detect_best_supported_implementation_on_first_use should be " "trivially destructible"); +#if SIMDUTF_USE_STATIC_INITIALIZATION +static const std::initializer_list + available_implementation_pointers{ + #if SIMDUTF_IMPLEMENTATION_ICELAKE + get_icelake_singleton(), + #endif + #if SIMDUTF_IMPLEMENTATION_HASWELL + get_haswell_singleton(), + #endif + #if SIMDUTF_IMPLEMENTATION_WESTMERE + get_westmere_singleton(), + #endif + #if SIMDUTF_IMPLEMENTATION_ARM64 + get_arm64_singleton(), + #endif + #if SIMDUTF_IMPLEMENTATION_PPC64 + get_ppc64_singleton(), + #endif + #if SIMDUTF_IMPLEMENTATION_RVV + get_rvv_singleton(), + #endif + #if SIMDUTF_IMPLEMENTATION_LASX + get_lasx_singleton(), + #endif + #if SIMDUTF_IMPLEMENTATION_LSX + get_lsx_singleton(), + #endif + #if SIMDUTF_IMPLEMENTATION_FALLBACK + get_fallback_singleton(), + #endif + }; +#endif static const std::initializer_list & get_available_implementation_pointers() { +#if !SIMDUTF_USE_STATIC_INITIALIZATION static const std::initializer_list available_implementation_pointers{ -#if SIMDUTF_IMPLEMENTATION_ICELAKE + #if SIMDUTF_IMPLEMENTATION_ICELAKE get_icelake_singleton(), -#endif -#if SIMDUTF_IMPLEMENTATION_HASWELL + #endif + #if SIMDUTF_IMPLEMENTATION_HASWELL get_haswell_singleton(), -#endif -#if SIMDUTF_IMPLEMENTATION_WESTMERE + #endif + #if SIMDUTF_IMPLEMENTATION_WESTMERE get_westmere_singleton(), -#endif -#if SIMDUTF_IMPLEMENTATION_ARM64 + #endif + #if SIMDUTF_IMPLEMENTATION_ARM64 get_arm64_singleton(), -#endif -#if SIMDUTF_IMPLEMENTATION_PPC64 + #endif + #if SIMDUTF_IMPLEMENTATION_PPC64 get_ppc64_singleton(), -#endif -#if SIMDUTF_IMPLEMENTATION_RVV + #endif + #if SIMDUTF_IMPLEMENTATION_RVV get_rvv_singleton(), -#endif -#if SIMDUTF_IMPLEMENTATION_LSX - get_lsx_singleton(), -#endif -#if SIMDUTF_IMPLEMENTATION_LASX + #endif + #if SIMDUTF_IMPLEMENTATION_LASX get_lasx_singleton(), -#endif -#if SIMDUTF_IMPLEMENTATION_FALLBACK + #endif + #if SIMDUTF_IMPLEMENTATION_LSX + get_lsx_singleton(), + #endif + #if SIMDUTF_IMPLEMENTATION_FALLBACK get_fallback_singleton(), + #endif + }; #endif - }; // available_implementation_pointers return available_implementation_pointers; } @@ -11799,6 +10396,10 @@ class unsupported_implementation final : public implementation { base64_options) const noexcept override { return 0; } + size_t binary_to_base64_with_lines(const char *, size_t, char *, size_t, + base64_options) const noexcept override { + return 0; + } const char *find(const char *, const char *, char) const noexcept override { return nullptr; } @@ -11806,14 +10407,27 @@ class unsupported_implementation final : public implementation { char16_t) const noexcept override { return nullptr; } + simdutf_warn_unused size_t + binary_length_from_base64(const char *, size_t) const noexcept override { + return 0; + } + simdutf_warn_unused size_t + binary_length_from_base64(const char16_t *, size_t) const noexcept override { + return 0; + } unsupported_implementation() : implementation("unsupported", "Unsupported CPU (no detected SIMD instructions)", 0) {} }; +#if SIMDUTF_USE_STATIC_INITIALIZATION +static const unsupported_implementation unsupported_singleton{}; +#endif const unsupported_implementation *get_unsupported_singleton() { +#if !SIMDUTF_USE_STATIC_INITIALIZATION static const unsupported_implementation unsupported_singleton{}; +#endif return &unsupported_singleton; } static_assert(std::is_trivially_destructible::value, @@ -11873,49 +10487,174 @@ detect_best_supported_implementation_on_first_use::set_best() const noexcept { /** * The list of available implementations compiled into simdutf. */ +#if SIMDUTF_USE_STATIC_INITIALIZATION +static const internal::available_implementation_list + available_implementations_instance{}; +#endif SIMDUTF_DLLIMPORTEXPORT const internal::available_implementation_list & get_available_implementations() { +#if !SIMDUTF_USE_STATIC_INITIALIZATION static const internal::available_implementation_list - available_implementations{}; - return available_implementations; + available_implementations_instance{}; +#endif + return available_implementations_instance; } +#if SIMDUTF_USE_STATIC_INITIALIZATION && !SIMDUTF_SINGLE_IMPLEMENTATION +static const internal::detect_best_supported_implementation_on_first_use + detect_best_supported_implementation_on_first_use_singleton; +#endif + +#if SIMDUTF_USE_STATIC_INITIALIZATION +static internal::atomic_ptr + active_implementation_instance{ + #if SIMDUTF_SINGLE_IMPLEMENTATION + internal::get_single_implementation() + #else + &detect_best_supported_implementation_on_first_use_singleton + #endif + }; +#endif + /** * The active implementation. */ SIMDUTF_DLLIMPORTEXPORT internal::atomic_ptr & get_active_implementation() { -#if SIMDUTF_SINGLE_IMPLEMENTATION - // skip runtime detection - static internal::atomic_ptr active_implementation{ - internal::get_single_implementation()}; - return active_implementation; -#else +#if !SIMDUTF_USE_STATIC_INITIALIZATION + #if !SIMDUTF_SINGLE_IMPLEMENTATION static const internal::detect_best_supported_implementation_on_first_use detect_best_supported_implementation_on_first_use_singleton; - static internal::atomic_ptr active_implementation{ - &detect_best_supported_implementation_on_first_use_singleton}; - return active_implementation; + #endif + static internal::atomic_ptr + active_implementation_instance{ + #if SIMDUTF_SINGLE_IMPLEMENTATION + internal::get_single_implementation() + #else + &detect_best_supported_implementation_on_first_use_singleton + #endif + }; #endif + return active_implementation_instance; } #if SIMDUTF_SINGLE_IMPLEMENTATION -const implementation *get_default_implementation() { +simdutf_really_inline const implementation *get_default_implementation() { return internal::get_single_implementation(); } #else -internal::atomic_ptr &get_default_implementation() { +simdutf_really_inline internal::atomic_ptr & +get_default_implementation() { return get_active_implementation(); } #endif -#define SIMDUTF_GET_CURRENT_IMPLEMENTION +#define SIMDUTF_GET_CURRENT_IMPLEMENTATION + + #if SIMDUTF_ATOMIC_REF +template +simdutf_warn_unused result atomic_base64_to_binary_safe_impl( + const char_type *input, size_t length, char *output, size_t &outlen, + base64_options options, + last_chunk_handling_options last_chunk_handling_options, + bool decode_up_to_bad_char) noexcept { + #if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) + // We use a smaller buffer during fuzzing to more easily detect bugs. + constexpr size_t buffer_size = 128; + #else + // Arbitrary block sizes: 4KB for input. + constexpr size_t buffer_size = 4096; + #endif + std::array temp_buffer; + const char_type *const input_init = input; + size_t actual_out = 0; + bool last_chunk = false; + const size_t length_init = length; + result r; + while (!last_chunk) { + last_chunk |= (temp_buffer.size() >= outlen - actual_out); + size_t temp_outlen = (std::min)(temp_buffer.size(), outlen - actual_out); + r = base64_to_binary_safe(input, length, temp_buffer.data(), temp_outlen, + options, last_chunk_handling_options, + decode_up_to_bad_char); + // We processed r.count characters of input. + // We wrote temp_outlen bytes to temp_buffer. + // If there is no ignorable characters, + // we should expect that values/4.0*3 == temp_outlen, + // except maybe at the tail end of the string. + + // + // We are assuming that when r.error == error_code::OUTPUT_BUFFER_TOO_SMALL, + // we truncate the results so that a number of base64 characters divisible + // by four is processed. + // + + // + // We wrote temp_outlen bytes to temp_buffer. + // We need to copy them to output. + // Copy with relaxed atomic operations to the output + simdutf_log_assert(temp_outlen <= outlen - actual_out, + "Output buffer is too small"); + simdutf_log_assert(temp_outlen <= temp_buffer.size(), + "Output buffer is too small"); + + simdutf::scalar::memcpy_atomic_write(output + actual_out, + temp_buffer.data(), temp_outlen); + actual_out += temp_outlen; + length -= r.count; + input += r.count; + + if (r.error != error_code::OUTPUT_BUFFER_TOO_SMALL) { + break; + } + } + if (size_t(input - input_init) != length_init) { + // We did not process all input characters. In such case, we + // should not end with an ignorable character. See + // https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64 + while (input > input_init && base64_ignorable(*(input - 1), options)) { + --input; + } + } + outlen = actual_out; + return {r.error, size_t(input - input_init)}; +} + +simdutf_warn_unused result atomic_base64_to_binary_safe( + const char *input, size_t length, char *output, size_t &outlen, + base64_options options, + last_chunk_handling_options last_chunk_handling_options, + bool decode_up_to_bad_char) noexcept { + return atomic_base64_to_binary_safe_impl( + input, length, output, outlen, options, last_chunk_handling_options, + decode_up_to_bad_char); +} +simdutf_warn_unused result atomic_base64_to_binary_safe( + const char16_t *input, size_t length, char *output, size_t &outlen, + base64_options options, + last_chunk_handling_options last_chunk_handling_options, + bool decode_up_to_bad_char) noexcept { + return atomic_base64_to_binary_safe_impl( + input, length, output, outlen, options, last_chunk_handling_options, + decode_up_to_bad_char); +} + #endif // SIMDUTF_ATOMIC_REF -simdutf_warn_unused const char *find(const char *start, const char *end, - char character) noexcept { +// this has been moved to implementation.h +// simdutf_warn_unused size_t +// base64_length_from_binary(size_t length, base64_options option) noexcept; + +// this has been moved to implementation.h +// simdutf_warn_unused size_t base64_length_from_binary_with_lines( +// size_t length, base64_options options, size_t line_length) noexcept; +// } + +simdutf_warn_unused const char *detail::find(const char *start, const char *end, + char character) noexcept { return get_default_implementation()->find(start, end, character); } -simdutf_warn_unused const char16_t * -find(const char16_t *start, const char16_t *end, char16_t character) noexcept { +simdutf_warn_unused const char16_t *detail::find(const char16_t *start, + const char16_t *end, + char16_t character) noexcept { return get_default_implementation()->find(start, end, character); } @@ -11938,6 +10677,16 @@ simdutf_warn_unused size_t maximal_binary_length_from_base64( input, length); } +simdutf_warn_unused size_t binary_length_from_base64(const char *input, + size_t length) noexcept { + return get_default_implementation()->binary_length_from_base64(input, length); +} + +simdutf_warn_unused size_t binary_length_from_base64(const char16_t *input, + size_t length) noexcept { + return get_default_implementation()->binary_length_from_base64(input, length); +} + simdutf_warn_unused result base64_to_binary( const char16_t *input, size_t length, char *output, base64_options options, last_chunk_handling_options last_chunk_handling_options) noexcept { @@ -11945,167 +10694,37 @@ simdutf_warn_unused result base64_to_binary( input, length, output, options, last_chunk_handling_options); } -template -simdutf_warn_unused result slow_base64_to_binary_safe_impl( - const chartype *input, size_t length, char *output, size_t &outlen, - base64_options options, - last_chunk_handling_options last_chunk_options) noexcept { - const bool ignore_garbage = (options & base64_default_accept_garbage) != 0; - auto ri = simdutf::scalar::base64::find_end(input, length, options); - size_t equallocation = ri.equallocation; - size_t equalsigns = ri.equalsigns; - length = ri.srclen; - size_t full_input_length = ri.full_input_length; - (void)full_input_length; - if (length == 0) { - outlen = 0; - if (!ignore_garbage && equalsigns > 0) { - return {INVALID_BASE64_CHARACTER, equallocation}; - } - return {SUCCESS, 0}; - } - - // The parameters of base64_tail_decode_safe are: - // - dst: the output buffer - // - outlen: the size of the output buffer - // - srcr: the input buffer - // - length: the size of the input buffer - // - padded_characters: the number of padding characters - // - options: the options for the base64 decoder - // - last_chunk_options: the options for the last chunk - // The function will return the number of bytes written to the output buffer - // and the number of bytes read from the input buffer. - // The function will also return an error code if the input buffer is not - // valid base64. - full_result r = scalar::base64::base64_tail_decode_safe( - output, outlen, input, length, equalsigns, options, last_chunk_options); - r = scalar::base64::patch_tail_result(r, 0, 0, equallocation, - full_input_length, last_chunk_options); - outlen = r.output_count; - if (!is_partial(last_chunk_options) && r.error == error_code::SUCCESS && - equalsigns > 0) { - // additional checks - if ((outlen % 3 == 0) || ((outlen % 3) + 1 + equalsigns != 4)) { - r.error = error_code::INVALID_BASE64_CHARACTER; - } - } - return {r.error, r.input_count}; // we cannot return r itself because it gets - // converted to error/output_count -} -simdutf_warn_unused bool base64_ignorable(char input, - base64_options options) noexcept { - return scalar::base64::is_ignorable(input, options); -} -simdutf_warn_unused bool base64_ignorable(char16_t input, - base64_options options) noexcept { - return scalar::base64::is_ignorable(input, options); -} -simdutf_warn_unused bool base64_valid(char input, - base64_options options) noexcept { - return scalar::base64::is_base64(input, options); -} -simdutf_warn_unused bool base64_valid(char16_t input, - base64_options options) noexcept { - return scalar::base64::is_base64(input, options); -} -simdutf_warn_unused bool -base64_valid_or_padding(char input, base64_options options) noexcept { - return scalar::base64::is_base64_or_padding(input, options); -} -simdutf_warn_unused bool -base64_valid_or_padding(char16_t input, base64_options options) noexcept { - return scalar::base64::is_base64_or_padding(input, options); +simdutf_warn_unused full_result base64_to_binary_details( + const char *input, size_t length, char *output, base64_options options, + last_chunk_handling_options last_chunk_handling_options) noexcept { + return get_default_implementation()->base64_to_binary_details( + input, length, output, options, last_chunk_handling_options); } -template -simdutf_warn_unused result base64_to_binary_safe_impl( - const chartype *input, size_t length, char *output, size_t &outlen, - base64_options options, - last_chunk_handling_options last_chunk_handling_options, - bool decode_up_to_bad_char) noexcept { - static_assert(std::is_same::value || - std::is_same::value, - "Only char and char16_t are supported."); - size_t remaining_input_length = length; - size_t remaining_output_length = outlen; - size_t input_position = 0; - size_t output_position = 0; - - // We also do a first pass using the fast path to decode as much as possible - size_t safe_input = (std::min)( - remaining_input_length, - base64_length_from_binary(remaining_output_length / 3 * 3, options)); - bool done_with_partial = (safe_input == remaining_input_length); - simdutf::full_result r = - get_default_implementation()->base64_to_binary_details( - input + input_position, safe_input, output + output_position, options, - done_with_partial - ? last_chunk_handling_options - : simdutf::last_chunk_handling_options::only_full_chunks); - simdutf_log_assert(r.input_count <= safe_input, - "You should not read more than safe_input"); - simdutf_log_assert(r.output_count <= remaining_output_length, - "You should not write more than remaining_output_length"); - // Technically redundant, but we want to be explicit about it. - input_position += r.input_count; - output_position += r.output_count; - remaining_input_length -= r.input_count; - remaining_output_length -= r.output_count; - if (r.error != simdutf::error_code::SUCCESS) { - // There is an error. We return. - if (decode_up_to_bad_char && - r.error == error_code::INVALID_BASE64_CHARACTER) { - return slow_base64_to_binary_safe_impl( - input, length, output, outlen, options, last_chunk_handling_options); - } - outlen = output_position; - return {r.error, input_position}; - } - - if (done_with_partial) { - // We are done. We have decoded everything. - outlen = output_position; - return {simdutf::error_code::SUCCESS, input_position}; - } - // We have decoded some data, but we still have some data to decode. - // We need to decode the rest of the input buffer. - r = simdutf::scalar::base64::base64_to_binary_details_safe_impl( - input + input_position, remaining_input_length, output + output_position, - remaining_output_length, options, last_chunk_handling_options); - input_position += r.input_count; - output_position += r.output_count; - remaining_input_length -= r.input_count; - remaining_output_length -= r.output_count; - - if (r.error != simdutf::error_code::SUCCESS) { - // There is an error. We return. - if (decode_up_to_bad_char && - r.error == error_code::INVALID_BASE64_CHARACTER) { - return slow_base64_to_binary_safe_impl( - input, length, output, outlen, options, last_chunk_handling_options); - } - outlen = output_position; - return {r.error, input_position}; - } - if (input_position < length) { - // We cannot process the entire input in one go, so we need to - // process it in two steps: first the fast path, then the slow path. - // In some cases, the processing might 'eat up' trailing ignorable - // characters in the fast path, but that can be a problem. - // suppose we have just white space followed by a single base64 character. - // If we first process the white space with the fast path, it will - // eat all of it. But, by the JavaScript standard, we should consume - // no character. See - // https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64 - while (input_position > 0 && - base64_ignorable(input[input_position - 1], options)) { - input_position--; - } - } - outlen = output_position; - return {simdutf::error_code::SUCCESS, input_position}; +simdutf_warn_unused full_result base64_to_binary_details( + const char16_t *input, size_t length, char *output, base64_options options, + last_chunk_handling_options last_chunk_handling_options) noexcept { + return get_default_implementation()->base64_to_binary_details( + input, length, output, options, last_chunk_handling_options); } +// moved to implementation.h +// simdutf_warn_unused bool base64_ignorable(char input, +// base64_options options) noexcept +// simdutf_warn_unused bool base64_ignorable(char16_t input, +// base64_options options) noexcept +// simdutf_warn_unused bool base64_valid(char input, +// base64_options options) noexcept +// simdutf_warn_unused bool base64_valid(char16_t input, +// base64_options options) noexcept +// simdutf_warn_unused bool +// base64_valid_or_padding(char input, base64_options options) noexcept +// simdutf_warn_unused bool +// base64_valid_or_padding(char16_t input, base64_options options) noexcept + +// base64_to_binary_safe_impl is moved to +// include/simdutf/base64_implementation.h + #if SIMDUTF_ATOMIC_REF size_t atomic_binary_to_base64(const char *input, size_t length, char *output, base64_options options) noexcept { @@ -12149,23 +10768,31 @@ base64_to_binary_safe(const char16_t *input, size_t length, char *output, decode_up_to_bad_char); } -simdutf_warn_unused size_t -base64_length_from_binary(size_t length, base64_options options) noexcept { - return get_default_implementation()->base64_length_from_binary(length, - options); -} - size_t binary_to_base64(const char *input, size_t length, char *output, base64_options options) noexcept { return get_default_implementation()->binary_to_base64(input, length, output, options); } +size_t binary_to_base64_with_lines(const char *input, size_t length, + char *output, size_t line_length, + base64_options options) noexcept { + return get_default_implementation()->binary_to_base64_with_lines( + input, length, output, line_length, options); +} + +#if SIMDUTF_USE_STATIC_INITIALIZATION +static const implementation *const builtin_impl_instance = + get_available_implementations()[SIMDUTF_STRINGIFY( + SIMDUTF_BUILTIN_IMPLEMENTATION)]; +#endif const implementation *builtin_implementation() { - static const implementation *builtin_impl = +#if !SIMDUTF_USE_STATIC_INITIALIZATION + static const implementation *const builtin_impl_instance = get_available_implementations()[SIMDUTF_STRINGIFY( SIMDUTF_BUILTIN_IMPLEMENTATION)]; - return builtin_impl; +#endif + return builtin_impl_instance; } } // namespace simdutf @@ -12179,7 +10806,6 @@ SIMDUTF_DISABLE_UNDESIRED_WARNINGS /* begin file src/simdutf/arm64/begin.h */ // redefining SIMDUTF_IMPLEMENTATION to "arm64" // #define SIMDUTF_IMPLEMENTATION arm64 -#define SIMDUTF_SIMD_HAS_BYTEMASK 1 /* end file src/simdutf/arm64/begin.h */ namespace simdutf { namespace arm64 { @@ -12218,8 +10844,101 @@ using namespace simd; * https://www.codeproject.com/Articles/276993/Base-Encoding-on-a-GPU. (2013). */ -size_t encode_base64(char *dst, const char *src, size_t srclen, - base64_options options) { +/** + * Insert a line feed character in the 16-byte input at index K in [0,16). + */ +inline uint8x16_t insert_line_feed16(uint8x16_t input, size_t K) { + static const uint8_t shuffle_masks[16][16] = { + {0x80, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 0x80, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 0x80, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 0x80, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 0x80, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 0x80, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 0x80, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 0x80, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 0x80, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 0x80, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0x80, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0x80, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0x80, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0x80, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0x80, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0x80}}; + // Prepare a vector with '\n' (0x0A) + uint8x16_t line_feed_vector = vdupq_n_u8('\n'); + + // Load the precomputed shuffle mask for K + uint8x16_t mask = vld1q_u8(shuffle_masks[K]); + + // Create a mask where 0x80 indicates the line feed position + uint8x16_t lf_pos = vceqq_u8(mask, vdupq_n_u8(0x80)); + + uint8x16_t result = vqtbl1q_u8(input, mask); + + // Use vbsl to select '\n' where lf_pos is true, else keep input bytes + return vbslq_u8(lf_pos, line_feed_vector, result); +} + +// offset is the number of characters in the current line. +// It can range from 0 to line_length (inclusive). +// If offset == line_length, we need to insert a line feed before writing +// anything. +size_t write_output_with_line_feeds(uint8_t *dst, uint8x16_t src, + size_t line_length, size_t &offset) { + // Fast path: no need to insert line feeds + // If we are at offset, we would write from [offset, offset + 16). + // We need that line_length >= offset + 16. + if (offset + 16 <= line_length) { + // No need to insert line feeds + vst1q_u8(dst, src); + offset += 16; // offset could be line_length here. + return 16; + } + + // We have that offset + 16 >= line_length + // the common case is that line_length is greater than 16 + if (simdutf_likely(line_length >= 16)) { + // offset <= line_length. + // offset + 16 > line_length + // So line_length - offset < 16 + // and line_length - offset >= 0 + uint8x16_t chunk = insert_line_feed16(src, line_length - offset); + vst1q_u8(dst, chunk); + // Not ideal to pull the last element and write it separately but + // it simplifies the code. + *(dst + 16) = vgetq_lane_u8(src, 15); + offset += 16 - line_length; + return 16 + 1; // we wrote 16 bytes plus one line feed + } + // Uncommon case where line_length < 16 + // This is going to be SLOW. + else { + uint8_t buffer[16]; + vst1q_u8(buffer, src); + size_t out_pos = 0; + size_t local_offset = offset; + for (size_t i = 0; i < 16;) { + if (local_offset == line_length) { + dst[out_pos++] = '\n'; + local_offset = 0; + } + dst[out_pos++] = buffer[i++]; + local_offset++; + } + offset = local_offset; + return out_pos; + } +} + +template +size_t encode_base64_impl(char *dst, const char *src, size_t srclen, + base64_options options, + size_t line_length = simdutf::default_line_length) { + size_t offset = 0; + if (line_length < 4) { + line_length = 4; // We do not support line_length less than 4 + } // credit: Wojciech Muła uint8_t *out = (uint8_t *)dst; constexpr static uint8_t source_table[64] = { @@ -12262,8 +10981,40 @@ size_t encode_base64(char *dst, const char *src, size_t srclen, result.val[1] = vqtbl4q_u8(table, result.val[1]); result.val[2] = vqtbl4q_u8(table, result.val[2]); result.val[3] = vqtbl4q_u8(table, result.val[3]); - vst4q_u8(out, result); - out += 64; + if (insert_line_feeds) { + if (line_length >= 64) { // fast path + vst4q_u8(out, result); + if (offset + 64 > line_length) { + size_t location_end = line_length - offset; + size_t to_move = 64 - location_end; + std::memmove(out + location_end + 1, out + location_end, to_move); + out[location_end] = '\n'; + offset = to_move; + out += 64 + 1; + } else { + offset += 64; + out += 64; + } + } else { // slow path + uint8x16x2_t Z0 = vzipq_u8(result.val[0], result.val[1]); + uint8x16x2_t Z1 = vzipq_u8(result.val[2], result.val[3]); + uint16x8x2_t Z2 = vzipq_u16(vreinterpretq_u16_u8(Z0.val[0]), + vreinterpretq_u16_u8(Z1.val[0])); + uint16x8x2_t Z3 = vzipq_u16(vreinterpretq_u16_u8(Z0.val[1]), + vreinterpretq_u16_u8(Z1.val[1])); + uint8x16_t T0 = vreinterpretq_u8_u16(Z2.val[0]); + uint8x16_t T1 = vreinterpretq_u8_u16(Z2.val[1]); + uint8x16_t T2 = vreinterpretq_u8_u16(Z3.val[0]); + uint8x16_t T3 = vreinterpretq_u8_u16(Z3.val[1]); + out += write_output_with_line_feeds(out, T0, line_length, offset); + out += write_output_with_line_feeds(out, T1, line_length, offset); + out += write_output_with_line_feeds(out, T2, line_length, offset); + out += write_output_with_line_feeds(out, T3, line_length, offset); + } + } else { + vst4q_u8(out, result); + out += 64; + } } if (i + 24 <= srclen) { @@ -12280,17 +11031,52 @@ size_t encode_base64(char *dst, const char *src, size_t srclen, result.val[1] = vqtbl4_u8(table, result.val[1]); result.val[2] = vqtbl4_u8(table, result.val[2]); result.val[3] = vqtbl4_u8(table, result.val[3]); - vst4_u8(out, result); - out += 32; + if (insert_line_feeds) { + if (line_length >= 32) { // fast path + vst4_u8(out, result); + if (offset + 32 > line_length) { + size_t location_end = line_length - offset; + size_t to_move = 32 - location_end; + std::memmove(out + location_end + 1, out + location_end, to_move); + out[location_end] = '\n'; + offset = to_move; + out += 32 + 1; + } else { + offset += 32; + out += 32; + } + } else { // slow path + uint8x8x2_t Z0 = vzip_u8(result.val[0], result.val[1]); + uint8x8x2_t Z1 = vzip_u8(result.val[2], result.val[3]); + uint16x4x2_t Z2 = vzip_u16(vreinterpret_u16_u8(Z0.val[0]), + vreinterpret_u16_u8(Z1.val[0])); + uint16x4x2_t Z3 = vzip_u16(vreinterpret_u16_u8(Z0.val[1]), + vreinterpret_u16_u8(Z1.val[1])); + uint8x8_t T0 = vreinterpret_u8_u16(Z2.val[0]); + uint8x8_t T1 = vreinterpret_u8_u16(Z2.val[1]); + uint8x8_t T2 = vreinterpret_u8_u16(Z3.val[0]); + uint8x8_t T3 = vreinterpret_u8_u16(Z3.val[1]); + uint8x16_t TT0 = vcombine_u8(T0, T1); + uint8x16_t TT1 = vcombine_u8(T2, T3); + out += write_output_with_line_feeds(out, TT0, line_length, offset); + out += write_output_with_line_feeds(out, TT1, line_length, offset); + } + } else { + vst4_u8(out, result); + out += 32; + } i += 24; } - - out += scalar::base64::tail_encode_base64((char *)out, src + i, srclen - i, - options); - + out += scalar::base64::tail_encode_base64_impl( + (char *)out, src + i, srclen - i, options, line_length, offset); return size_t((char *)out - dst); } +size_t encode_base64(char *dst, const char *src, size_t srclen, + base64_options options) { + return encode_base64_impl(dst, src, srclen, options); +} + static inline void compress(uint8x16_t data, uint16_t mask, char *output) { if (mask == 0) { vst1q_u8((uint8_t *)output, data); @@ -12746,7 +11532,9 @@ compress_decode_base64(char *dst, const char_type *src, size_t srclen, (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) + (uint32_t(uint8_t(buffer_start[3])) << 0 * 6)) << 8; +#if !SIMDUTF_IS_BIG_ENDIAN triple = scalar::u32_swap_bytes(triple); +#endif std::memcpy(dst, &triple, 4); dst += 3; @@ -12758,7 +11546,9 @@ compress_decode_base64(char *dst, const char_type *src, size_t srclen, (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) + (uint32_t(uint8_t(buffer_start[3])) << 0 * 6)) << 8; +#if !SIMDUTF_IS_BIG_ENDIAN triple = scalar::u32_swap_bytes(triple); +#endif std::memcpy(dst, &triple, 3); dst += 3; @@ -13057,40 +11847,6 @@ template struct buf_block_reader { size_t idx; }; -// Routines to print masks and text for debugging bitmask operations -simdutf_unused static char *format_input_text_64(const uint8_t *text) { - static char *buf = - reinterpret_cast(malloc(sizeof(simd8x64) + 1)); - for (size_t i = 0; i < sizeof(simd8x64); i++) { - buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -// Routines to print masks and text for debugging bitmask operations -simdutf_unused static char *format_input_text(const simd8x64 &in) { - static char *buf = - reinterpret_cast(malloc(sizeof(simd8x64) + 1)); - in.store(reinterpret_cast(buf)); - for (size_t i = 0; i < sizeof(simd8x64); i++) { - if (buf[i] < ' ') { - buf[i] = '_'; - } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -simdutf_unused static char *format_mask(uint64_t mask) { - static char *buf = reinterpret_cast(malloc(64 + 1)); - for (size_t i = 0; i < 64; i++) { - buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; - } - buf[64] = '\0'; - return buf; -} - template simdutf_really_inline buf_block_reader::buf_block_reader(const uint8_t *_buf, size_t _len) @@ -13137,6 +11893,71 @@ simdutf_really_inline void buf_block_reader::advance() { /* end file src/generic/buf_block_reader.h */ // other functions +/* begin file src/generic/base64lengths.h */ +namespace simdutf { +namespace arm64 { +namespace { +namespace base64_lengths { + +simdutf_warn_unused size_t binary_length_from_base64(const char *input, + size_t length) { + size_t pos = 0; + size_t count = 0; + for (; pos + 64 <= length; pos += 64) { + simd8x64 block(reinterpret_cast(input + pos)); + uint64_t maybe_base64 = block.gteq(33); // >= 33 which is '!' in ASCII + count += count_ones(maybe_base64); + } + while (pos < length) { + count += (input[pos] > 0x20) ? 1 : 0; + pos++; + } + // Count padding at the end. + size_t padding = 0; + pos = length; + while (pos > 0 && padding < 2) { + char c = input[--pos]; + if (c == '=') { + padding++; + } else if (c > ' ') { + break; + } + } + return ((count - padding) * 3) / 4; +} + +simdutf_warn_unused size_t binary_length_from_base64(const char16_t *input, + size_t length) { + size_t pos = 0; + size_t count = 0; + for (; pos + 32 <= length; pos += 32) { + simd16x32 block(reinterpret_cast(input + pos)); + uint64_t maybe_base64 = block.gteq(33); // >= 33 which is '!' in ASCII + count += count_ones(maybe_base64); + } + while (pos < length) { + count += (input[pos] > 0x20) ? 1 : 0; + pos++; + } + // Count padding at the end. + size_t padding = 0; + pos = length; + while (pos > 0 && padding < 2) { + char16_t c = input[--pos]; + if (c == '=') { + padding++; + } else if (c > ' ') { + break; + } + } + return ((count - padding) * 3) / 4; +} + +} // namespace base64_lengths +} // unnamed namespace +} // namespace arm64 +} // namespace simdutf +/* end file src/generic/base64lengths.h */ // // Implementation-specific overrides @@ -13270,6 +12091,12 @@ size_t implementation::binary_to_base64(const char *input, size_t length, return encode_base64(output, input, length, options); } +size_t implementation::binary_to_base64_with_lines( + const char *input, size_t length, char *output, size_t line_length, + base64_options options) const noexcept { + return encode_base64_impl(output, input, length, options, line_length); +} + const char *implementation::find(const char *start, const char *end, char character) const noexcept { return util_find(start, end, character); @@ -13280,11 +12107,20 @@ const char16_t *implementation::find(const char16_t *start, const char16_t *end, return util_find(start, end, character); } +simdutf_warn_unused size_t implementation::binary_length_from_base64( + const char *input, size_t length) const noexcept { + return base64_lengths::binary_length_from_base64(input, length); +} + +simdutf_warn_unused size_t implementation::binary_length_from_base64( + const char16_t *input, size_t length) const noexcept { + return base64_lengths::binary_length_from_base64(input, length); +} + } // namespace arm64 } // namespace simdutf /* begin file src/simdutf/arm64/end.h */ -#undef SIMDUTF_SIMD_HAS_BYTEMASK /* end file src/simdutf/arm64/end.h */ /* end file src/arm64/implementation.cpp */ #endif @@ -13332,6 +12168,13 @@ size_t implementation::binary_to_base64(const char *input, size_t length, return scalar::base64::tail_encode_base64(output, input, length, options); } +size_t implementation::binary_to_base64_with_lines( + const char *input, size_t length, char *output, size_t line_length, + base64_options options) const noexcept { + return scalar::base64::tail_encode_base64_impl(output, input, length, + options, line_length); +} + const char *implementation::find(const char *start, const char *end, char character) const noexcept { return std::find(start, end, character); @@ -13597,9 +12440,14 @@ struct block64 { __m512i chunks[1]; }; -template -size_t encode_base64(char *dst, const char *src, size_t srclen, - base64_options options) { +template +size_t encode_base64_impl(char *dst, const char *src, size_t srclen, + base64_options options, + size_t line_length = simdutf::default_line_length) { + size_t offset = 0; + if (line_length < 4) { + line_length = 4; // We do not support line_length less than 4 + } // credit: Wojciech Muła const uint8_t *input = (const uint8_t *)src; @@ -13608,7 +12456,6 @@ size_t encode_base64(char *dst, const char *src, size_t srclen, base64_url ? "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - const __m512i shuffle_input = _mm512_setr_epi32( 0x01020001, 0x04050304, 0x07080607, 0x0a0b090a, 0x0d0e0c0d, 0x10110f10, 0x13141213, 0x16171516, 0x191a1819, 0x1c1d1b1c, 0x1f201e1f, 0x22232122, @@ -13618,17 +12465,58 @@ size_t encode_base64(char *dst, const char *src, size_t srclen, const __m512i multi_shifts = _mm512_set1_epi64(UINT64_C(0x3036242a1016040a)); size_t size = srclen; __mmask64 input_mask = 0xffffffffffff; // (1 << 48) - 1 - while (size >= 48) { + // We want that input == end_input means that we must stop. + const uint8_t *end_input = input + (size - (size % 48)); + while (input != end_input) { const __m512i v = _mm512_maskz_loadu_epi8( input_mask, reinterpret_cast(input)); const __m512i in = _mm512_permutexvar_epi8(shuffle_input, v); const __m512i indices = _mm512_multishift_epi64_epi8(multi_shifts, in); const __m512i result = _mm512_permutexvar_epi8(indices, lookup); - _mm512_storeu_si512(reinterpret_cast<__m512i *>(out), result); - out += 64; + if (use_lines) { + if (offset + 64 > line_length) { + if (line_length >= 64) { + __m512i expanded = _mm512_mask_expand_epi8( + _mm512_set1_epi8('\n'), ~(1ULL << ((line_length - offset))), + result); + _mm512_storeu_si512(reinterpret_cast<__m512i *>(out), expanded); + __m128i last_lane = + _mm512_extracti32x4_epi32(result, 3); // Lane 3 (bytes 48-63) + uint8_t last_byte = + static_cast(_mm_extract_epi8(last_lane, 15)); + out[64] = last_byte; + out += 65; + offset = 64 - (line_length - offset); + } else { // slow path + alignas(64) uint8_t local_buffer[64]; + _mm512_storeu_si512(reinterpret_cast<__m512i *>(local_buffer), + result); + size_t out_pos = 0; + size_t local_offset = offset; + for (size_t j = 0; j < 64;) { + if (local_offset == line_length) { + out[out_pos++] = '\n'; + local_offset = 0; + } + out[out_pos++] = local_buffer[j++]; + local_offset++; + } + offset = local_offset; + out += out_pos; + } + } else { + _mm512_storeu_si512(reinterpret_cast<__m512i *>(out), result); + offset += 64; + out += 64; + } + } else { + _mm512_storeu_si512(reinterpret_cast<__m512i *>(out), result); + out += 64; + } input += 48; - size -= 48; } + size = size % 48; + input_mask = ((__mmask64)1 << size) - 1; const __m512i v = _mm512_maskz_loadu_epi8( input_mask, reinterpret_cast(input)); @@ -13643,14 +12531,65 @@ size_t encode_base64(char *dst, const char *src, size_t srclen, if (!padding_needed) { output_len = non_padded_output_len; } - __mmask64 output_mask = output_len == 64 ? (__mmask64)UINT64_MAX - : ((__mmask64)1 << output_len) - 1; + // If no output, we are done. + if (output_len == 0) { + return (size_t)(out - (uint8_t *)dst); + } + __mmask64 output_mask = 0xFFFFFFFFFFFFFFFF >> (64 - output_len); __m512i result = _mm512_mask_permutexvar_epi8( _mm512_set1_epi8('='), ((__mmask64)1 << non_padded_output_len) - 1, indices, lookup); - _mm512_mask_storeu_epi8(reinterpret_cast<__m512i *>(out), output_mask, - result); - return (size_t)(out - (uint8_t *)dst) + output_len; + if (use_lines) { + if (offset + output_len > line_length) { + if (line_length >= 64) { + __m512i expanded = _mm512_mask_expand_epi8( + _mm512_set1_epi8('\n'), ~(1ULL << ((line_length - offset))), + result); + if (output_len == 64) { + _mm512_storeu_si512(reinterpret_cast<__m512i *>(out), expanded); + out += 64; + _mm512_mask_storeu_epi8(reinterpret_cast<__m512i *>(out - 63), + 1ULL << 63, result); + out++; + } else { + output_mask = 0xFFFFFFFFFFFFFFFF >> (64 - output_len - 1); + _mm512_mask_storeu_epi8(reinterpret_cast<__m512i *>(out), output_mask, + expanded); + out += output_len + 1; + } + } else { + alignas(64) uint8_t local_buffer[64]; + _mm512_storeu_si512(reinterpret_cast<__m512i *>(local_buffer), result); + size_t out_pos = 0; + size_t local_offset = offset; + for (size_t j = 0; j < output_len;) { + if (local_offset == line_length) { + out[out_pos++] = '\n'; + local_offset = 0; + } + out[out_pos++] = local_buffer[j++]; + local_offset++; + } + offset = local_offset; + out += out_pos; + } + } else { + _mm512_mask_storeu_epi8(reinterpret_cast<__m512i *>(out), output_mask, + result); + out += output_len; + } + } else { + _mm512_mask_storeu_epi8(reinterpret_cast<__m512i *>(out), output_mask, + result); + out += output_len; + } + return (size_t)(out - (uint8_t *)dst); +} + +template +size_t encode_base64(char *dst, const char *src, size_t srclen, + base64_options options) { + return encode_base64_impl(dst, src, srclen, options); } template @@ -13807,7 +12746,7 @@ compress_decode_base64(char *dst, const chartype *src, size_t srclen, size_t full_input_length = ri.full_input_length; if (srclen == 0) { if (!ignore_garbage && padding_characters > 0) { - return {INVALID_BASE64_CHARACTER, equallocation, 0}; + return {INVALID_BASE64_CHARACTER, equallocation, 0, true}; } return {SUCCESS, full_input_length, 0}; } @@ -13905,8 +12844,8 @@ compress_decode_base64(char *dst, const chartype *src, size_t srclen, // We never should have that the number of base64 characters + the // number of padding characters is more than 4. if (!ignore_garbage && (idx + padding_characters > 4)) { - return {INVALID_BASE64_CHARACTER, size_t(src - srcinit), - size_t(dst - dstinit), true}; + return {INVALID_BASE64_CHARACTER, equallocation, size_t(dst - dstinit), + true}; } // The idea here is that in loose mode, // if there is padding at all, it must be used @@ -13916,8 +12855,8 @@ compress_decode_base64(char *dst, const chartype *src, size_t srclen, last_chunk_options == last_chunk_handling_options::loose && (idx >= 2) && padding_characters > 0 && ((idx + padding_characters) & 3) != 0) { - return {INVALID_BASE64_CHARACTER, size_t(src - srcinit), - size_t(dst - dstinit), true}; + return {INVALID_BASE64_CHARACTER, equallocation, size_t(dst - dstinit), + true}; } else // The idea here is that in strict mode, we do not want to accept // incomplete base64 chunks. So if the chunk was otherwise valid, we @@ -13928,7 +12867,8 @@ compress_decode_base64(char *dst, const chartype *src, size_t srclen, // The partial chunk was at src - idx _mm512_mask_storeu_epi8((__m512i *)dst, output_mask, shuffled); dst += output_len; - return {BASE64_INPUT_REMAINDER, equallocation, size_t(dst - dstinit)}; + return {BASE64_INPUT_REMAINDER, equallocation, size_t(dst - dstinit), + true}; } else // If there is a partial chunk with insufficient padding, with // stop_before_partial, we need to just ignore it. In "only full" mode, @@ -14009,7 +12949,7 @@ compress_decode_base64(char *dst, const chartype *src, size_t srclen, _mm512_mask_storeu_epi8((__m512i *)dst, output_mask, shuffled); dst += output_len; return {INVALID_BASE64_CHARACTER, equallocation, - size_t(dst - dstinit)}; + size_t(dst - dstinit), true}; } else { _mm512_mask_storeu_epi8((__m512i *)dst, output_mask, shuffled); dst += output_len; @@ -14020,7 +12960,7 @@ compress_decode_base64(char *dst, const chartype *src, size_t srclen, size_t output_count = size_t(dst - dstinit); if ((output_count % 3 == 0) || ((output_count % 3) + 1 + padding_characters != 4)) { - return {INVALID_BASE64_CHARACTER, equallocation, output_count}; + return {INVALID_BASE64_CHARACTER, equallocation, output_count, true}; } } return {SUCCESS, full_input_length, size_t(dst - dstinit)}; @@ -14029,11 +12969,76 @@ compress_decode_base64(char *dst, const chartype *src, size_t srclen, if (!ignore_garbage && padding_characters > 0) { if ((size_t(dst - dstinit) % 3 == 0) || ((size_t(dst - dstinit) % 3) + 1 + padding_characters != 4)) { - return {INVALID_BASE64_CHARACTER, equallocation, size_t(dst - dstinit)}; + return {INVALID_BASE64_CHARACTER, equallocation, size_t(dst - dstinit), + true}; } } return {SUCCESS, srclen, size_t(dst - dstinit)}; } + +simdutf_warn_unused size_t icelake_binary_length_from_base64(const char *input, + size_t length) { + size_t count = 0; + const char *ptr = input; + const char *end = input + length; + + __m512i spaces = _mm512_set1_epi8(0x20); + while (ptr + 64 <= end) { + __m512i data = _mm512_loadu_si512(reinterpret_cast(ptr)); + uint64_t mask = _mm512_cmpgt_epi8_mask(data, spaces); + count += count_ones(mask); + ptr += 64; + } + + while (ptr < end) { + count += (*ptr > 0x20) ? 1 : 0; + ptr++; + } + + size_t padding = 0; + size_t pos = length; + while (pos > 0 && padding < 2) { + char c = input[--pos]; + if (c == '=') { + padding++; + } else if (c > ' ') { + break; + } + } + return ((count - padding) * 3) / 4; +} + +simdutf_warn_unused size_t +icelake_binary_length_from_base64(const char16_t *input, size_t length) { + size_t count = 0; + const char16_t *ptr = input; + const char16_t *end = input + length; + + __m512i spaces = _mm512_set1_epi16(0x20); + while (ptr + 32 <= end) { + __m512i data = _mm512_loadu_si512(reinterpret_cast(ptr)); + __mmask32 mask = _mm512_cmpgt_epi16_mask(data, spaces); + count += _mm_popcnt_u32(mask); + ptr += 32; + } + + while (ptr < end) { + count += (*ptr > 0x20) ? 1 : 0; + ptr++; + } + + size_t padding = 0; + size_t pos = length; + while (pos > 0 && padding < 2) { + char16_t c = input[--pos]; + if (c == '=') { + padding++; + } else if (c > ' ') { + break; + } + } + return ((count - padding) * 3) / 4; +} /* end file src/icelake/icelake_base64.inl.cpp */ /* begin file src/icelake/icelake_find.inl.cpp */ simdutf_really_inline const char *util_find(const char *start, const char *end, @@ -14055,6 +13060,8 @@ simdutf_really_inline const char *util_find(const char *start, const char *end, __m512i data = _mm512_maskz_loadu_epi8( load_mask, reinterpret_cast(start)); __mmask64 match_mask = _mm512_cmpeq_epi8_mask(data, char_vec); + match_mask &= load_mask; // When searching for null terminators, this + // prevents false positives if (match_mask != 0) { size_t index = _tzcnt_u64(match_mask); @@ -14144,6 +13151,8 @@ simdutf_really_inline const char16_t *util_find(const char16_t *start, __m512i data = _mm512_maskz_loadu_epi16( load_mask, reinterpret_cast(start)); __mmask32 match_mask = _mm512_cmpeq_epi16_mask(data, char_vec); + match_mask &= load_mask; // When searching for null terminators, this + // prevents false positives if (match_mask != 0) { size_t index = _tzcnt_u32(match_mask); @@ -14323,6 +13332,18 @@ size_t implementation::binary_to_base64(const char *input, size_t length, } } +size_t implementation::binary_to_base64_with_lines( + const char *input, size_t length, char *output, size_t line_length, + base64_options options) const noexcept { + if (options & base64_url) { + return encode_base64_impl(output, input, length, options, + line_length); + } else { + return encode_base64_impl(output, input, length, options, + line_length); + } +} + const char *implementation::find(const char *start, const char *end, char character) const noexcept { return util_find(start, end, character); @@ -14332,6 +13353,16 @@ const char16_t *implementation::find(const char16_t *start, const char16_t *end, return util_find(start, end, character); } +simdutf_warn_unused size_t implementation::binary_length_from_base64( + const char *input, size_t length) const noexcept { + return icelake_binary_length_from_base64(input, length); +} + +simdutf_warn_unused size_t implementation::binary_length_from_base64( + const char16_t *input, size_t length) const noexcept { + return icelake_binary_length_from_base64(input, length); +} + } // namespace icelake } // namespace simdutf @@ -14410,6 +13441,7 @@ using namespace simd; template simdutf_really_inline __m256i lookup_pshufb_improved(const __m256i input) { + // Precomputed shuffle masks for K = 1 to 16 // credit: Wojciech Muła __m256i result = _mm256_subs_epu8(input, _mm256_set1_epi8(51)); const __m256i less = _mm256_cmpgt_epi8(_mm256_set1_epi8(26), input); @@ -14436,9 +13468,110 @@ simdutf_really_inline __m256i lookup_pshufb_improved(const __m256i input) { return _mm256_add_epi8(result, input); } -template -size_t encode_base64(char *dst, const char *src, size_t srclen, - base64_options options) { +simdutf_really_inline __m256i insert_line_feed32(__m256i input, int K) { + + static const uint8_t low_table[16][32] = { + {0x80, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 0x80, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 1, 0x80, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 1, 2, 0x80, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 1, 2, 3, 0x80, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 1, 2, 3, 4, 0x80, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 1, 2, 3, 4, 5, 0x80, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 1, 2, 3, 4, 5, 6, 0x80, 7, 8, 9, 10, 11, 12, 13, 14, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 1, 2, 3, 4, 5, 6, 7, 0x80, 8, 9, 10, 11, 12, 13, 14, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 0x80, 9, 10, 11, 12, 13, 14, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0x80, 10, 11, 12, 13, 14, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0x80, 11, 12, 13, 14, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0x80, 12, 13, 14, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0x80, 13, 14, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0x80, 14, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0x80, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}; + static const uint8_t high_table[16][32] = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0x80, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 0x80, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 1, 0x80, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 1, 2, 0x80, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 1, 2, 3, 0x80, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 1, 2, 3, 4, 0x80, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 1, 2, 3, 4, 5, 0x80, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 1, 2, 3, 4, 5, 6, 0x80, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 1, 2, 3, 4, 5, 6, 7, 0x80, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 0x80, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0x80, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0x80, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0x80, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0x80, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0x80, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0x80}}; + + __m256i line_feed_vector = _mm256_set1_epi8('\n'); + if (K >= 16) { + __m256i mask = _mm256_loadu_si256((const __m256i *)high_table[K - 16]); + __m256i lf_pos = + _mm256_cmpeq_epi8(mask, _mm256_set1_epi8(static_cast(0x80))); + __m256i shuffled = _mm256_shuffle_epi8(input, mask); + __m256i result = _mm256_blendv_epi8(shuffled, line_feed_vector, lf_pos); + return result; + } + // Shift input right by 1 byte + __m256i shift = _mm256_alignr_epi8( + input, _mm256_permute2x128_si256(input, input, 0x21), 15); + + input = _mm256_blend_epi32(input, shift, 0xF0); + + __m256i mask = _mm256_loadu_si256((const __m256i *)low_table[K]); + + __m256i lf_pos = + _mm256_cmpeq_epi8(mask, _mm256_set1_epi8(static_cast(0x80))); + __m256i shuffled = _mm256_shuffle_epi8(input, mask); + + __m256i result = _mm256_blendv_epi8(shuffled, line_feed_vector, lf_pos); + return result; +} + +template +size_t +avx2_encode_base64_impl(char *dst, const char *src, size_t srclen, + base64_options options, + size_t line_length = simdutf::default_line_length) { + size_t offset = 0; + + if (line_length < 4) { + line_length = 4; // We do not support line_length less than 4 + } // credit: Wojciech Muła const uint8_t *input = (const uint8_t *)src; @@ -14504,20 +13637,123 @@ size_t encode_base64(char *dst, const char *src, size_t srclen, const __m256i input2 = _mm256_or_si256(t1_2, t3_2); const __m256i input3 = _mm256_or_si256(t1_3, t3_3); - _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), - lookup_pshufb_improved(input0)); - out += 32; + if (use_lines) { + if (line_length >= 32) { // fast path + __m256i result; + result = lookup_pshufb_improved(input0); + if (offset + 32 > line_length) { + size_t location_end = line_length - offset; + size_t to_move = 32 - location_end; + // We could do this, or extract instead. + _mm256_storeu_si256(reinterpret_cast<__m256i *>(out + 1), result); + _mm256_storeu_si256( + reinterpret_cast<__m256i *>(out), + insert_line_feed32(result, static_cast(location_end))); + offset = to_move; + out += 32 + 1; + } else { + _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), result); + offset += 32; + out += 32; + } + result = lookup_pshufb_improved(input1); + + if (offset + 32 > line_length) { + size_t location_end = line_length - offset; + size_t to_move = 32 - location_end; + + // We could do this, or extract instead. + _mm256_storeu_si256(reinterpret_cast<__m256i *>(out + 1), result); + _mm256_storeu_si256( + reinterpret_cast<__m256i *>(out), + insert_line_feed32(result, static_cast(location_end))); + // see above. + // out[32] = static_cast(_mm256_extract_epi8(result, 31)); + offset = to_move; + out += 32 + 1; + } else { - _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), - lookup_pshufb_improved(input1)); - out += 32; + _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), result); - _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), - lookup_pshufb_improved(input2)); - out += 32; - _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), - lookup_pshufb_improved(input3)); - out += 32; + offset += 32; + out += 32; + } + result = lookup_pshufb_improved(input2); + + if (offset + 32 > line_length) { + size_t location_end = line_length - offset; + size_t to_move = 32 - location_end; + + // We could do this, or extract instead. + _mm256_storeu_si256(reinterpret_cast<__m256i *>(out + 1), result); + _mm256_storeu_si256( + reinterpret_cast<__m256i *>(out), + insert_line_feed32(result, static_cast(location_end))); + // see above. + // out[32] = static_cast(_mm256_extract_epi8(result, 31)); + offset = to_move; + out += 32 + 1; + } else { + _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), result); + offset += 32; + out += 32; + } + result = lookup_pshufb_improved(input3); + + if (offset + 32 > line_length) { + size_t location_end = line_length - offset; + size_t to_move = 32 - location_end; + + // We could do this, or extract instead. + _mm256_storeu_si256(reinterpret_cast<__m256i *>(out + 1), result); + _mm256_storeu_si256( + reinterpret_cast<__m256i *>(out), + insert_line_feed32(result, static_cast(location_end))); + // see above. + // out[32] = static_cast(_mm256_extract_epi8(result, 31)); + offset = to_move; + out += 32 + 1; + } else { + _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), result); + offset += 32; + out += 32; + } + } else { // slow path + // could be optimized + uint8_t buffer[128]; + _mm256_storeu_si256(reinterpret_cast<__m256i *>(buffer), + lookup_pshufb_improved(input0)); + _mm256_storeu_si256(reinterpret_cast<__m256i *>(buffer + 32), + lookup_pshufb_improved(input1)); + _mm256_storeu_si256(reinterpret_cast<__m256i *>(buffer + 64), + lookup_pshufb_improved(input2)); + _mm256_storeu_si256(reinterpret_cast<__m256i *>(buffer + 96), + lookup_pshufb_improved(input3)); + size_t out_pos = 0; + size_t local_offset = offset; + for (size_t j = 0; j < 128;) { + if (local_offset == line_length) { + out[out_pos++] = '\n'; + local_offset = 0; + } + out[out_pos++] = buffer[j++]; + local_offset++; + } + offset = local_offset; + out += out_pos; + } + } else { + _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), + lookup_pshufb_improved(input0)); + _mm256_storeu_si256(reinterpret_cast<__m256i *>(out + 32), + lookup_pshufb_improved(input1)); + _mm256_storeu_si256(reinterpret_cast<__m256i *>(out + 64), + lookup_pshufb_improved(input2)); + _mm256_storeu_si256(reinterpret_cast<__m256i *>(out + 96), + lookup_pshufb_improved(input3)); + + out += 128; + } } for (; i + 28 <= srclen; i += 24) { // lo = [xxxx|DDDC|CCBB|BAAA] @@ -14539,12 +13775,57 @@ size_t encode_base64(char *dst, const char *src, size_t srclen, const __m256i t3 = _mm256_mullo_epi16(t2, _mm256_set1_epi32(0x01000010)); const __m256i indices = _mm256_or_si256(t1, t3); - _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), - lookup_pshufb_improved(indices)); - out += 32; + if (use_lines) { + if (line_length >= 32) { // fast path + _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), + lookup_pshufb_improved(indices)); + + if (offset + 32 > line_length) { + size_t location_end = line_length - offset; + size_t to_move = 32 - location_end; + std::memmove(out + location_end + 1, out + location_end, to_move); + out[location_end] = '\n'; + offset = to_move; + out += 32 + 1; + } else { + offset += 32; + out += 32; + } + } else { // slow path + // could be optimized + alignas(32) uint8_t buffer[32]; + _mm256_storeu_si256(reinterpret_cast<__m256i *>(buffer), + lookup_pshufb_improved(indices)); + std::memcpy(out, buffer, 32); + size_t out_pos = 0; + size_t local_offset = offset; + for (size_t j = 0; j < 32;) { + if (local_offset == line_length) { + out[out_pos++] = '\n'; + local_offset = 0; + } + out[out_pos++] = buffer[j++]; + local_offset++; + } + offset = local_offset; + out += out_pos; + } + } else { + _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), + lookup_pshufb_improved(indices)); + + out += 32; + } } - return i / 3 * 4 + scalar::base64::tail_encode_base64((char *)out, src + i, - srclen - i, options); + return ((char *)out - (char *)dst) + + scalar::base64::tail_encode_base64_impl( + (char *)out, src + i, srclen - i, options, line_length, offset); +} + +template +size_t encode_base64(char *dst, const char *src, size_t srclen, + base64_options options) { + return avx2_encode_base64_impl(dst, src, srclen, options); } static inline void compress(__m128i data, uint16_t mask, char *output) { @@ -14622,7 +13903,7 @@ simdutf_really_inline void base64_decode_block_safe(char *out, const char *src) { base64_decode(out, _mm256_loadu_si256(reinterpret_cast(src))); - char buffer[32]; // We enforce safety with a buffer. + alignas(32) char buffer[32]; // We enforce safety with a buffer. base64_decode( buffer, _mm256_loadu_si256(reinterpret_cast(src + 32))); std::memcpy(out + 24, buffer, 24); @@ -14674,7 +13955,7 @@ class block64 { simdutf_really_inline void base64_decode_block_safe(char *out) { base64_decode(out, chunks[0]); - char buffer[32]; // We enforce safety with a buffer. + alignas(32) char buffer[32]; // We enforce safety with a buffer. base64_decode(buffer, chunks[1]); std::memcpy(out + 24, buffer, 24); } @@ -14900,6 +14181,73 @@ class block64 { return 63; } }; + +simdutf_warn_unused size_t avx2_binary_length_from_base64(const char *input, + size_t length) { + size_t count = 0; + const char *ptr = input; + const char *end = input + length; + + __m256i spaces = _mm256_set1_epi8(0x20); + while (ptr + 32 <= end) { + __m256i data = _mm256_loadu_si256(reinterpret_cast(ptr)); + __m256i gt_space = _mm256_cmpgt_epi8(data, spaces); + uint32_t mask = static_cast(_mm256_movemask_epi8(gt_space)); + count += count_ones(mask); + ptr += 32; + } + + while (ptr < end) { + count += (*ptr > 0x20) ? 1 : 0; + ptr++; + } + + size_t padding = 0; + size_t pos = length; + while (pos > 0 && padding < 2) { + char c = input[--pos]; + if (c == '=') { + padding++; + } else if (c > ' ') { + break; + } + } + return ((count - padding) * 3) / 4; +} + +simdutf_warn_unused size_t avx2_binary_length_from_base64(const char16_t *input, + size_t length) { + size_t count = 0; + const char16_t *ptr = input; + const char16_t *end = input + length; + + __m256i spaces = _mm256_set1_epi16(0x20); + while (ptr + 16 <= end) { + __m256i data = _mm256_loadu_si256(reinterpret_cast(ptr)); + __m256i gt_space = _mm256_cmpgt_epi16(data, spaces); + uint32_t mask = static_cast(_mm256_movemask_epi8(gt_space)); + count += count_ones(mask); + ptr += 16; + } + count /= 2; + + while (ptr < end) { + count += (*ptr > 0x20) ? 1 : 0; + ptr++; + } + + size_t padding = 0; + size_t pos = length; + while (pos > 0 && padding < 2) { + char16_t c = input[--pos]; + if (c == '=') { + padding++; + } else if (c > ' ') { + break; + } + } + return ((count - padding) * 3) / 4; +} /* end file src/haswell/avx2_base64.cpp */ } // unnamed namespace @@ -14939,40 +14287,6 @@ template struct buf_block_reader { size_t idx; }; -// Routines to print masks and text for debugging bitmask operations -simdutf_unused static char *format_input_text_64(const uint8_t *text) { - static char *buf = - reinterpret_cast(malloc(sizeof(simd8x64) + 1)); - for (size_t i = 0; i < sizeof(simd8x64); i++) { - buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -// Routines to print masks and text for debugging bitmask operations -simdutf_unused static char *format_input_text(const simd8x64 &in) { - static char *buf = - reinterpret_cast(malloc(sizeof(simd8x64) + 1)); - in.store(reinterpret_cast(buf)); - for (size_t i = 0; i < sizeof(simd8x64); i++) { - if (buf[i] < ' ') { - buf[i] = '_'; - } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -simdutf_unused static char *format_mask(uint64_t mask) { - static char *buf = reinterpret_cast(malloc(64 + 1)); - for (size_t i = 0; i < 64; i++) { - buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; - } - buf[64] = '\0'; - return buf; -} - template simdutf_really_inline buf_block_reader::buf_block_reader(const uint8_t *_buf, size_t _len) @@ -15077,7 +14391,7 @@ compress_decode_base64(char *dst, const chartype *src, size_t srclen, size_t full_input_length = ri.full_input_length; if (srclen == 0) { if (!ignore_garbage && equalsigns > 0) { - return {INVALID_BASE64_CHARACTER, equallocation, 0}; + return {INVALID_BASE64_CHARACTER, equallocation, 0, true}; } return {SUCCESS, full_input_length, 0}; } @@ -15244,7 +14558,8 @@ compress_decode_base64(char *dst, const chartype *src, size_t srclen, if (!ignore_garbage && equalsigns > 0) { if ((size_t(dst - dstinit) % 3 == 0) || ((size_t(dst - dstinit) % 3) + 1 + equalsigns != 4)) { - return {INVALID_BASE64_CHARACTER, equallocation, size_t(dst - dstinit)}; + return {INVALID_BASE64_CHARACTER, equallocation, size_t(dst - dstinit), + true}; } } return {SUCCESS, srclen, size_t(dst - dstinit)}; @@ -15460,9 +14775,21 @@ size_t implementation::binary_to_base64(const char *input, size_t length, char *output, base64_options options) const noexcept { if (options & base64_url) { - return encode_base64(output, input, length, options); + return encode_base64(output, input, length, options); + } else { + return encode_base64(output, input, length, options); + } +} + +size_t implementation::binary_to_base64_with_lines( + const char *input, size_t length, char *output, size_t line_length, + base64_options options) const noexcept { + if (options & base64_url) { + return avx2_encode_base64_impl(output, input, length, options, + line_length); } else { - return encode_base64(output, input, length, options); + return avx2_encode_base64_impl(output, input, length, options, + line_length); } } @@ -15476,6 +14803,16 @@ const char16_t *implementation::find(const char16_t *start, const char16_t *end, return util::find(start, end, character); } +simdutf_warn_unused size_t implementation::binary_length_from_base64( + const char *input, size_t length) const noexcept { + return avx2_binary_length_from_base64(input, length); +} + +simdutf_warn_unused size_t implementation::binary_length_from_base64( + const char16_t *input, size_t length) const noexcept { + return avx2_binary_length_from_base64(input, length); +} + } // namespace haswell } // namespace simdutf @@ -16551,6 +15888,307 @@ enum class ErrorReporting { none, // no error checking is done, we assume valid inputs }; +#if (SIMDUTF_FEATURE_UTF16 || SIMDUTF_FEATURE_UTF32) && SIMDUTF_FEATURE_UTF8 +/* begin file src/ppc64/ppc64_convert_utf16_to_utf8.cpp */ +/* + The vectorized algorithm works on single SSE register i.e., it + loads eight 16-bit code units. + + We consider three cases: + 1. an input register contains no surrogates and each value + is in range 0x0000 .. 0x07ff. + 2. an input register contains no surrogates and values are + is in range 0x0000 .. 0xffff. + 3. an input register contains surrogates --- i.e. codepoints + can have 16 or 32 bits. + + Ad 1. + + When values are less than 0x0800, it means that a 16-bit code unit + can be converted into: 1) single UTF8 byte (when it is an ASCII + char) or 2) two UTF8 bytes. + + For this case we do only some shuffle to obtain these 2-byte + codes and finally compress the whole SSE register with a single + shuffle. + + We need 256-entry lookup table to get a compression pattern + and the number of output bytes in the compressed vector register. + Each entry occupies 17 bytes. + + Ad 2. + + When values fit in 16-bit code units, but are above 0x07ff, then + a single word may produce one, two or three UTF8 bytes. + + We prepare data for all these three cases in two registers. + The first register contains lower two UTF8 bytes (used in all + cases), while the second one contains just the third byte for + the three-UTF8-bytes case. + + Finally these two registers are interleaved forming eight-element + array of 32-bit values. The array spans two SSE registers. + The bytes from the registers are compressed using two shuffles. + + We need 256-entry lookup table to get a compression pattern + and the number of output bytes in the compressed vector register. + Each entry occupies 17 bytes. + + To summarize: + - We need two 256-entry tables that have 8704 bytes in total. +*/ + +// Auxiliary procedure used by UTF-16 and UTF-32 into UTF-8. +// Note the pointer is passed by reference, it is updated by the procedure. +template +simdutf_really_inline void ppc64_convert_utf16_to_1_2_3_bytes_of_utf8( + const vector_u16 in, uint16_t one_byte_bitmask, + const T one_or_two_bytes_bytemask, uint16_t one_or_two_bytes_bitmask, + char *&utf8_output) { + // case: code units from register produce either 1, 2 or 3 UTF-8 bytes +#if SIMDUTF_IS_BIG_ENDIAN + const auto dup_lsb = + vector_u8(1, 1, 3, 3, 5, 5, 7, 7, 9, 9, 11, 11, 13, 13, 15, 15); +#else + const auto dup_lsb = + vector_u8(0, 0, 2, 2, 4, 4, 6, 6, 8, 8, 10, 10, 12, 12, 14, 14); +#endif // SIMDUTF_IS_BIG_ENDIAN + + /* In this branch we handle three cases: + 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - + single UFT-8 byte + 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two + UTF-8 bytes + 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - + three UTF-8 bytes + + We expand the input word (16-bit) into two code units (32-bit), thus + we have room for four bytes. However, we need five distinct bit + layouts. Note that the last byte in cases #2 and #3 is the same. + + We precompute byte 1 for case #1 and the common byte for cases #2 & #3 + in register t2. + + We precompute byte 1 for case #3 and -- **conditionally** -- precompute + either byte 1 for case #2 or byte 2 for case #3. Note that they + differ by exactly one bit. + + Finally from these two code units we build proper UTF-8 sequence, taking + into account the case (i.e, the number of bytes to write). + */ + /** + * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce: + * t2 => [0ccc|cccc] [10cc|cccc] + * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb]) + */ + // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc] + const auto t0 = as_vector_u16(dup_lsb.lookup_16(as_vector_u8(in))); + + // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc] + const auto t1 = t0 & uint16_t(0b0011111101111111); + // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc] + const auto t2 = t1 | uint16_t(0b1000000000000000); + + // in = [aaaa|bbbb|bbcc|cccc] + // a0 = [0000|0000|0000|aaaa] + const auto a0 = in.shr<12>(); + // b0 = [aabb|bbbb|cccc|cc00] + const auto b0 = in.shl<2>(); + // s0 = [00bb|bbbb|00cc|cccc] + const auto s0 = select(uint16_t(0x3f00), b0, a0); + + // s3 = [11bb|bbbb|1110|aaaa] + const auto s3 = s0 | uint16_t(0b1100000011100000); + + const auto m0 = + ~as_vector_u16(one_or_two_bytes_bytemask) & uint16_t(0b0100000000000000); + const auto s4 = s3 ^ m0; + + // 4. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle + const uint16_t mask = + (one_byte_bitmask & 0x5555) | (one_or_two_bytes_bitmask & 0xaaaa); + if (mask == 0) { + // We only have three-byte code units. Use fast path. +#if SIMDUTF_IS_BIG_ENDIAN + // Lookups produced by scripts/ppc64_convert_utf16_to_utf8.py + const auto shuffle0 = + vector_u8(1, 0, 16, 3, 2, 18, 5, 4, 20, 7, 6, 22, 9, 8, 24, 11); + const auto shuffle1 = vector_u8(10, 26, 13, 12, 28, 15, 14, 30, -1, -1, -1, + -1, -1, -1, -1, -1); +#else + const auto shuffle0 = + vector_u8(0, 1, 17, 2, 3, 19, 4, 5, 21, 6, 7, 23, 8, 9, 25, 10); + const auto shuffle1 = vector_u8(11, 27, 12, 13, 29, 14, 15, 31, -1, -1, -1, + -1, -1, -1, -1, -1); +#endif // SIMDUTF_IS_BIG_ENDIAN + const auto utf8_0 = shuffle0.lookup_32(as_vector_u8(s4), as_vector_u8(t2)); + const auto utf8_1 = shuffle1.lookup_32(as_vector_u8(s4), as_vector_u8(t2)); + + utf8_0.store(utf8_output); + utf8_output += 16; + utf8_1.store(utf8_output); + utf8_output += 8; + return; + } + + const uint8_t mask0 = uint8_t(mask); + + const uint8_t *row0 = + &simdutf::tables::ppc64_utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0]; + const auto shuffle0 = vector_u8::load(row0 + 1); + + const auto utf8_0 = shuffle0.lookup_32(as_vector_u8(s4), as_vector_u8(t2)); + const uint8_t mask1 = static_cast(mask >> 8); + + const uint8_t *row1 = + &simdutf::tables::ppc64_utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0]; + const auto shuffle1 = vector_u8::load(row1 + 1) + uint8_t(8); + const auto utf8_1 = shuffle1.lookup_32(as_vector_u8(s4), as_vector_u8(t2)); + + utf8_0.store(utf8_output); + utf8_output += row0[0]; + utf8_1.store(utf8_output); + utf8_output += row1[0]; +} + +struct utf16_to_utf8_t { + error_code err; + const char16_t *input; + char *output; +}; + +/* + Returns utf16_to_utf8_t value + A scalar routine should carry on the conversion of the tail, + iff there was no error. +*/ +template +utf16_to_utf8_t ppc64_convert_utf16_to_utf8(const char16_t *buf, size_t len, + char *utf8_output) { + + const char16_t *end = buf + len; + + const auto v_f800 = vector_u16(0xf800); + const auto v_d800 = vector_u16(0xd800); + const size_t safety_margin = + 12; // to avoid overruns, see issue + // https://github.com/simdutf/simdutf/issues/92 + + while (end - buf >= std::ptrdiff_t(16 + safety_margin)) { + auto in = vector_u16::load(buf); + if (not match_system(big_endian)) { + in = in.swap_bytes(); + } + // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes + if (in.is_ascii()) { + auto nextin = vector_u16::load(buf + vector_u16::ELEMENTS); + if (not match_system(big_endian)) { + nextin = nextin.swap_bytes(); + } + + if (nextin.is_ascii()) { + // 1. pack the bytes + const auto utf8_packed = vector_u16::pack(in, nextin); + // 2. store (16 bytes) + utf8_packed.store(utf8_output); + // 3. adjust pointers + buf += 16; + utf8_output += 16; + continue; // we are done for this round! + } + + // next block is not ASCII + const auto utf8_packed = vector_u16::pack(in, in); + // 2. store (16 bytes) + utf8_packed.store(utf8_output); + // 3. adjust pointers + buf += 8; + utf8_output += 8; + in = nextin; + // fallback + } + + // no bits set above 7th bit + const auto one_byte_bytemask = in < uint16_t(1 << 7); + const uint16_t one_byte_bitmask = one_byte_bytemask.to_bitmask(); + + // no bits set above 11th bit + const auto one_or_two_bytes_bytemask = in < uint16_t(1 << 11); + const uint16_t one_or_two_bytes_bitmask = + one_or_two_bytes_bytemask.to_bitmask(); + + if (one_or_two_bytes_bitmask == 0xffff) { + write_v_u16_11bits_to_utf8( + in, utf8_output, as_vector_u8(one_byte_bytemask), one_byte_bitmask); + buf += 8; + continue; + } + + // 1. Check if there are any surrogate word in the input chunk. + // We have also to deal with situation when there is a surrogate word + // at the end of a chunk. + const auto surrogates_bytemask = (in & v_f800) == v_d800; + + // bitmask = 0x0000 if there are no surrogates + // = 0xc000 if the last word is a surrogate + const uint16_t surrogates_bitmask = surrogates_bytemask.to_bitmask(); + // It might seem like checking for surrogates_bitmask == 0xc000 could help. + // However, it is likely an uncommon occurrence. + if (surrogates_bitmask == 0x0000) { + ppc64_convert_utf16_to_1_2_3_bytes_of_utf8( + in, one_byte_bitmask, one_or_two_bytes_bytemask, + one_or_two_bytes_bitmask, utf8_output); + + buf += 8; + // surrogate pair(s) in a register + } else { + // Let us do a scalar fallback. + // It may seem wasteful to use scalar code, but being efficient with SIMD + // in the presence of surrogate pairs may require non-trivial tables. + size_t forward = 15; + size_t k = 0; + if (size_t(end - buf) < forward + 1) { + forward = size_t(end - buf - 1); + } + for (; k < forward; k++) { + uint16_t word = scalar::utf16::swap_if_needed(buf[k]); + if ((word & 0xFF80) == 0) { + *utf8_output++ = uint8_t(word); + } else if ((word & 0xF800) == 0) { + *utf8_output++ = uint8_t((word >> 6) | 0b11000000); + *utf8_output++ = uint8_t((word & 0b111111) | 0b10000000); + } else if ((word & 0xF800) != 0xD800) { + *utf8_output++ = uint8_t((word >> 12) | 0b11100000); + *utf8_output++ = uint8_t(((word >> 6) & 0b111111) | 0b10000000); + *utf8_output++ = uint8_t((word & 0b111111) | 0b10000000); + } else { + // must be a surrogate pair + uint16_t diff = uint16_t(word - 0xD800); + uint16_t next_word = + scalar::utf16::swap_if_needed(buf[k + 1]); + k++; + uint16_t diff2 = uint16_t(next_word - 0xDC00); + if ((diff | diff2) > 0x3FF) { + return utf16_to_utf8_t{error_code::SURROGATE, buf + k - 1, + utf8_output}; + } + uint32_t value = (diff << 10) + diff2 + 0x10000; + *utf8_output++ = uint8_t((value >> 18) | 0b11110000); + *utf8_output++ = uint8_t(((value >> 12) & 0b111111) | 0b10000000); + *utf8_output++ = uint8_t(((value >> 6) & 0b111111) | 0b10000000); + *utf8_output++ = uint8_t((value & 0b111111) | 0b10000000); + } + } + buf += k; + } + } // while + + return utf16_to_utf8_t{error_code::SUCCESS, buf, utf8_output}; +} +/* end file src/ppc64/ppc64_convert_utf16_to_utf8.cpp */ +#endif // (SIMDUTF_FEATURE_UTF16 || SIMDUTF_FEATURE_UTF32) && + // SIMDUTF_FEATURE_UTF8 + /* begin file src/ppc64/ppc64_base64.cpp */ /* * References and further reading: @@ -16580,9 +16218,9 @@ enum class ErrorReporting { * https://www.codeproject.com/Articles/276993/Base-Encoding-on-a-GPU. (2013). * * AMD XOP specific: http://0x80.pl/notesen/2016-01-12-sse-base64-encoding.html - * Altivec has capabilites of AMD XOP (or vice versa): shuffle using 2 vectors + * Altivec has capabilities of AMD XOP (or vice versa): shuffle using 2 vectors * and variable shifts, thus this implementation shares some code solution - * (modulo intrisic function names). + * (modulo intrinsic function names). */ constexpr bool with_base64_std = false; @@ -17095,7 +16733,7 @@ compress_decode_base64(char *dst, const chartype *src, size_t srclen, size_t full_input_length = ri.full_input_length; if (srclen == 0) { if (!ignore_garbage && equalsigns > 0) { - return {INVALID_BASE64_CHARACTER, equallocation, 0}; + return {INVALID_BASE64_CHARACTER, equallocation, 0, true}; } return {SUCCESS, full_input_length, 0}; } @@ -17262,7 +16900,8 @@ compress_decode_base64(char *dst, const chartype *src, size_t srclen, if (!ignore_garbage && equalsigns > 0) { if ((size_t(dst - dstinit) % 3 == 0) || ((size_t(dst - dstinit) % 3) + 1 + equalsigns != 4)) { - return {INVALID_BASE64_CHARACTER, equallocation, size_t(dst - dstinit)}; + return {INVALID_BASE64_CHARACTER, equallocation, size_t(dst - dstinit), + true}; } } return {SUCCESS, srclen, size_t(dst - dstinit)}; @@ -17590,6 +17229,14 @@ size_t implementation::binary_to_base64(const char *input, size_t length, return encode_base64(output, input, length, options); } } + +size_t implementation::binary_to_base64_with_lines( + const char *input, size_t length, char *output, size_t line_length, + base64_options options) const noexcept { + return scalar::base64::tail_encode_base64_impl(output, input, length, + options, line_length); +} + const char *implementation::find(const char *start, const char *end, char character) const noexcept { return util::find(start, end, character); @@ -17695,8 +17342,8 @@ rvv_utf32_store_utf16_m4(uint16_t *dst, vuint32m4_t utf32, size_t vl, /* end file src/rvv/rvv_utf8_to.inl.cpp */ /* begin file src/rvv/rvv_find.cpp */ -simdutf_really_inline const char *util_find(const char *start, const char *end, - char character) noexcept { +const char *implementation::find(const char *start, const char *end, + char character) const noexcept { const char *src = start; for (size_t len = end - start, vl; len > 0; len -= vl, src += vl) { vl = __riscv_vsetvl_e8m8(len); @@ -17709,9 +17356,8 @@ simdutf_really_inline const char *util_find(const char *start, const char *end, return end; } -simdutf_really_inline const char16_t *util_find(const char16_t *start, - const char16_t *end, - char16_t character) noexcept { +const char16_t *implementation::find(const char16_t *start, const char16_t *end, + char16_t character) const noexcept { const char16_t *src = start; for (size_t len = end - start, vl; len > 0; len -= vl, src += vl) { vl = __riscv_vsetvl_e16m8(len); @@ -17759,14 +17405,11 @@ size_t implementation::binary_to_base64(const char *input, size_t length, return scalar::base64::tail_encode_base64(output, input, length, options); } -const char *implementation::find(const char *start, const char *end, - char character) const noexcept { - return util_find(start, end, character); -} - -const char16_t *implementation::find(const char16_t *start, const char16_t *end, - char16_t character) const noexcept { - return util_find(start, end, character); +size_t implementation::binary_to_base64_with_lines( + const char *input, size_t length, char *output, size_t line_length, + base64_options options) const noexcept { + return scalar::base64::tail_encode_base64_impl(output, input, length, + options, line_length); } } // namespace rvv @@ -17834,7 +17477,6 @@ using namespace simd; */ // --- encoding ---------------------------------------------------- - template __m128i lookup_pshufb_improved(const __m128i input) { // credit: Wojciech Muła // reduce 0..51 -> 0 @@ -17866,9 +17508,47 @@ template __m128i lookup_pshufb_improved(const __m128i input) { return _mm_add_epi8(result, input); } -template -size_t encode_base64(char *dst, const char *src, size_t srclen, - base64_options options) { +inline __m128i insert_line_feed16(__m128i input, size_t K) { + static const uint8_t shuffle_masks[16][16] = { + {0x80, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 0x80, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 0x80, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 0x80, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 0x80, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 0x80, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 0x80, 6, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 0x80, 7, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 0x80, 8, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 0x80, 9, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0x80, 10, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0x80, 11, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0x80, 12, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0x80, 13, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0x80, 14}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0x80}}; + // Prepare a vector with '\n' (0x0A) + __m128i line_feed_vector = _mm_set1_epi8('\n'); + + // Load the precomputed shuffle mask for K (index K-1) + __m128i mask = _mm_loadu_si128((__m128i *)shuffle_masks[K]); + __m128i lf_pos = _mm_cmpeq_epi8(mask, _mm_set1_epi8(static_cast(0x80))); + + // Perform the shuffle to reposition the K bytes + __m128i shuffled = _mm_shuffle_epi8(input, mask); + + // Blend with line_feed_vector to insert '\n' at the appropriate positions + __m128i result = _mm_blendv_epi8(shuffled, line_feed_vector, lf_pos); + + return result; +} +template +size_t encode_base64_impl(char *dst, const char *src, size_t srclen, + base64_options options, + size_t line_length = simdutf::default_line_length) { + size_t offset = 0; + if (line_length < 4) { + line_length = 4; // We do not support line_length less than 4 + } // credit: Wojciech Muła // SSE (lookup: pshufb improved unrolled) const uint8_t *input = (const uint8_t *)src; @@ -17918,21 +17598,98 @@ size_t encode_base64(char *dst, const char *src, size_t srclen, const __m128i input2 = _mm_or_si128(t1_2, t3_2); const __m128i input3 = _mm_or_si128(t1_3, t3_3); - _mm_storeu_si128(reinterpret_cast<__m128i *>(out), - lookup_pshufb_improved(input0)); - out += 16; - - _mm_storeu_si128(reinterpret_cast<__m128i *>(out), - lookup_pshufb_improved(input1)); - out += 16; - - _mm_storeu_si128(reinterpret_cast<__m128i *>(out), - lookup_pshufb_improved(input2)); - out += 16; + const __m128i t0 = lookup_pshufb_improved(input0); + const __m128i t1 = lookup_pshufb_improved(input1); + const __m128i t2 = lookup_pshufb_improved(input2); + const __m128i t3 = lookup_pshufb_improved(input3); + + if (use_lines) { + if (line_length >= 64) { // fast path + if (offset + 64 > line_length) { + size_t location_end = line_length - offset; + size_t to_move = 64 - location_end; + if (location_end < 16) { + // We can store or extract store. See below. + //_mm_storeu_si128(reinterpret_cast<__m128i *>(out+1), t0); + _mm_storeu_si128(reinterpret_cast<__m128i *>(out), + insert_line_feed16(t0, location_end)); + out[16] = static_cast(_mm_extract_epi8(t0, 15)); + out += 17; + } else { + _mm_storeu_si128(reinterpret_cast<__m128i *>(out), t0); + out += 16; + } + if (location_end >= 16 && location_end < 32) { + // We can store or extract store. See below. + //_mm_storeu_si128(reinterpret_cast<__m128i *>(out+1), t1); + _mm_storeu_si128(reinterpret_cast<__m128i *>(out), + insert_line_feed16(t1, location_end - 16)); + out[16] = static_cast(_mm_extract_epi8(t1, 15)); + out += 17; + } else { + _mm_storeu_si128(reinterpret_cast<__m128i *>(out), t1); + out += 16; + } + if (location_end >= 32 && location_end < 48) { + // We can store or extract store. See below. + //_mm_storeu_si128(reinterpret_cast<__m128i *>(out+1), t2); + _mm_storeu_si128(reinterpret_cast<__m128i *>(out), + insert_line_feed16(t2, location_end - 32)); + out[16] = static_cast(_mm_extract_epi8(t2, 15)); + out += 17; + } else { + _mm_storeu_si128(reinterpret_cast<__m128i *>(out), t2); + out += 16; + } + if (location_end >= 48) { + // We can store or extract store. See below. + //_mm_storeu_si128(reinterpret_cast<__m128i *>(out+1), t3); + _mm_storeu_si128(reinterpret_cast<__m128i *>(out), + insert_line_feed16(t3, location_end - 48)); + out[16] = static_cast(_mm_extract_epi8(t3, 15)); + out += 17; + } else { + _mm_storeu_si128(reinterpret_cast<__m128i *>(out), t3); + out += 16; + } + offset = to_move; + } else { - _mm_storeu_si128(reinterpret_cast<__m128i *>(out), - lookup_pshufb_improved(input3)); - out += 16; + _mm_storeu_si128(reinterpret_cast<__m128i *>(out), t0); + _mm_storeu_si128(reinterpret_cast<__m128i *>(out + 16), t1); + _mm_storeu_si128(reinterpret_cast<__m128i *>(out + 32), t2); + _mm_storeu_si128(reinterpret_cast<__m128i *>(out + 48), t3); + offset += 64; + out += 64; + } + } else { // slow path + // could be optimized + alignas(64) uint8_t buffer[64]; + _mm_storeu_si128(reinterpret_cast<__m128i *>(buffer), t0); + _mm_storeu_si128(reinterpret_cast<__m128i *>(buffer + 16), t1); + _mm_storeu_si128(reinterpret_cast<__m128i *>(buffer + 32), t2); + _mm_storeu_si128(reinterpret_cast<__m128i *>(buffer + 48), t3); + std::memcpy(out, buffer, 64); + size_t out_pos = 0; + size_t local_offset = offset; + for (size_t j = 0; j < 64;) { + if (local_offset == line_length) { + out[out_pos++] = '\n'; + local_offset = 0; + } + out[out_pos++] = buffer[j++]; + local_offset++; + } + offset = local_offset; + out += out_pos; + } + } else { + _mm_storeu_si128(reinterpret_cast<__m128i *>(out), t0); + _mm_storeu_si128(reinterpret_cast<__m128i *>(out + 16), t1); + _mm_storeu_si128(reinterpret_cast<__m128i *>(out + 32), t2); + _mm_storeu_si128(reinterpret_cast<__m128i *>(out + 48), t3); + out += 64; + } } for (; i + 16 <= srclen; i += 12) { @@ -17970,13 +17727,55 @@ size_t encode_base64(char *dst, const char *src, size_t srclen, // res = [00dddddd|00cccccc|00bbbbbb|00aaaaaa] = t1 | t3 const __m128i indices = _mm_or_si128(t1, t3); - _mm_storeu_si128(reinterpret_cast<__m128i *>(out), - lookup_pshufb_improved(indices)); - out += 16; + const __m128i T0 = lookup_pshufb_improved(indices); + + _mm_storeu_si128(reinterpret_cast<__m128i *>(out), T0); + + if (use_lines) { + if (line_length >= 16) { // fast path + _mm_storeu_si128(reinterpret_cast<__m128i *>(out), T0); + if (offset + 16 > line_length) { + size_t location_end = line_length - offset; + size_t to_move = 16 - location_end; + std::memmove(out + location_end + 1, out + location_end, to_move); + out[location_end] = '\n'; + offset = to_move; + out += 16 + 1; + } else { + offset += 16; + out += 16; + } + } else { // slow path + // could be optimized + uint8_t buffer[16]; + _mm_storeu_si128(reinterpret_cast<__m128i *>(buffer), T0); + size_t out_pos = 0; + size_t local_offset = offset; + for (size_t j = 0; j < 16;) { + if (local_offset == line_length) { + out[out_pos++] = '\n'; + local_offset = 0; + } + out[out_pos++] = buffer[j++]; + local_offset++; + } + offset = local_offset; + out += out_pos; + } + } else { + _mm_storeu_si128(reinterpret_cast<__m128i *>(out), T0); + out += 16; + } } + return ((char *)out - (char *)dst) + + scalar::base64::tail_encode_base64_impl( + (char *)out, src + i, srclen - i, options, line_length, offset); +} - return i / 3 * 4 + scalar::base64::tail_encode_base64((char *)out, src + i, - srclen - i, options); +template +size_t encode_base64(char *dst, const char *src, size_t srclen, + base64_options options) { + return encode_base64_impl(dst, src, srclen, options); } // --- decoding ----------------------------------------------- @@ -18360,40 +18159,6 @@ template struct buf_block_reader { size_t idx; }; -// Routines to print masks and text for debugging bitmask operations -simdutf_unused static char *format_input_text_64(const uint8_t *text) { - static char *buf = - reinterpret_cast(malloc(sizeof(simd8x64) + 1)); - for (size_t i = 0; i < sizeof(simd8x64); i++) { - buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -// Routines to print masks and text for debugging bitmask operations -simdutf_unused static char *format_input_text(const simd8x64 &in) { - static char *buf = - reinterpret_cast(malloc(sizeof(simd8x64) + 1)); - in.store(reinterpret_cast(buf)); - for (size_t i = 0; i < sizeof(simd8x64); i++) { - if (buf[i] < ' ') { - buf[i] = '_'; - } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -simdutf_unused static char *format_mask(uint64_t mask) { - static char *buf = reinterpret_cast(malloc(64 + 1)); - for (size_t i = 0; i < 64; i++) { - buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; - } - buf[64] = '\0'; - return buf; -} - template simdutf_really_inline buf_block_reader::buf_block_reader(const uint8_t *_buf, size_t _len) @@ -18496,7 +18261,7 @@ compress_decode_base64(char *dst, const chartype *src, size_t srclen, size_t full_input_length = ri.full_input_length; if (srclen == 0) { if (!ignore_garbage && equalsigns > 0) { - return {INVALID_BASE64_CHARACTER, equallocation, 0}; + return {INVALID_BASE64_CHARACTER, equallocation, 0, true}; } return {SUCCESS, full_input_length, 0}; } @@ -18663,7 +18428,8 @@ compress_decode_base64(char *dst, const chartype *src, size_t srclen, if (!ignore_garbage && equalsigns > 0) { if ((size_t(dst - dstinit) % 3 == 0) || ((size_t(dst - dstinit) % 3) + 1 + equalsigns != 4)) { - return {INVALID_BASE64_CHARACTER, equallocation, size_t(dst - dstinit)}; + return {INVALID_BASE64_CHARACTER, equallocation, size_t(dst - dstinit), + true}; } } return {SUCCESS, srclen, size_t(dst - dstinit)}; @@ -18751,6 +18517,71 @@ find(const char16_t *start, const char16_t *end, char16_t character) noexcept { } // namespace westmere } // namespace simdutf /* end file src/generic/find.h */ +/* begin file src/generic/base64lengths.h */ +namespace simdutf { +namespace westmere { +namespace { +namespace base64_lengths { + +simdutf_warn_unused size_t binary_length_from_base64(const char *input, + size_t length) { + size_t pos = 0; + size_t count = 0; + for (; pos + 64 <= length; pos += 64) { + simd8x64 block(reinterpret_cast(input + pos)); + uint64_t maybe_base64 = block.gteq(33); // >= 33 which is '!' in ASCII + count += count_ones(maybe_base64); + } + while (pos < length) { + count += (input[pos] > 0x20) ? 1 : 0; + pos++; + } + // Count padding at the end. + size_t padding = 0; + pos = length; + while (pos > 0 && padding < 2) { + char c = input[--pos]; + if (c == '=') { + padding++; + } else if (c > ' ') { + break; + } + } + return ((count - padding) * 3) / 4; +} + +simdutf_warn_unused size_t binary_length_from_base64(const char16_t *input, + size_t length) { + size_t pos = 0; + size_t count = 0; + for (; pos + 32 <= length; pos += 32) { + simd16x32 block(reinterpret_cast(input + pos)); + uint64_t maybe_base64 = block.gteq(33); // >= 33 which is '!' in ASCII + count += count_ones(maybe_base64); + } + while (pos < length) { + count += (input[pos] > 0x20) ? 1 : 0; + pos++; + } + // Count padding at the end. + size_t padding = 0; + pos = length; + while (pos > 0 && padding < 2) { + char16_t c = input[--pos]; + if (c == '=') { + padding++; + } else if (c > ' ') { + break; + } + } + return ((count - padding) * 3) / 4; +} + +} // namespace base64_lengths +} // unnamed namespace +} // namespace westmere +} // namespace simdutf +/* end file src/generic/base64lengths.h */ // // Implementation-specific overrides @@ -18889,6 +18720,19 @@ size_t implementation::binary_to_base64(const char *input, size_t length, } } +size_t implementation::binary_to_base64_with_lines( + const char *input, size_t length, char *output, size_t line_length, + base64_options options) const noexcept { + if (options & base64_url) { + return encode_base64_impl(output, input, length, options, + line_length); + + } else { + return encode_base64_impl(output, input, length, options, + line_length); + } +} + const char *implementation::find(const char *start, const char *end, char character) const noexcept { return util::find(start, end, character); @@ -18899,6 +18743,16 @@ const char16_t *implementation::find(const char16_t *start, const char16_t *end, return util::find(start, end, character); } +simdutf_warn_unused size_t implementation::binary_length_from_base64( + const char *input, size_t length) const noexcept { + return base64_lengths::binary_length_from_base64(input, length); +} + +simdutf_warn_unused size_t implementation::binary_length_from_base64( + const char16_t *input, size_t length) const noexcept { + return base64_lengths::binary_length_from_base64(input, length); +} + } // namespace westmere } // namespace simdutf @@ -18913,22 +18767,28 @@ SIMDUTF_UNTARGET_REGION /* end file src/simdutf/westmere/end.h */ /* end file src/westmere/implementation.cpp */ #endif -#if SIMDUTF_IMPLEMENTATION_LSX -/* begin file src/lsx/implementation.cpp */ -/* begin file src/simdutf/lsx/begin.h */ -// redefining SIMDUTF_IMPLEMENTATION to "lsx" -// #define SIMDUTF_IMPLEMENTATION lsx +#if SIMDUTF_IMPLEMENTATION_LASX +/* begin file src/lasx/implementation.cpp */ +/* begin file src/simdutf/lasx/begin.h */ +// redefining SIMDUTF_IMPLEMENTATION to "lasx" +// #define SIMDUTF_IMPLEMENTATION lasx #define SIMDUTF_SIMD_HAS_UNSIGNED_CMP 1 -/* end file src/simdutf/lsx/begin.h */ + +#if SIMDUTF_CAN_ALWAYS_RUN_LASX +// nothing needed. +#else +SIMDUTF_TARGET_LASX +#endif +/* end file src/simdutf/lasx/begin.h */ namespace simdutf { -namespace lsx { +namespace lasx { namespace { -#ifndef SIMDUTF_LSX_H - #error "lsx.h must be included" +#ifndef SIMDUTF_LASX_H + #error "lasx.h must be included" #endif using namespace simd; -/* begin file src/lsx/lsx_base64.cpp */ +/* begin file src/lasx/lasx_base64.cpp */ /** * References and further reading: * @@ -18969,100 +18829,118 @@ size_t encode_base64(char *dst, const char *src, size_t srclen, : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; uint8_t *out = (uint8_t *)dst; - v16u8 shuf; - __m128i v_fc0fc00, v_3f03f0, shift_r, shift_l, base64_tbl0, base64_tbl1, + v32u8 shuf; + __m256i v_fc0fc00, v_3f03f0, shift_r, shift_l, base64_tbl0, base64_tbl1, base64_tbl2, base64_tbl3; - if (srclen >= 16) { - shuf = v16u8{1, 0, 2, 1, 4, 3, 5, 4, 7, 6, 8, 7, 10, 9, 11, 10}; - v_fc0fc00 = __lsx_vreplgr2vr_w(uint32_t(0x0fc0fc00)); - v_3f03f0 = __lsx_vreplgr2vr_w(uint32_t(0x003f03f0)); - shift_r = __lsx_vreplgr2vr_w(uint32_t(0x0006000a)); - shift_l = __lsx_vreplgr2vr_w(uint32_t(0x00080004)); - base64_tbl0 = __lsx_vld(lookup_tbl, 0); - base64_tbl1 = __lsx_vld(lookup_tbl, 16); - base64_tbl2 = __lsx_vld(lookup_tbl, 32); - base64_tbl3 = __lsx_vld(lookup_tbl, 48); - } + if (srclen >= 28) { + shuf = v32u8{1, 0, 2, 1, 4, 3, 5, 4, 7, 6, 8, 7, 10, 9, 11, 10, + 1, 0, 2, 1, 4, 3, 5, 4, 7, 6, 8, 7, 10, 9, 11, 10}; + v_fc0fc00 = __lasx_xvreplgr2vr_w(uint32_t(0x0fc0fc00)); + v_3f03f0 = __lasx_xvreplgr2vr_w(uint32_t(0x003f03f0)); + shift_r = __lasx_xvreplgr2vr_w(uint32_t(0x0006000a)); + shift_l = __lasx_xvreplgr2vr_w(uint32_t(0x00080004)); + base64_tbl0 = ____m256i(__lsx_vld(lookup_tbl, 0)); + base64_tbl1 = ____m256i(__lsx_vld(lookup_tbl, 16)); + base64_tbl2 = ____m256i(__lsx_vld(lookup_tbl, 32)); + base64_tbl3 = ____m256i(__lsx_vld(lookup_tbl, 48)); + } size_t i = 0; - for (; i + 52 <= srclen; i += 48) { - __m128i in0 = + for (; i + 100 <= srclen; i += 96) { + __m128i in0_lo = __lsx_vld(reinterpret_cast(input + i), 4 * 3 * 0); - __m128i in1 = + __m128i in0_hi = __lsx_vld(reinterpret_cast(input + i), 4 * 3 * 1); - __m128i in2 = + __m128i in1_lo = __lsx_vld(reinterpret_cast(input + i), 4 * 3 * 2); - __m128i in3 = + __m128i in1_hi = __lsx_vld(reinterpret_cast(input + i), 4 * 3 * 3); + __m128i in2_lo = + __lsx_vld(reinterpret_cast(input + i), 4 * 3 * 4); + __m128i in2_hi = + __lsx_vld(reinterpret_cast(input + i), 4 * 3 * 5); + __m128i in3_lo = + __lsx_vld(reinterpret_cast(input + i), 4 * 3 * 6); + __m128i in3_hi = + __lsx_vld(reinterpret_cast(input + i), 4 * 3 * 7); - in0 = __lsx_vshuf_b(in0, in0, (__m128i)shuf); - in1 = __lsx_vshuf_b(in1, in1, (__m128i)shuf); - in2 = __lsx_vshuf_b(in2, in2, (__m128i)shuf); - in3 = __lsx_vshuf_b(in3, in3, (__m128i)shuf); + __m256i in0 = lasx_set_q(in0_hi, in0_lo); + __m256i in1 = lasx_set_q(in1_hi, in1_lo); + __m256i in2 = lasx_set_q(in2_hi, in2_lo); + __m256i in3 = lasx_set_q(in3_hi, in3_lo); + + in0 = __lasx_xvshuf_b(in0, in0, (__m256i)shuf); + in1 = __lasx_xvshuf_b(in1, in1, (__m256i)shuf); + in2 = __lasx_xvshuf_b(in2, in2, (__m256i)shuf); + in3 = __lasx_xvshuf_b(in3, in3, (__m256i)shuf); - __m128i t0_0 = __lsx_vand_v(in0, v_fc0fc00); - __m128i t0_1 = __lsx_vand_v(in1, v_fc0fc00); - __m128i t0_2 = __lsx_vand_v(in2, v_fc0fc00); - __m128i t0_3 = __lsx_vand_v(in3, v_fc0fc00); + __m256i t0_0 = __lasx_xvand_v(in0, v_fc0fc00); + __m256i t0_1 = __lasx_xvand_v(in1, v_fc0fc00); + __m256i t0_2 = __lasx_xvand_v(in2, v_fc0fc00); + __m256i t0_3 = __lasx_xvand_v(in3, v_fc0fc00); - __m128i t1_0 = __lsx_vsrl_h(t0_0, shift_r); - __m128i t1_1 = __lsx_vsrl_h(t0_1, shift_r); - __m128i t1_2 = __lsx_vsrl_h(t0_2, shift_r); - __m128i t1_3 = __lsx_vsrl_h(t0_3, shift_r); + __m256i t1_0 = __lasx_xvsrl_h(t0_0, shift_r); + __m256i t1_1 = __lasx_xvsrl_h(t0_1, shift_r); + __m256i t1_2 = __lasx_xvsrl_h(t0_2, shift_r); + __m256i t1_3 = __lasx_xvsrl_h(t0_3, shift_r); - __m128i t2_0 = __lsx_vand_v(in0, v_3f03f0); - __m128i t2_1 = __lsx_vand_v(in1, v_3f03f0); - __m128i t2_2 = __lsx_vand_v(in2, v_3f03f0); - __m128i t2_3 = __lsx_vand_v(in3, v_3f03f0); + __m256i t2_0 = __lasx_xvand_v(in0, v_3f03f0); + __m256i t2_1 = __lasx_xvand_v(in1, v_3f03f0); + __m256i t2_2 = __lasx_xvand_v(in2, v_3f03f0); + __m256i t2_3 = __lasx_xvand_v(in3, v_3f03f0); - __m128i t3_0 = __lsx_vsll_h(t2_0, shift_l); - __m128i t3_1 = __lsx_vsll_h(t2_1, shift_l); - __m128i t3_2 = __lsx_vsll_h(t2_2, shift_l); - __m128i t3_3 = __lsx_vsll_h(t2_3, shift_l); + __m256i t3_0 = __lasx_xvsll_h(t2_0, shift_l); + __m256i t3_1 = __lasx_xvsll_h(t2_1, shift_l); + __m256i t3_2 = __lasx_xvsll_h(t2_2, shift_l); + __m256i t3_3 = __lasx_xvsll_h(t2_3, shift_l); - __m128i input0 = __lsx_vor_v(t1_0, t3_0); - __m128i input0_shuf0 = __lsx_vshuf_b(base64_tbl1, base64_tbl0, input0); - __m128i input0_shuf1 = __lsx_vshuf_b(base64_tbl3, base64_tbl2, - __lsx_vsub_b(input0, __lsx_vldi(32))); - __m128i input0_mask = __lsx_vslei_bu(input0, 31); - __m128i input0_result = - __lsx_vbitsel_v(input0_shuf1, input0_shuf0, input0_mask); - __lsx_vst(input0_result, reinterpret_cast<__m128i *>(out), 0); - out += 16; + __m256i input0 = __lasx_xvor_v(t1_0, t3_0); + __m256i input0_shuf0 = __lasx_xvshuf_b(base64_tbl1, base64_tbl0, input0); + __m256i input0_shuf1 = __lasx_xvshuf_b( + base64_tbl3, base64_tbl2, __lasx_xvsub_b(input0, __lasx_xvldi(32))); + __m256i input0_mask = __lasx_xvslei_bu(input0, 31); + __m256i input0_result = + __lasx_xvbitsel_v(input0_shuf1, input0_shuf0, input0_mask); + __lasx_xvst(input0_result, reinterpret_cast<__m256i *>(out), 0); + out += 32; - __m128i input1 = __lsx_vor_v(t1_1, t3_1); - __m128i input1_shuf0 = __lsx_vshuf_b(base64_tbl1, base64_tbl0, input1); - __m128i input1_shuf1 = __lsx_vshuf_b(base64_tbl3, base64_tbl2, - __lsx_vsub_b(input1, __lsx_vldi(32))); - __m128i input1_mask = __lsx_vslei_bu(input1, 31); - __m128i input1_result = - __lsx_vbitsel_v(input1_shuf1, input1_shuf0, input1_mask); - __lsx_vst(input1_result, reinterpret_cast<__m128i *>(out), 0); - out += 16; + __m256i input1 = __lasx_xvor_v(t1_1, t3_1); + __m256i input1_shuf0 = __lasx_xvshuf_b(base64_tbl1, base64_tbl0, input1); + __m256i input1_shuf1 = __lasx_xvshuf_b( + base64_tbl3, base64_tbl2, __lasx_xvsub_b(input1, __lasx_xvldi(32))); + __m256i input1_mask = __lasx_xvslei_bu(input1, 31); + __m256i input1_result = + __lasx_xvbitsel_v(input1_shuf1, input1_shuf0, input1_mask); + __lasx_xvst(input1_result, reinterpret_cast<__m256i *>(out), 0); + out += 32; - __m128i input2 = __lsx_vor_v(t1_2, t3_2); - __m128i input2_shuf0 = __lsx_vshuf_b(base64_tbl1, base64_tbl0, input2); - __m128i input2_shuf1 = __lsx_vshuf_b(base64_tbl3, base64_tbl2, - __lsx_vsub_b(input2, __lsx_vldi(32))); - __m128i input2_mask = __lsx_vslei_bu(input2, 31); - __m128i input2_result = - __lsx_vbitsel_v(input2_shuf1, input2_shuf0, input2_mask); - __lsx_vst(input2_result, reinterpret_cast<__m128i *>(out), 0); - out += 16; + __m256i input2 = __lasx_xvor_v(t1_2, t3_2); + __m256i input2_shuf0 = __lasx_xvshuf_b(base64_tbl1, base64_tbl0, input2); + __m256i input2_shuf1 = __lasx_xvshuf_b( + base64_tbl3, base64_tbl2, __lasx_xvsub_b(input2, __lasx_xvldi(32))); + __m256i input2_mask = __lasx_xvslei_bu(input2, 31); + __m256i input2_result = + __lasx_xvbitsel_v(input2_shuf1, input2_shuf0, input2_mask); + __lasx_xvst(input2_result, reinterpret_cast<__m256i *>(out), 0); + out += 32; - __m128i input3 = __lsx_vor_v(t1_3, t3_3); - __m128i input3_shuf0 = __lsx_vshuf_b(base64_tbl1, base64_tbl0, input3); - __m128i input3_shuf1 = __lsx_vshuf_b(base64_tbl3, base64_tbl2, - __lsx_vsub_b(input3, __lsx_vldi(32))); - __m128i input3_mask = __lsx_vslei_bu(input3, 31); - __m128i input3_result = - __lsx_vbitsel_v(input3_shuf1, input3_shuf0, input3_mask); - __lsx_vst(input3_result, reinterpret_cast<__m128i *>(out), 0); - out += 16; + __m256i input3 = __lasx_xvor_v(t1_3, t3_3); + __m256i input3_shuf0 = __lasx_xvshuf_b(base64_tbl1, base64_tbl0, input3); + __m256i input3_shuf1 = __lasx_xvshuf_b( + base64_tbl3, base64_tbl2, __lasx_xvsub_b(input3, __lasx_xvldi(32))); + __m256i input3_mask = __lasx_xvslei_bu(input3, 31); + __m256i input3_result = + __lasx_xvbitsel_v(input3_shuf1, input3_shuf0, input3_mask); + __lasx_xvst(input3_result, reinterpret_cast<__m256i *>(out), 0); + out += 32; } - for (; i + 16 <= srclen; i += 12) { + for (; i + 28 <= srclen; i += 24) { - __m128i in = __lsx_vld(reinterpret_cast(input + i), 0); + __m128i in_lo = __lsx_vld(reinterpret_cast(input + i), 0); + __m128i in_hi = + __lsx_vld(reinterpret_cast(input + i), 4 * 3 * 1); + + __m256i in = lasx_set_q(in_hi, in_lo); // bytes from groups A, B and C are needed in separate 32-bit lanes // in = [DDDD|CCCC|BBBB|AAAA] @@ -19076,33 +18954,32 @@ size_t encode_base64(char *dst, const char *src, size_t srclen, // [bbbbcccc|ccdddddd|aaaaaabb|bbbbcccc] // ^^^^ ^^^^^^^^ ^^^^^^^^ ^^^^ // processed bits - in = __lsx_vshuf_b(in, in, (__m128i)shuf); + in = __lasx_xvshuf_b(in, in, (__m256i)shuf); // unpacking // t0 = [0000cccc|cc000000|aaaaaa00|00000000] - __m128i t0 = __lsx_vand_v(in, v_fc0fc00); + __m256i t0 = __lasx_xvand_v(in, v_fc0fc00); // t1 = [00000000|00cccccc|00000000|00aaaaaa] // ((c >> 6), (a >> 10)) - __m128i t1 = __lsx_vsrl_h(t0, shift_r); + __m256i t1 = __lasx_xvsrl_h(t0, shift_r); // t2 = [00000000|00dddddd|000000bb|bbbb0000] - __m128i t2 = __lsx_vand_v(in, v_3f03f0); + __m256i t2 = __lasx_xvand_v(in, v_3f03f0); // t3 = [00dddddd|00000000|00bbbbbb|00000000] // ((d << 8), (b << 4)) - __m128i t3 = __lsx_vsll_h(t2, shift_l); + __m256i t3 = __lasx_xvsll_h(t2, shift_l); // res = [00dddddd|00cccccc|00bbbbbb|00aaaaaa] = t1 | t3 - __m128i indices = __lsx_vor_v(t1, t3); - - __m128i indices_shuf0 = __lsx_vshuf_b(base64_tbl1, base64_tbl0, indices); - __m128i indices_shuf1 = __lsx_vshuf_b( - base64_tbl3, base64_tbl2, __lsx_vsub_b(indices, __lsx_vldi(32))); - __m128i indices_mask = __lsx_vslei_bu(indices, 31); - __m128i indices_result = - __lsx_vbitsel_v(indices_shuf1, indices_shuf0, indices_mask); + __m256i indices = __lasx_xvor_v(t1, t3); - __lsx_vst(indices_result, reinterpret_cast<__m128i *>(out), 0); - out += 16; + __m256i indices_shuf0 = __lasx_xvshuf_b(base64_tbl1, base64_tbl0, indices); + __m256i indices_shuf1 = __lasx_xvshuf_b( + base64_tbl3, base64_tbl2, __lasx_xvsub_b(indices, __lasx_xvldi(32))); + __m256i indices_mask = __lasx_xvslei_bu(indices, 31); + __m256i indices_result = + __lasx_xvbitsel_v(indices_shuf1, indices_shuf0, indices_mask); + __lasx_xvst(indices_result, reinterpret_cast<__m256i *>(out), 0); + out += 32; } return i / 3 * 4 + scalar::base64::tail_encode_base64((char *)out, src + i, @@ -19126,7 +19003,7 @@ static inline void compress(__m128i data, uint16_t mask, char *output) { tables::base64::thintable_epi8[mask2]}; // we increment by 0x08 the second half of the mask - v4u32 hi = {0, 0, 0x08080808, 0x08080808}; + const v4u32 hi = {0, 0, 0x08080808, 0x08080808}; __m128i shufmask1 = __lsx_vadd_b((__m128i)shufmask, (__m128i)hi); // this is the version "nearly pruned" @@ -19148,115 +19025,111 @@ static inline void compress(__m128i data, uint16_t mask, char *output) { } struct block64 { - __m128i chunks[4]; + __m256i chunks[2]; }; template -static inline uint16_t to_base64_mask(__m128i *src, bool *error) { - const v16u8 ascii_space_tbl = {0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x9, 0xa, 0x0, 0xc, 0xd, 0x0, 0x0}; +static inline uint32_t to_base64_mask(__m256i *src, bool *error) { + __m256i ascii_space_tbl = + ____m256i((__m128i)v16u8{0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x9, 0xa, 0x0, 0xc, 0xd, 0x0, 0x0}); // credit: aqrit - /* - '0'(0x30)-'9'(0x39) => delta_values_index = 4 - 'A'(0x41)-'Z'(0x5a) => delta_values_index = 4/5/12(4+8) - 'a'(0x61)-'z'(0x7a) => delta_values_index = 6/7/14(6+8) - '+'(0x2b) => delta_values_index = 3 - '/'(0x2f) => delta_values_index = 2+8 = 10 - '-'(0x2d) => delta_values_index = 2+8 = 10 - '_'(0x5f) => delta_values_index = 5+8 = 13 - */ - v16u8 delta_asso; + __m256i delta_asso; if (default_or_url) { - delta_asso = v16u8{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x16}; + delta_asso = + ____m256i((__m128i)v16u8{0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x11, 0x0, 0x16}); } else { - delta_asso = v16u8{0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, - 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF}; + delta_asso = + ____m256i((__m128i)v16u8{0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, + 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF}); } - v16i8 delta_values; + __m256i delta_values; if (default_or_url) { - delta_values = - v16i8{int8_t(0xBF), int8_t(0xE0), int8_t(0xB9), int8_t(0x13), - int8_t(0x04), int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), - int8_t(0xB9), int8_t(0x00), int8_t(0xFF), int8_t(0x11), - int8_t(0xFF), int8_t(0xBF), int8_t(0x10), int8_t(0xB9)}; + delta_values = ____m256i( + (__m128i)v16i8{int8_t(0xBF), int8_t(0xE0), int8_t(0xB9), int8_t(0x13), + int8_t(0x04), int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), + int8_t(0xB9), int8_t(0x00), int8_t(0xFF), int8_t(0x11), + int8_t(0xFF), int8_t(0xBF), int8_t(0x10), int8_t(0xB9)}); } else if (base64_url) { - delta_values = - v16i8{int8_t(0x00), int8_t(0x00), int8_t(0x00), int8_t(0x13), - int8_t(0x04), int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), - int8_t(0xB9), int8_t(0x00), int8_t(0x11), int8_t(0xC3), - int8_t(0xBF), int8_t(0xE0), int8_t(0xB9), int8_t(0xB9)}; + delta_values = ____m256i( + (__m128i)v16i8{int8_t(0x00), int8_t(0x00), int8_t(0x00), int8_t(0x13), + int8_t(0x04), int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), + int8_t(0xB9), int8_t(0x00), int8_t(0x11), int8_t(0xC3), + int8_t(0xBF), int8_t(0xE0), int8_t(0xB9), int8_t(0xB9)}); } else { - delta_values = - v16i8{int8_t(0x00), int8_t(0x00), int8_t(0x00), int8_t(0x13), - int8_t(0x04), int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), - int8_t(0xB9), int8_t(0x00), int8_t(0x10), int8_t(0xC3), - int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), int8_t(0xB9)}; + delta_values = ____m256i( + (__m128i)v16i8{int8_t(0x00), int8_t(0x00), int8_t(0x00), int8_t(0x13), + int8_t(0x04), int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), + int8_t(0xB9), int8_t(0x00), int8_t(0x10), int8_t(0xC3), + int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), int8_t(0xB9)}); } - v16u8 check_asso; + __m256i check_asso; if (default_or_url) { - check_asso = v16u8{0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x03, 0x07, 0x0B, 0x0E, 0x0B, 0x06}; + check_asso = ____m256i((__m128i)v16u8{0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x03, 0x07, + 0x0B, 0x0E, 0x0B, 0x06}); + } else if (base64_url) { - check_asso = v16u8{0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x03, 0x07, 0x0B, 0x06, 0x0B, 0x12}; + check_asso = ____m256i((__m128i)v16u8{0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x03, 0x07, + 0x0B, 0x06, 0x0B, 0x12}); } else { - check_asso = v16u8{0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x03, 0x07, 0x0B, 0x0B, 0x0B, 0x0F}; + check_asso = ____m256i((__m128i)v16u8{0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x03, 0x07, + 0x0B, 0x0B, 0x0B, 0x0F}); } - v16i8 check_values; + __m256i check_values; if (default_or_url) { - check_values = - v16i8{int8_t(0x80), int8_t(0x80), int8_t(0x80), int8_t(0x80), - int8_t(0xCF), int8_t(0xBF), int8_t(0xD5), int8_t(0xA6), - int8_t(0xB5), int8_t(0xA1), int8_t(0x00), int8_t(0x80), - int8_t(0x00), int8_t(0x80), int8_t(0x00), int8_t(0x80)}; + + check_values = ____m256i( + (__m128i)v16i8{int8_t(0x80), int8_t(0x80), int8_t(0x80), int8_t(0x80), + int8_t(0xCF), int8_t(0xBF), int8_t(0xD5), int8_t(0xA6), + int8_t(0xB5), int8_t(0xA1), int8_t(0x00), int8_t(0x80), + int8_t(0x00), int8_t(0x80), int8_t(0x00), int8_t(0x80)}); } else if (base64_url) { - check_values = v16i8{int8_t(0x0), int8_t(0x80), int8_t(0x80), int8_t(0x80), - int8_t(0xCF), int8_t(0xBF), int8_t(0xD3), int8_t(0xA6), - int8_t(0xB5), int8_t(0x86), int8_t(0xD0), int8_t(0x80), - int8_t(0xB0), int8_t(0x80), int8_t(0x0), int8_t(0x0)}; + check_values = ____m256i( + (__m128i)v16i8{int8_t(0x0), int8_t(0x80), int8_t(0x80), int8_t(0x80), + int8_t(0xCF), int8_t(0xBF), int8_t(0xD3), int8_t(0xA6), + int8_t(0xB5), int8_t(0x86), int8_t(0xD0), int8_t(0x80), + int8_t(0xB0), int8_t(0x80), int8_t(0x0), int8_t(0x0)}); } else { - check_values = - v16i8{int8_t(0x80), int8_t(0x80), int8_t(0x80), int8_t(0x80), - int8_t(0xCF), int8_t(0xBF), int8_t(0xD5), int8_t(0xA6), - int8_t(0xB5), int8_t(0x86), int8_t(0xD1), int8_t(0x80), - int8_t(0xB1), int8_t(0x80), int8_t(0x91), int8_t(0x80)}; + check_values = ____m256i( + (__m128i)v16i8{int8_t(0x80), int8_t(0x80), int8_t(0x80), int8_t(0x80), + int8_t(0xCF), int8_t(0xBF), int8_t(0xD5), int8_t(0xA6), + int8_t(0xB5), int8_t(0x86), int8_t(0xD1), int8_t(0x80), + int8_t(0xB1), int8_t(0x80), int8_t(0x91), int8_t(0x80)}); } - const __m128i shifted = __lsx_vsrli_b(*src, 3); - __m128i asso_index = __lsx_vand_v(*src, __lsx_vldi(0xF)); - const __m128i delta_hash = - __lsx_vavgr_bu(__lsx_vshuf_b((__m128i)delta_asso, (__m128i)delta_asso, - (__m128i)asso_index), - shifted); - const __m128i check_hash = - __lsx_vavgr_bu(__lsx_vshuf_b((__m128i)check_asso, (__m128i)check_asso, - (__m128i)asso_index), - shifted); + __m256i shifted = __lasx_xvsrli_b(*src, 3); + __m256i asso_index = __lasx_xvand_v(*src, __lasx_xvldi(0xF)); + __m256i delta_hash = __lasx_xvavgr_bu( + __lasx_xvshuf_b(delta_asso, delta_asso, asso_index), shifted); + __m256i check_hash = __lasx_xvavgr_bu( + __lasx_xvshuf_b(check_asso, check_asso, asso_index), shifted); - const __m128i out = - __lsx_vsadd_b(__lsx_vshuf_b((__m128i)delta_values, (__m128i)delta_values, - (__m128i)delta_hash), - *src); - const __m128i chk = - __lsx_vsadd_b(__lsx_vshuf_b((__m128i)check_values, (__m128i)check_values, - (__m128i)check_hash), - *src); - unsigned int mask = __lsx_vpickve2gr_hu(__lsx_vmskltz_b(chk), 0); + __m256i out = __lasx_xvsadd_b( + __lasx_xvshuf_b(delta_values, delta_values, delta_hash), *src); + __m256i chk = __lasx_xvsadd_b( + __lasx_xvshuf_b(check_values, check_values, check_hash), *src); + __m256i chk_ltz = __lasx_xvmskltz_b(chk); + unsigned int mask = __lasx_xvpickve2gr_wu(chk_ltz, 0); + mask = mask | (__lsx_vpickve2gr_hu(lasx_extracti128_hi(chk_ltz), 0) << 16); if (mask) { - __m128i ascii_space = __lsx_vseq_b(__lsx_vshuf_b((__m128i)ascii_space_tbl, - (__m128i)ascii_space_tbl, - (__m128i)asso_index), - *src); - *error |= - (mask != __lsx_vpickve2gr_hu(__lsx_vmskltz_b((__m128i)ascii_space), 0)); + __m256i ascii_space = __lasx_xvseq_b( + __lasx_xvshuf_b(ascii_space_tbl, ascii_space_tbl, asso_index), *src); + __m256i ascii_space_ltz = __lasx_xvmskltz_b(ascii_space); + unsigned int ascii_space_mask = __lasx_xvpickve2gr_wu(ascii_space_ltz, 0); + ascii_space_mask = + ascii_space_mask | + (__lsx_vpickve2gr_hu(lasx_extracti128_hi(ascii_space_ltz), 0) << 16); + *error |= (mask != ascii_space_mask); } *src = out; - return (uint16_t)mask; + return (uint32_t)mask; } template @@ -19266,18 +19139,12 @@ static inline uint64_t to_base64_mask(block64 *b, bool *error) { to_base64_mask(&b->chunks[0], error); uint64_t m1 = to_base64_mask(&b->chunks[1], error); - uint64_t m2 = - to_base64_mask(&b->chunks[2], error); - uint64_t m3 = - to_base64_mask(&b->chunks[3], error); - return m0 | (m1 << 16) | (m2 << 32) | (m3 << 48); + return m0 | (m1 << 32); } static inline void copy_block(block64 *b, char *output) { - __lsx_vst(b->chunks[0], reinterpret_cast<__m128i *>(output), 0); - __lsx_vst(b->chunks[1], reinterpret_cast<__m128i *>(output), 16); - __lsx_vst(b->chunks[2], reinterpret_cast<__m128i *>(output), 32); - __lsx_vst(b->chunks[3], reinterpret_cast<__m128i *>(output), 48); + __lasx_xvst(b->chunks[0], reinterpret_cast<__m256i *>(output), 0); + __lasx_xvst(b->chunks[1], reinterpret_cast<__m256i *>(output), 32); } static inline uint64_t compress_block(block64 *b, uint64_t mask, char *output) { @@ -19285,84 +19152,150 @@ static inline uint64_t compress_block(block64 *b, uint64_t mask, char *output) { uint64_t count = __lsx_vpickve2gr_d(__lsx_vpcnt_h(__lsx_vreplgr2vr_d(nmask)), 0); uint16_t *count_ptr = (uint16_t *)&count; - compress(b->chunks[0], uint16_t(mask), output); - compress(b->chunks[1], uint16_t(mask >> 16), output + count_ptr[0]); - compress(b->chunks[2], uint16_t(mask >> 32), + compress(lasx_extracti128_lo(b->chunks[0]), uint16_t(mask), output); + compress(lasx_extracti128_hi(b->chunks[0]), uint16_t(mask >> 16), + output + count_ptr[0]); + compress(lasx_extracti128_lo(b->chunks[1]), uint16_t(mask >> 32), output + count_ptr[0] + count_ptr[1]); - compress(b->chunks[3], uint16_t(mask >> 48), + compress(lasx_extracti128_hi(b->chunks[1]), uint16_t(mask >> 48), output + count_ptr[0] + count_ptr[1] + count_ptr[2]); return count_ones(nmask); } +template bool is_power_of_two(T x) { return (x & (x - 1)) == 0; } + +inline size_t compress_block_single(block64 *b, uint64_t mask, char *output) { + const size_t pos64 = trailing_zeroes(mask); + const int8_t pos = pos64 & 0xf; + + // Predefine the index vector + const v16u8 v1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + + switch (pos64 >> 4) { + case 0b00: { + const __m128i lane0 = lasx_extracti128_lo(b->chunks[0]); + const __m128i lane1 = lasx_extracti128_hi(b->chunks[0]); + + const __m128i v0 = __lsx_vreplgr2vr_b((uint8_t)(pos - 1)); + const __m128i v2 = __lsx_vslt_b(v0, (__m128i)v1); // v1 > v0 + const __m128i sh = __lsx_vsub_b((__m128i)v1, v2); + const __m128i compressed = __lsx_vshuf_b(lane0, lane0, sh); + + __lsx_vst(compressed, reinterpret_cast<__m128i *>(output + 0 * 16), 0); + __lsx_vst(lane1, reinterpret_cast<__m128i *>(output + 1 * 16 - 1), 0); + __lasx_xvst(b->chunks[1], reinterpret_cast<__m256i *>(output + 2 * 16 - 1), + 0); + } break; + case 0b01: { + const __m128i lane0 = lasx_extracti128_lo(b->chunks[0]); + const __m128i lane1 = lasx_extracti128_hi(b->chunks[0]); + __lsx_vst(lane0, reinterpret_cast<__m128i *>(output + 0 * 16), 0); + + const __m128i v0 = __lsx_vreplgr2vr_b((uint8_t)(pos - 1)); + const __m128i v2 = __lsx_vslt_b(v0, (__m128i)v1); + const __m128i sh = __lsx_vsub_b((__m128i)v1, v2); + const __m128i compressed = __lsx_vshuf_b(lane1, lane1, sh); + + __lsx_vst(compressed, reinterpret_cast<__m128i *>(output + 1 * 16), 0); + __lasx_xvst(b->chunks[1], reinterpret_cast<__m256i *>(output + 2 * 16 - 1), + 0); + } break; + case 0b10: { + __lasx_xvst(b->chunks[0], reinterpret_cast<__m256i *>(output + 0 * 16), 0); + + const __m128i lane2 = lasx_extracti128_lo(b->chunks[1]); + const __m128i lane3 = lasx_extracti128_hi(b->chunks[1]); + + const __m128i v0 = __lsx_vreplgr2vr_b((uint8_t)(pos - 1)); + const __m128i v2 = __lsx_vslt_b(v0, (__m128i)v1); + const __m128i sh = __lsx_vsub_b((__m128i)v1, v2); + const __m128i compressed = __lsx_vshuf_b(lane2, lane2, sh); + + __lsx_vst(compressed, reinterpret_cast<__m128i *>(output + 2 * 16), 0); + __lsx_vst(lane3, reinterpret_cast<__m128i *>(output + 3 * 16 - 1), 0); + } break; + case 0b11: { + __lasx_xvst(b->chunks[0], reinterpret_cast<__m256i *>(output + 0 * 16), 0); + __lsx_vst(lasx_extracti128_lo(b->chunks[1]), + reinterpret_cast<__m128i *>(output + 2 * 16), 0); + + const __m128i lane3 = lasx_extracti128_hi(b->chunks[1]); + + const __m128i v0 = __lsx_vreplgr2vr_b((uint8_t)(pos - 1)); + const __m128i v2 = __lsx_vslt_b(v0, (__m128i)v1); + const __m128i sh = __lsx_vsub_b((__m128i)v1, v2); + const __m128i compressed = __lsx_vshuf_b(lane3, lane3, sh); + + __lsx_vst(compressed, reinterpret_cast<__m128i *>(output + 3 * 16), 0); + } break; + } + return 63; +} + // The caller of this function is responsible to ensure that there are 64 bytes // available from reading at src. The data is read into a block64 structure. static inline void load_block(block64 *b, const char *src) { - b->chunks[0] = __lsx_vld(reinterpret_cast(src), 0); - b->chunks[1] = __lsx_vld(reinterpret_cast(src), 16); - b->chunks[2] = __lsx_vld(reinterpret_cast(src), 32); - b->chunks[3] = __lsx_vld(reinterpret_cast(src), 48); + b->chunks[0] = __lasx_xvld(reinterpret_cast(src), 0); + b->chunks[1] = __lasx_xvld(reinterpret_cast(src), 32); } // The caller of this function is responsible to ensure that there are 128 bytes // available from reading at src. The data is read into a block64 structure. static inline void load_block(block64 *b, const char16_t *src) { - __m128i m1 = __lsx_vld(reinterpret_cast(src), 0); - __m128i m2 = __lsx_vld(reinterpret_cast(src), 16); - __m128i m3 = __lsx_vld(reinterpret_cast(src), 32); - __m128i m4 = __lsx_vld(reinterpret_cast(src), 48); - __m128i m5 = __lsx_vld(reinterpret_cast(src), 64); - __m128i m6 = __lsx_vld(reinterpret_cast(src), 80); - __m128i m7 = __lsx_vld(reinterpret_cast(src), 96); - __m128i m8 = __lsx_vld(reinterpret_cast(src), 112); - b->chunks[0] = __lsx_vssrlni_bu_h(m2, m1, 0); - b->chunks[1] = __lsx_vssrlni_bu_h(m4, m3, 0); - b->chunks[2] = __lsx_vssrlni_bu_h(m6, m5, 0); - b->chunks[3] = __lsx_vssrlni_bu_h(m8, m7, 0); + __m256i m1 = __lasx_xvld(reinterpret_cast(src), 0); + __m256i m2 = __lasx_xvld(reinterpret_cast(src), 32); + __m256i m3 = __lasx_xvld(reinterpret_cast(src), 64); + __m256i m4 = __lasx_xvld(reinterpret_cast(src), 96); + b->chunks[0] = __lasx_xvpermi_d(__lasx_xvssrlni_bu_h(m2, m1, 0), 0b11011000); + b->chunks[1] = __lasx_xvpermi_d(__lasx_xvssrlni_bu_h(m4, m3, 0), 0b11011000); } -static inline void base64_decode(char *out, __m128i str) { - __m128i t0 = __lsx_vor_v( - __lsx_vslli_w(str, 26), - __lsx_vslli_w(__lsx_vand_v(str, lsx_splat_u32(0x0000FF00)), 12)); - __m128i t1 = __lsx_vsrli_w(__lsx_vand_v(str, lsx_splat_u32(0x003F0000)), 2); - __m128i t2 = __lsx_vor_v(t0, t1); - __m128i t3 = __lsx_vor_v(t2, __lsx_vsrli_w(str, 16)); - const v16u8 pack_shuffle = {3, 2, 1, 7, 6, 5, 11, 10, - 9, 15, 14, 13, 0, 0, 0, 0}; - t3 = __lsx_vshuf_b(t3, t3, (__m128i)pack_shuffle); +static inline void base64_decode(char *out, __m256i str) { + __m256i t0 = __lasx_xvor_v( + __lasx_xvslli_w(str, 26), + __lasx_xvslli_w(__lasx_xvand_v(str, lasx_splat_u32(0x0000ff00)), 12)); + __m256i t1 = + __lasx_xvsrli_w(__lasx_xvand_v(str, lasx_splat_u32(0x003f0000)), 2); + __m256i t2 = __lasx_xvor_v(t0, t1); + __m256i t3 = __lasx_xvor_v(t2, __lasx_xvsrli_w(str, 16)); + __m256i pack_shuffle = ____m256i( + (__m128i)v16u8{3, 2, 1, 7, 6, 5, 11, 10, 9, 15, 14, 13, 0, 0, 0, 0}); + t3 = __lasx_xvshuf_b(t3, t3, (__m256i)pack_shuffle); // Store the output: - // we only need 12. - __lsx_vstelm_d(t3, out, 0, 0); - __lsx_vstelm_w(t3, out + 8, 0, 2); + __lsx_vst(lasx_extracti128_lo(t3), out, 0); + __lsx_vst(lasx_extracti128_hi(t3), out, 12); } // decode 64 bytes and output 48 bytes static inline void base64_decode_block(char *out, const char *src) { - base64_decode(out, __lsx_vld(reinterpret_cast(src), 0)); - base64_decode(out + 12, - __lsx_vld(reinterpret_cast(src), 16)); + base64_decode(out, __lasx_xvld(reinterpret_cast(src), 0)); base64_decode(out + 24, - __lsx_vld(reinterpret_cast(src), 32)); - base64_decode(out + 36, - __lsx_vld(reinterpret_cast(src), 48)); + __lasx_xvld(reinterpret_cast(src), 32)); } + static inline void base64_decode_block_safe(char *out, const char *src) { - base64_decode_block(out, src); + base64_decode(out, __lasx_xvld(reinterpret_cast(src), 0)); + alignas(32) char buffer[32]; + base64_decode(buffer, + __lasx_xvld(reinterpret_cast(src), 32)); + std::memcpy(out + 24, buffer, 24); } + static inline void base64_decode_block(char *out, block64 *b) { base64_decode(out, b->chunks[0]); - base64_decode(out + 12, b->chunks[1]); - base64_decode(out + 24, b->chunks[2]); - base64_decode(out + 36, b->chunks[3]); + base64_decode(out + 24, b->chunks[1]); } static inline void base64_decode_block_safe(char *out, block64 *b) { - base64_decode_block(out, b); + base64_decode(out, b->chunks[0]); + alignas(32) char buffer[32]; + base64_decode(buffer, b->chunks[1]); + std::memcpy(out + 24, buffer, 24); } template + typename chartype> full_result -compress_decode_base64(char *dst, const char_type *src, size_t srclen, +compress_decode_base64(char *dst, const chartype *src, size_t srclen, base64_options options, last_chunk_handling_options last_chunk_options) { const uint8_t *to_base64 = @@ -19380,15 +19313,19 @@ compress_decode_base64(char *dst, const char_type *src, size_t srclen, } return {SUCCESS, full_input_length, 0}; } - const char_type *const srcinit = src; + char *end_of_safe_64byte_zone = + (srclen + 3) / 4 * 3 >= 63 ? dst + (srclen + 3) / 4 * 3 - 63 : dst; + + const chartype *const srcinit = src; const char *const dstinit = dst; - const char_type *const srcend = src + srclen; + const chartype *const srcend = src + srclen; - constexpr size_t block_size = 10; + constexpr size_t block_size = 6; + static_assert(block_size >= 2, "block_size must be at least two"); char buffer[block_size * 64]; char *bufferptr = buffer; if (srclen >= 64) { - const char_type *const srcend64 = src + srclen - 64; + const chartype *const srcend64 = src + srclen - 64; while (src <= srcend64) { block64 b; load_block(&b, src); @@ -19396,48 +19333,56 @@ compress_decode_base64(char *dst, const char_type *src, size_t srclen, bool error = false; uint64_t badcharmask = to_base64_mask(&b, &error); - if (badcharmask) { - if (error && !ignore_garbage) { - src -= 64; - while (src < srcend && scalar::base64::is_eight_byte(*src) && - to_base64[uint8_t(*src)] <= 64) { - src++; - } - if (src < srcend) { - // should never happen - } - return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit), - size_t(dst - dstinit)}; + if (error && !ignore_garbage) { + src -= 64; + while (src < srcend && scalar::base64::is_eight_byte(*src) && + to_base64[uint8_t(*src)] <= 64) { + src++; } + return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit), + size_t(dst - dstinit)}; } - if (badcharmask != 0) { - // optimization opportunity: check for simple masks like those made of - // continuous 1s followed by continuous 0s. And masks containing a - // single bad character. - bufferptr += compress_block(&b, badcharmask, bufferptr); - } else { - // optimization opportunity: if bufferptr == buffer and mask == 0, we - // can avoid the call to compress_block and decode directly. + if (is_power_of_two(badcharmask)) { + bufferptr += compress_block_single(&b, badcharmask, bufferptr); + } else { + bufferptr += compress_block(&b, badcharmask, bufferptr); + } + } else if (bufferptr != buffer) { copy_block(&b, bufferptr); bufferptr += 64; + } else { + if (dst >= end_of_safe_64byte_zone) { + base64_decode_block_safe(dst, &b); + } else { + base64_decode_block(dst, &b); + } + dst += 48; } if (bufferptr >= (block_size - 1) * 64 + buffer) { - for (size_t i = 0; i < (block_size - 1); i++) { + for (size_t i = 0; i < (block_size - 2); i++) { base64_decode_block(dst, buffer + i * 64); dst += 48; } + if (dst >= end_of_safe_64byte_zone) { + base64_decode_block_safe(dst, buffer + (block_size - 2) * 64); + } else { + base64_decode_block(dst, buffer + (block_size - 2) * 64); + } + dst += 48; std::memcpy(buffer, buffer + (block_size - 1) * 64, 64); // 64 might be too much bufferptr -= (block_size - 1) * 64; } } } + char *buffer_start = buffer; // Optimization note: if this is almost full, then it is worth our // time, otherwise, we should just decode directly. int last_block = (int)((bufferptr - buffer_start) % 64); if (last_block != 0 && srcend - src + last_block >= 64) { + while ((bufferptr - buffer_start) % 64 != 0 && src < srcend) { uint8_t val = to_base64[uint8_t(*src)]; *bufferptr = char(val); @@ -19452,7 +19397,11 @@ compress_decode_base64(char *dst, const char_type *src, size_t srclen, } for (; buffer_start + 64 <= bufferptr; buffer_start += 64) { - base64_decode_block(dst, buffer_start); + if (dst >= end_of_safe_64byte_zone) { + base64_decode_block_safe(dst, buffer_start); + } else { + base64_decode_block(dst, buffer_start); + } dst += 48; } if ((bufferptr - buffer_start) % 64 != 0) { @@ -19462,6 +19411,7 @@ compress_decode_base64(char *dst, const char_type *src, size_t srclen, (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) + (uint32_t(uint8_t(buffer_start[3])) << 0 * 6)) << 8; + // lasx is little-endian triple = scalar::u32_swap_bytes(triple); std::memcpy(dst, &triple, 4); @@ -19474,6 +19424,7 @@ compress_decode_base64(char *dst, const char_type *src, size_t srclen, (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) + (uint32_t(uint8_t(buffer_start[3])) << 0 * 6)) << 8; + // lasx is little-endian triple = scalar::u32_swap_bytes(triple); std::memcpy(dst, &triple, 3); @@ -19533,22 +19484,24 @@ compress_decode_base64(char *dst, const char_type *src, size_t srclen, } return {SUCCESS, srclen, size_t(dst - dstinit)}; } -/* end file src/lsx/lsx_base64.cpp */ -/* begin file src/lsx/lsx_find.cpp */ +/* end file src/lasx/lasx_base64.cpp */ +/* begin file src/lasx/lasx_find.cpp */ simdutf_really_inline const char *util_find(const char *start, const char *end, char character) noexcept { if (start >= end) return end; - const int step = 16; - __m128i char_vec = __lsx_vreplgr2vr_b(static_cast(character)); + const int step = 32; + __m256i char_vec = __lasx_xvreplgr2vr_b(static_cast(character)); while (end - start >= step) { - __m128i data = __lsx_vld(reinterpret_cast(start), 0); - __m128i cmp = __lsx_vseq_b(data, char_vec); - if (__lsx_bnz_v(cmp)) { - uint16_t mask = - static_cast(__lsx_vpickve2gr_hu(__lsx_vmsknz_b(cmp), 0)); + __m256i data = __lasx_xvld(reinterpret_cast(start), 0); + __m256i cmp = __lasx_xvseq_b(data, char_vec); + if (__lasx_xbnz_v(cmp)) { + __m256i res = __lasx_xvmsknz_b(cmp); + uint32_t mask0 = __lasx_xvpickve2gr_wu(res, 0); + uint32_t mask1 = __lasx_xvpickve2gr_wu(res, 4); + uint32_t mask = (mask0 | (mask1 << 16)); return start + trailing_zeroes(mask); } @@ -19571,15 +19524,17 @@ simdutf_really_inline const char16_t *util_find(const char16_t *start, if (start >= end) return end; - const int step = 8; - __m128i char_vec = __lsx_vreplgr2vr_h(static_cast(character)); + const int step = 16; + __m256i char_vec = __lasx_xvreplgr2vr_h(static_cast(character)); while (end - start >= step) { - __m128i data = __lsx_vld(reinterpret_cast(start), 0); - __m128i cmp = __lsx_vseq_h(data, char_vec); - if (__lsx_bnz_v(cmp)) { - uint16_t mask = - static_cast(__lsx_vpickve2gr_hu(__lsx_vmsknz_b(cmp), 0)); + __m256i data = __lasx_xvld(reinterpret_cast(start), 0); + __m256i cmp = __lasx_xvseq_h(data, char_vec); + if (__lasx_xbnz_v(cmp)) { + __m256i res = __lasx_xvmsknz_b(cmp); + uint32_t mask0 = __lasx_xvpickve2gr_wu(res, 0); + uint32_t mask1 = __lasx_xvpickve2gr_wu(res, 4); + uint32_t mask = (mask0 | (mask1 << 16)); return start + trailing_zeroes(mask) / 2; } @@ -19595,15 +19550,15 @@ simdutf_really_inline const char16_t *util_find(const char16_t *start, return end; } -/* end file src/lsx/lsx_find.cpp */ +/* end file src/lasx/lasx_find.cpp */ } // namespace -} // namespace lsx +} // namespace lasx } // namespace simdutf /* begin file src/generic/buf_block_reader.h */ namespace simdutf { -namespace lsx { +namespace lasx { namespace { // Walks through a buffer in block-sized increments, loading the last part with @@ -19634,40 +19589,6 @@ template struct buf_block_reader { size_t idx; }; -// Routines to print masks and text for debugging bitmask operations -simdutf_unused static char *format_input_text_64(const uint8_t *text) { - static char *buf = - reinterpret_cast(malloc(sizeof(simd8x64) + 1)); - for (size_t i = 0; i < sizeof(simd8x64); i++) { - buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -// Routines to print masks and text for debugging bitmask operations -simdutf_unused static char *format_input_text(const simd8x64 &in) { - static char *buf = - reinterpret_cast(malloc(sizeof(simd8x64) + 1)); - in.store(reinterpret_cast(buf)); - for (size_t i = 0; i < sizeof(simd8x64); i++) { - if (buf[i] < ' ') { - buf[i] = '_'; - } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -simdutf_unused static char *format_mask(uint64_t mask) { - static char *buf = reinterpret_cast(malloc(64 + 1)); - for (size_t i = 0; i < 64; i++) { - buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; - } - buf[64] = '\0'; - return buf; -} - template simdutf_really_inline buf_block_reader::buf_block_reader(const uint8_t *_buf, size_t _len) @@ -19709,15 +19630,81 @@ simdutf_really_inline void buf_block_reader::advance() { } } // unnamed namespace -} // namespace lsx +} // namespace lasx } // namespace simdutf /* end file src/generic/buf_block_reader.h */ +/* begin file src/generic/base64lengths.h */ +namespace simdutf { +namespace lasx { +namespace { +namespace base64_lengths { + +simdutf_warn_unused size_t binary_length_from_base64(const char *input, + size_t length) { + size_t pos = 0; + size_t count = 0; + for (; pos + 64 <= length; pos += 64) { + simd8x64 block(reinterpret_cast(input + pos)); + uint64_t maybe_base64 = block.gteq(33); // >= 33 which is '!' in ASCII + count += count_ones(maybe_base64); + } + while (pos < length) { + count += (input[pos] > 0x20) ? 1 : 0; + pos++; + } + // Count padding at the end. + size_t padding = 0; + pos = length; + while (pos > 0 && padding < 2) { + char c = input[--pos]; + if (c == '=') { + padding++; + } else if (c > ' ') { + break; + } + } + return ((count - padding) * 3) / 4; +} + +simdutf_warn_unused size_t binary_length_from_base64(const char16_t *input, + size_t length) { + size_t pos = 0; + size_t count = 0; + for (; pos + 32 <= length; pos += 32) { + simd16x32 block(reinterpret_cast(input + pos)); + uint64_t maybe_base64 = block.gteq(33); // >= 33 which is '!' in ASCII + count += count_ones(maybe_base64); + } + while (pos < length) { + count += (input[pos] > 0x20) ? 1 : 0; + pos++; + } + // Count padding at the end. + size_t padding = 0; + pos = length; + while (pos > 0 && padding < 2) { + char16_t c = input[--pos]; + if (c == '=') { + padding++; + } else if (c > ' ') { + break; + } + } + return ((count - padding) * 3) / 4; +} + +} // namespace base64_lengths +} // unnamed namespace +} // namespace lasx +} // namespace simdutf +/* end file src/generic/base64lengths.h */ + // // Implementation-specific overrides // namespace simdutf { -namespace lsx { +namespace lasx { simdutf_warn_unused result implementation::base64_to_binary( const char *input, size_t length, char *output, base64_options options, @@ -19848,6 +19835,14 @@ size_t implementation::binary_to_base64(const char *input, size_t length, return encode_base64(output, input, length, options); } } + +size_t implementation::binary_to_base64_with_lines( + const char *input, size_t length, char *output, size_t line_length, + base64_options options) const noexcept { + return scalar::base64::tail_encode_base64_impl(output, input, length, + options, line_length); +} + const char *implementation::find(const char *start, const char *end, char character) const noexcept { return util_find(start, end, character); @@ -19858,30 +19853,46 @@ const char16_t *implementation::find(const char16_t *start, const char16_t *end, return util_find(start, end, character); } -} // namespace lsx +simdutf_warn_unused size_t implementation::binary_length_from_base64( + const char *input, size_t length) const noexcept { + return base64_lengths::binary_length_from_base64(input, length); +} + +simdutf_warn_unused size_t implementation::binary_length_from_base64( + const char16_t *input, size_t length) const noexcept { + return base64_lengths::binary_length_from_base64(input, length); +} + +} // namespace lasx } // namespace simdutf -/* begin file src/simdutf/lsx/end.h */ +/* begin file src/simdutf/lasx/end.h */ #undef SIMDUTF_SIMD_HAS_UNSIGNED_CMP -/* end file src/simdutf/lsx/end.h */ -/* end file src/lsx/implementation.cpp */ + +#if SIMDUTF_CAN_ALWAYS_RUN_LASX +// nothing needed. +#else +SIMDUTF_UNTARGET_REGION #endif -#if SIMDUTF_IMPLEMENTATION_LASX -/* begin file src/lasx/implementation.cpp */ -/* begin file src/simdutf/lasx/begin.h */ -// redefining SIMDUTF_IMPLEMENTATION to "lasx" -// #define SIMDUTF_IMPLEMENTATION lasx +/* end file src/simdutf/lasx/end.h */ +/* end file src/lasx/implementation.cpp */ +#endif +#if SIMDUTF_IMPLEMENTATION_LSX +/* begin file src/lsx/implementation.cpp */ +/* begin file src/simdutf/lsx/begin.h */ +// redefining SIMDUTF_IMPLEMENTATION to "lsx" +// #define SIMDUTF_IMPLEMENTATION lsx #define SIMDUTF_SIMD_HAS_UNSIGNED_CMP 1 -/* end file src/simdutf/lasx/begin.h */ +/* end file src/simdutf/lsx/begin.h */ namespace simdutf { -namespace lasx { +namespace lsx { namespace { -#ifndef SIMDUTF_LASX_H - #error "lasx.h must be included" +#ifndef SIMDUTF_LSX_H + #error "lsx.h must be included" #endif using namespace simd; -/* begin file src/lasx/lasx_base64.cpp */ +/* begin file src/lsx/lsx_base64.cpp */ /** * References and further reading: * @@ -19922,118 +19933,100 @@ size_t encode_base64(char *dst, const char *src, size_t srclen, : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; uint8_t *out = (uint8_t *)dst; - v32u8 shuf; - __m256i v_fc0fc00, v_3f03f0, shift_r, shift_l, base64_tbl0, base64_tbl1, + v16u8 shuf; + __m128i v_fc0fc00, v_3f03f0, shift_r, shift_l, base64_tbl0, base64_tbl1, base64_tbl2, base64_tbl3; - if (srclen >= 28) { - shuf = v32u8{1, 0, 2, 1, 4, 3, 5, 4, 7, 6, 8, 7, 10, 9, 11, 10, - 1, 0, 2, 1, 4, 3, 5, 4, 7, 6, 8, 7, 10, 9, 11, 10}; - - v_fc0fc00 = __lasx_xvreplgr2vr_w(uint32_t(0x0fc0fc00)); - v_3f03f0 = __lasx_xvreplgr2vr_w(uint32_t(0x003f03f0)); - shift_r = __lasx_xvreplgr2vr_w(uint32_t(0x0006000a)); - shift_l = __lasx_xvreplgr2vr_w(uint32_t(0x00080004)); - base64_tbl0 = ____m256i(__lsx_vld(lookup_tbl, 0)); - base64_tbl1 = ____m256i(__lsx_vld(lookup_tbl, 16)); - base64_tbl2 = ____m256i(__lsx_vld(lookup_tbl, 32)); - base64_tbl3 = ____m256i(__lsx_vld(lookup_tbl, 48)); + if (srclen >= 16) { + shuf = v16u8{1, 0, 2, 1, 4, 3, 5, 4, 7, 6, 8, 7, 10, 9, 11, 10}; + v_fc0fc00 = __lsx_vreplgr2vr_w(uint32_t(0x0fc0fc00)); + v_3f03f0 = __lsx_vreplgr2vr_w(uint32_t(0x003f03f0)); + shift_r = __lsx_vreplgr2vr_w(uint32_t(0x0006000a)); + shift_l = __lsx_vreplgr2vr_w(uint32_t(0x00080004)); + base64_tbl0 = __lsx_vld(lookup_tbl, 0); + base64_tbl1 = __lsx_vld(lookup_tbl, 16); + base64_tbl2 = __lsx_vld(lookup_tbl, 32); + base64_tbl3 = __lsx_vld(lookup_tbl, 48); } + size_t i = 0; - for (; i + 100 <= srclen; i += 96) { - __m128i in0_lo = + for (; i + 52 <= srclen; i += 48) { + __m128i in0 = __lsx_vld(reinterpret_cast(input + i), 4 * 3 * 0); - __m128i in0_hi = + __m128i in1 = __lsx_vld(reinterpret_cast(input + i), 4 * 3 * 1); - __m128i in1_lo = + __m128i in2 = __lsx_vld(reinterpret_cast(input + i), 4 * 3 * 2); - __m128i in1_hi = + __m128i in3 = __lsx_vld(reinterpret_cast(input + i), 4 * 3 * 3); - __m128i in2_lo = - __lsx_vld(reinterpret_cast(input + i), 4 * 3 * 4); - __m128i in2_hi = - __lsx_vld(reinterpret_cast(input + i), 4 * 3 * 5); - __m128i in3_lo = - __lsx_vld(reinterpret_cast(input + i), 4 * 3 * 6); - __m128i in3_hi = - __lsx_vld(reinterpret_cast(input + i), 4 * 3 * 7); - - __m256i in0 = lasx_set_q(in0_hi, in0_lo); - __m256i in1 = lasx_set_q(in1_hi, in1_lo); - __m256i in2 = lasx_set_q(in2_hi, in2_lo); - __m256i in3 = lasx_set_q(in3_hi, in3_lo); - - in0 = __lasx_xvshuf_b(in0, in0, (__m256i)shuf); - in1 = __lasx_xvshuf_b(in1, in1, (__m256i)shuf); - in2 = __lasx_xvshuf_b(in2, in2, (__m256i)shuf); - in3 = __lasx_xvshuf_b(in3, in3, (__m256i)shuf); - __m256i t0_0 = __lasx_xvand_v(in0, v_fc0fc00); - __m256i t0_1 = __lasx_xvand_v(in1, v_fc0fc00); - __m256i t0_2 = __lasx_xvand_v(in2, v_fc0fc00); - __m256i t0_3 = __lasx_xvand_v(in3, v_fc0fc00); + in0 = __lsx_vshuf_b(in0, in0, (__m128i)shuf); + in1 = __lsx_vshuf_b(in1, in1, (__m128i)shuf); + in2 = __lsx_vshuf_b(in2, in2, (__m128i)shuf); + in3 = __lsx_vshuf_b(in3, in3, (__m128i)shuf); - __m256i t1_0 = __lasx_xvsrl_h(t0_0, shift_r); - __m256i t1_1 = __lasx_xvsrl_h(t0_1, shift_r); - __m256i t1_2 = __lasx_xvsrl_h(t0_2, shift_r); - __m256i t1_3 = __lasx_xvsrl_h(t0_3, shift_r); + __m128i t0_0 = __lsx_vand_v(in0, v_fc0fc00); + __m128i t0_1 = __lsx_vand_v(in1, v_fc0fc00); + __m128i t0_2 = __lsx_vand_v(in2, v_fc0fc00); + __m128i t0_3 = __lsx_vand_v(in3, v_fc0fc00); - __m256i t2_0 = __lasx_xvand_v(in0, v_3f03f0); - __m256i t2_1 = __lasx_xvand_v(in1, v_3f03f0); - __m256i t2_2 = __lasx_xvand_v(in2, v_3f03f0); - __m256i t2_3 = __lasx_xvand_v(in3, v_3f03f0); + __m128i t1_0 = __lsx_vsrl_h(t0_0, shift_r); + __m128i t1_1 = __lsx_vsrl_h(t0_1, shift_r); + __m128i t1_2 = __lsx_vsrl_h(t0_2, shift_r); + __m128i t1_3 = __lsx_vsrl_h(t0_3, shift_r); - __m256i t3_0 = __lasx_xvsll_h(t2_0, shift_l); - __m256i t3_1 = __lasx_xvsll_h(t2_1, shift_l); - __m256i t3_2 = __lasx_xvsll_h(t2_2, shift_l); - __m256i t3_3 = __lasx_xvsll_h(t2_3, shift_l); + __m128i t2_0 = __lsx_vand_v(in0, v_3f03f0); + __m128i t2_1 = __lsx_vand_v(in1, v_3f03f0); + __m128i t2_2 = __lsx_vand_v(in2, v_3f03f0); + __m128i t2_3 = __lsx_vand_v(in3, v_3f03f0); - __m256i input0 = __lasx_xvor_v(t1_0, t3_0); - __m256i input0_shuf0 = __lasx_xvshuf_b(base64_tbl1, base64_tbl0, input0); - __m256i input0_shuf1 = __lasx_xvshuf_b( - base64_tbl3, base64_tbl2, __lasx_xvsub_b(input0, __lasx_xvldi(32))); - __m256i input0_mask = __lasx_xvslei_bu(input0, 31); - __m256i input0_result = - __lasx_xvbitsel_v(input0_shuf1, input0_shuf0, input0_mask); - __lasx_xvst(input0_result, reinterpret_cast<__m256i *>(out), 0); - out += 32; + __m128i t3_0 = __lsx_vsll_h(t2_0, shift_l); + __m128i t3_1 = __lsx_vsll_h(t2_1, shift_l); + __m128i t3_2 = __lsx_vsll_h(t2_2, shift_l); + __m128i t3_3 = __lsx_vsll_h(t2_3, shift_l); - __m256i input1 = __lasx_xvor_v(t1_1, t3_1); - __m256i input1_shuf0 = __lasx_xvshuf_b(base64_tbl1, base64_tbl0, input1); - __m256i input1_shuf1 = __lasx_xvshuf_b( - base64_tbl3, base64_tbl2, __lasx_xvsub_b(input1, __lasx_xvldi(32))); - __m256i input1_mask = __lasx_xvslei_bu(input1, 31); - __m256i input1_result = - __lasx_xvbitsel_v(input1_shuf1, input1_shuf0, input1_mask); - __lasx_xvst(input1_result, reinterpret_cast<__m256i *>(out), 0); - out += 32; + __m128i input0 = __lsx_vor_v(t1_0, t3_0); + __m128i input0_shuf0 = __lsx_vshuf_b(base64_tbl1, base64_tbl0, input0); + __m128i input0_shuf1 = __lsx_vshuf_b(base64_tbl3, base64_tbl2, + __lsx_vsub_b(input0, __lsx_vldi(32))); + __m128i input0_mask = __lsx_vslei_bu(input0, 31); + __m128i input0_result = + __lsx_vbitsel_v(input0_shuf1, input0_shuf0, input0_mask); + __lsx_vst(input0_result, reinterpret_cast<__m128i *>(out), 0); + out += 16; - __m256i input2 = __lasx_xvor_v(t1_2, t3_2); - __m256i input2_shuf0 = __lasx_xvshuf_b(base64_tbl1, base64_tbl0, input2); - __m256i input2_shuf1 = __lasx_xvshuf_b( - base64_tbl3, base64_tbl2, __lasx_xvsub_b(input2, __lasx_xvldi(32))); - __m256i input2_mask = __lasx_xvslei_bu(input2, 31); - __m256i input2_result = - __lasx_xvbitsel_v(input2_shuf1, input2_shuf0, input2_mask); - __lasx_xvst(input2_result, reinterpret_cast<__m256i *>(out), 0); - out += 32; + __m128i input1 = __lsx_vor_v(t1_1, t3_1); + __m128i input1_shuf0 = __lsx_vshuf_b(base64_tbl1, base64_tbl0, input1); + __m128i input1_shuf1 = __lsx_vshuf_b(base64_tbl3, base64_tbl2, + __lsx_vsub_b(input1, __lsx_vldi(32))); + __m128i input1_mask = __lsx_vslei_bu(input1, 31); + __m128i input1_result = + __lsx_vbitsel_v(input1_shuf1, input1_shuf0, input1_mask); + __lsx_vst(input1_result, reinterpret_cast<__m128i *>(out), 0); + out += 16; - __m256i input3 = __lasx_xvor_v(t1_3, t3_3); - __m256i input3_shuf0 = __lasx_xvshuf_b(base64_tbl1, base64_tbl0, input3); - __m256i input3_shuf1 = __lasx_xvshuf_b( - base64_tbl3, base64_tbl2, __lasx_xvsub_b(input3, __lasx_xvldi(32))); - __m256i input3_mask = __lasx_xvslei_bu(input3, 31); - __m256i input3_result = - __lasx_xvbitsel_v(input3_shuf1, input3_shuf0, input3_mask); - __lasx_xvst(input3_result, reinterpret_cast<__m256i *>(out), 0); - out += 32; - } - for (; i + 28 <= srclen; i += 24) { + __m128i input2 = __lsx_vor_v(t1_2, t3_2); + __m128i input2_shuf0 = __lsx_vshuf_b(base64_tbl1, base64_tbl0, input2); + __m128i input2_shuf1 = __lsx_vshuf_b(base64_tbl3, base64_tbl2, + __lsx_vsub_b(input2, __lsx_vldi(32))); + __m128i input2_mask = __lsx_vslei_bu(input2, 31); + __m128i input2_result = + __lsx_vbitsel_v(input2_shuf1, input2_shuf0, input2_mask); + __lsx_vst(input2_result, reinterpret_cast<__m128i *>(out), 0); + out += 16; - __m128i in_lo = __lsx_vld(reinterpret_cast(input + i), 0); - __m128i in_hi = - __lsx_vld(reinterpret_cast(input + i), 4 * 3 * 1); + __m128i input3 = __lsx_vor_v(t1_3, t3_3); + __m128i input3_shuf0 = __lsx_vshuf_b(base64_tbl1, base64_tbl0, input3); + __m128i input3_shuf1 = __lsx_vshuf_b(base64_tbl3, base64_tbl2, + __lsx_vsub_b(input3, __lsx_vldi(32))); + __m128i input3_mask = __lsx_vslei_bu(input3, 31); + __m128i input3_result = + __lsx_vbitsel_v(input3_shuf1, input3_shuf0, input3_mask); + __lsx_vst(input3_result, reinterpret_cast<__m128i *>(out), 0); + out += 16; + } + for (; i + 16 <= srclen; i += 12) { - __m256i in = lasx_set_q(in_hi, in_lo); + __m128i in = __lsx_vld(reinterpret_cast(input + i), 0); // bytes from groups A, B and C are needed in separate 32-bit lanes // in = [DDDD|CCCC|BBBB|AAAA] @@ -20047,32 +20040,33 @@ size_t encode_base64(char *dst, const char *src, size_t srclen, // [bbbbcccc|ccdddddd|aaaaaabb|bbbbcccc] // ^^^^ ^^^^^^^^ ^^^^^^^^ ^^^^ // processed bits - in = __lasx_xvshuf_b(in, in, (__m256i)shuf); + in = __lsx_vshuf_b(in, in, (__m128i)shuf); // unpacking // t0 = [0000cccc|cc000000|aaaaaa00|00000000] - __m256i t0 = __lasx_xvand_v(in, v_fc0fc00); + __m128i t0 = __lsx_vand_v(in, v_fc0fc00); // t1 = [00000000|00cccccc|00000000|00aaaaaa] // ((c >> 6), (a >> 10)) - __m256i t1 = __lasx_xvsrl_h(t0, shift_r); + __m128i t1 = __lsx_vsrl_h(t0, shift_r); // t2 = [00000000|00dddddd|000000bb|bbbb0000] - __m256i t2 = __lasx_xvand_v(in, v_3f03f0); + __m128i t2 = __lsx_vand_v(in, v_3f03f0); // t3 = [00dddddd|00000000|00bbbbbb|00000000] // ((d << 8), (b << 4)) - __m256i t3 = __lasx_xvsll_h(t2, shift_l); + __m128i t3 = __lsx_vsll_h(t2, shift_l); // res = [00dddddd|00cccccc|00bbbbbb|00aaaaaa] = t1 | t3 - __m256i indices = __lasx_xvor_v(t1, t3); + __m128i indices = __lsx_vor_v(t1, t3); - __m256i indices_shuf0 = __lasx_xvshuf_b(base64_tbl1, base64_tbl0, indices); - __m256i indices_shuf1 = __lasx_xvshuf_b( - base64_tbl3, base64_tbl2, __lasx_xvsub_b(indices, __lasx_xvldi(32))); - __m256i indices_mask = __lasx_xvslei_bu(indices, 31); - __m256i indices_result = - __lasx_xvbitsel_v(indices_shuf1, indices_shuf0, indices_mask); - __lasx_xvst(indices_result, reinterpret_cast<__m256i *>(out), 0); - out += 32; + __m128i indices_shuf0 = __lsx_vshuf_b(base64_tbl1, base64_tbl0, indices); + __m128i indices_shuf1 = __lsx_vshuf_b( + base64_tbl3, base64_tbl2, __lsx_vsub_b(indices, __lsx_vldi(32))); + __m128i indices_mask = __lsx_vslei_bu(indices, 31); + __m128i indices_result = + __lsx_vbitsel_v(indices_shuf1, indices_shuf0, indices_mask); + + __lsx_vst(indices_result, reinterpret_cast<__m128i *>(out), 0); + out += 16; } return i / 3 * 4 + scalar::base64::tail_encode_base64((char *)out, src + i, @@ -20096,7 +20090,7 @@ static inline void compress(__m128i data, uint16_t mask, char *output) { tables::base64::thintable_epi8[mask2]}; // we increment by 0x08 the second half of the mask - const v4u32 hi = {0, 0, 0x08080808, 0x08080808}; + v4u32 hi = {0, 0, 0x08080808, 0x08080808}; __m128i shufmask1 = __lsx_vadd_b((__m128i)shufmask, (__m128i)hi); // this is the version "nearly pruned" @@ -20118,111 +20112,115 @@ static inline void compress(__m128i data, uint16_t mask, char *output) { } struct block64 { - __m256i chunks[2]; + __m128i chunks[4]; }; template -static inline uint32_t to_base64_mask(__m256i *src, bool *error) { - __m256i ascii_space_tbl = - ____m256i((__m128i)v16u8{0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x9, 0xa, 0x0, 0xc, 0xd, 0x0, 0x0}); +static inline uint16_t to_base64_mask(__m128i *src, bool *error) { + const v16u8 ascii_space_tbl = {0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x9, 0xa, 0x0, 0xc, 0xd, 0x0, 0x0}; // credit: aqrit - __m256i delta_asso; + /* + '0'(0x30)-'9'(0x39) => delta_values_index = 4 + 'A'(0x41)-'Z'(0x5a) => delta_values_index = 4/5/12(4+8) + 'a'(0x61)-'z'(0x7a) => delta_values_index = 6/7/14(6+8) + '+'(0x2b) => delta_values_index = 3 + '/'(0x2f) => delta_values_index = 2+8 = 10 + '-'(0x2d) => delta_values_index = 2+8 = 10 + '_'(0x5f) => delta_values_index = 5+8 = 13 + */ + v16u8 delta_asso; if (default_or_url) { - delta_asso = - ____m256i((__m128i)v16u8{0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x11, 0x0, 0x16}); + delta_asso = v16u8{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x16}; } else { - delta_asso = - ____m256i((__m128i)v16u8{0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, - 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF}); + delta_asso = v16u8{0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF}; } - __m256i delta_values; + v16i8 delta_values; if (default_or_url) { - delta_values = ____m256i( - (__m128i)v16i8{int8_t(0xBF), int8_t(0xE0), int8_t(0xB9), int8_t(0x13), - int8_t(0x04), int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), - int8_t(0xB9), int8_t(0x00), int8_t(0xFF), int8_t(0x11), - int8_t(0xFF), int8_t(0xBF), int8_t(0x10), int8_t(0xB9)}); + delta_values = + v16i8{int8_t(0xBF), int8_t(0xE0), int8_t(0xB9), int8_t(0x13), + int8_t(0x04), int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), + int8_t(0xB9), int8_t(0x00), int8_t(0xFF), int8_t(0x11), + int8_t(0xFF), int8_t(0xBF), int8_t(0x10), int8_t(0xB9)}; } else if (base64_url) { - delta_values = ____m256i( - (__m128i)v16i8{int8_t(0x00), int8_t(0x00), int8_t(0x00), int8_t(0x13), - int8_t(0x04), int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), - int8_t(0xB9), int8_t(0x00), int8_t(0x11), int8_t(0xC3), - int8_t(0xBF), int8_t(0xE0), int8_t(0xB9), int8_t(0xB9)}); + delta_values = + v16i8{int8_t(0x00), int8_t(0x00), int8_t(0x00), int8_t(0x13), + int8_t(0x04), int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), + int8_t(0xB9), int8_t(0x00), int8_t(0x11), int8_t(0xC3), + int8_t(0xBF), int8_t(0xE0), int8_t(0xB9), int8_t(0xB9)}; } else { - delta_values = ____m256i( - (__m128i)v16i8{int8_t(0x00), int8_t(0x00), int8_t(0x00), int8_t(0x13), - int8_t(0x04), int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), - int8_t(0xB9), int8_t(0x00), int8_t(0x10), int8_t(0xC3), - int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), int8_t(0xB9)}); + delta_values = + v16i8{int8_t(0x00), int8_t(0x00), int8_t(0x00), int8_t(0x13), + int8_t(0x04), int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), + int8_t(0xB9), int8_t(0x00), int8_t(0x10), int8_t(0xC3), + int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), int8_t(0xB9)}; } - __m256i check_asso; + v16u8 check_asso; if (default_or_url) { - check_asso = ____m256i((__m128i)v16u8{0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x03, 0x07, - 0x0B, 0x0E, 0x0B, 0x06}); - + check_asso = v16u8{0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x03, 0x07, 0x0B, 0x0E, 0x0B, 0x06}; } else if (base64_url) { - check_asso = ____m256i((__m128i)v16u8{0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x03, 0x07, - 0x0B, 0x06, 0x0B, 0x12}); + check_asso = v16u8{0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x03, 0x07, 0x0B, 0x06, 0x0B, 0x12}; } else { - check_asso = ____m256i((__m128i)v16u8{0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x03, 0x07, - 0x0B, 0x0B, 0x0B, 0x0F}); + check_asso = v16u8{0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x03, 0x07, 0x0B, 0x0B, 0x0B, 0x0F}; } - __m256i check_values; + v16i8 check_values; if (default_or_url) { - - check_values = ____m256i( - (__m128i)v16i8{int8_t(0x80), int8_t(0x80), int8_t(0x80), int8_t(0x80), - int8_t(0xCF), int8_t(0xBF), int8_t(0xD5), int8_t(0xA6), - int8_t(0xB5), int8_t(0xA1), int8_t(0x00), int8_t(0x80), - int8_t(0x00), int8_t(0x80), int8_t(0x00), int8_t(0x80)}); + check_values = + v16i8{int8_t(0x80), int8_t(0x80), int8_t(0x80), int8_t(0x80), + int8_t(0xCF), int8_t(0xBF), int8_t(0xD5), int8_t(0xA6), + int8_t(0xB5), int8_t(0xA1), int8_t(0x00), int8_t(0x80), + int8_t(0x00), int8_t(0x80), int8_t(0x00), int8_t(0x80)}; } else if (base64_url) { - check_values = ____m256i( - (__m128i)v16i8{int8_t(0x0), int8_t(0x80), int8_t(0x80), int8_t(0x80), - int8_t(0xCF), int8_t(0xBF), int8_t(0xD3), int8_t(0xA6), - int8_t(0xB5), int8_t(0x86), int8_t(0xD0), int8_t(0x80), - int8_t(0xB0), int8_t(0x80), int8_t(0x0), int8_t(0x0)}); + check_values = v16i8{int8_t(0x0), int8_t(0x80), int8_t(0x80), int8_t(0x80), + int8_t(0xCF), int8_t(0xBF), int8_t(0xD3), int8_t(0xA6), + int8_t(0xB5), int8_t(0x86), int8_t(0xD0), int8_t(0x80), + int8_t(0xB0), int8_t(0x80), int8_t(0x0), int8_t(0x0)}; } else { - check_values = ____m256i( - (__m128i)v16i8{int8_t(0x80), int8_t(0x80), int8_t(0x80), int8_t(0x80), - int8_t(0xCF), int8_t(0xBF), int8_t(0xD5), int8_t(0xA6), - int8_t(0xB5), int8_t(0x86), int8_t(0xD1), int8_t(0x80), - int8_t(0xB1), int8_t(0x80), int8_t(0x91), int8_t(0x80)}); + check_values = + v16i8{int8_t(0x80), int8_t(0x80), int8_t(0x80), int8_t(0x80), + int8_t(0xCF), int8_t(0xBF), int8_t(0xD5), int8_t(0xA6), + int8_t(0xB5), int8_t(0x86), int8_t(0xD1), int8_t(0x80), + int8_t(0xB1), int8_t(0x80), int8_t(0x91), int8_t(0x80)}; } - __m256i shifted = __lasx_xvsrli_b(*src, 3); - __m256i asso_index = __lasx_xvand_v(*src, __lasx_xvldi(0xF)); - __m256i delta_hash = __lasx_xvavgr_bu( - __lasx_xvshuf_b(delta_asso, delta_asso, asso_index), shifted); - __m256i check_hash = __lasx_xvavgr_bu( - __lasx_xvshuf_b(check_asso, check_asso, asso_index), shifted); + const __m128i shifted = __lsx_vsrli_b(*src, 3); + __m128i asso_index = __lsx_vand_v(*src, __lsx_vldi(0xF)); + const __m128i delta_hash = + __lsx_vavgr_bu(__lsx_vshuf_b((__m128i)delta_asso, (__m128i)delta_asso, + (__m128i)asso_index), + shifted); + const __m128i check_hash = + __lsx_vavgr_bu(__lsx_vshuf_b((__m128i)check_asso, (__m128i)check_asso, + (__m128i)asso_index), + shifted); - __m256i out = __lasx_xvsadd_b( - __lasx_xvshuf_b(delta_values, delta_values, delta_hash), *src); - __m256i chk = __lasx_xvsadd_b( - __lasx_xvshuf_b(check_values, check_values, check_hash), *src); - __m256i chk_ltz = __lasx_xvmskltz_b(chk); - unsigned int mask = __lasx_xvpickve2gr_wu(chk_ltz, 0); - mask = mask | (__lsx_vpickve2gr_hu(lasx_extracti128_hi(chk_ltz), 0) << 16); + const __m128i out = + __lsx_vsadd_b(__lsx_vshuf_b((__m128i)delta_values, (__m128i)delta_values, + (__m128i)delta_hash), + *src); + const __m128i chk = + __lsx_vsadd_b(__lsx_vshuf_b((__m128i)check_values, (__m128i)check_values, + (__m128i)check_hash), + *src); + unsigned int mask = __lsx_vpickve2gr_hu(__lsx_vmskltz_b(chk), 0); if (mask) { - __m256i ascii_space = __lasx_xvseq_b( - __lasx_xvshuf_b(ascii_space_tbl, ascii_space_tbl, asso_index), *src); - __m256i ascii_space_ltz = __lasx_xvmskltz_b(ascii_space); - unsigned int ascii_space_mask = __lasx_xvpickve2gr_wu(ascii_space_ltz, 0); - ascii_space_mask = - ascii_space_mask | - (__lsx_vpickve2gr_hu(lasx_extracti128_hi(ascii_space_ltz), 0) << 16); - *error |= (mask != ascii_space_mask); + __m128i ascii_space = __lsx_vseq_b(__lsx_vshuf_b((__m128i)ascii_space_tbl, + (__m128i)ascii_space_tbl, + (__m128i)asso_index), + *src); + *error |= + (mask != __lsx_vpickve2gr_hu(__lsx_vmskltz_b((__m128i)ascii_space), 0)); } *src = out; - return (uint32_t)mask; + return (uint16_t)mask; } template @@ -20232,12 +20230,18 @@ static inline uint64_t to_base64_mask(block64 *b, bool *error) { to_base64_mask(&b->chunks[0], error); uint64_t m1 = to_base64_mask(&b->chunks[1], error); - return m0 | (m1 << 32); + uint64_t m2 = + to_base64_mask(&b->chunks[2], error); + uint64_t m3 = + to_base64_mask(&b->chunks[3], error); + return m0 | (m1 << 16) | (m2 << 32) | (m3 << 48); } static inline void copy_block(block64 *b, char *output) { - __lasx_xvst(b->chunks[0], reinterpret_cast<__m256i *>(output), 0); - __lasx_xvst(b->chunks[1], reinterpret_cast<__m256i *>(output), 32); + __lsx_vst(b->chunks[0], reinterpret_cast<__m128i *>(output), 0); + __lsx_vst(b->chunks[1], reinterpret_cast<__m128i *>(output), 16); + __lsx_vst(b->chunks[2], reinterpret_cast<__m128i *>(output), 32); + __lsx_vst(b->chunks[3], reinterpret_cast<__m128i *>(output), 48); } static inline uint64_t compress_block(block64 *b, uint64_t mask, char *output) { @@ -20245,80 +20249,152 @@ static inline uint64_t compress_block(block64 *b, uint64_t mask, char *output) { uint64_t count = __lsx_vpickve2gr_d(__lsx_vpcnt_h(__lsx_vreplgr2vr_d(nmask)), 0); uint16_t *count_ptr = (uint16_t *)&count; - compress(lasx_extracti128_lo(b->chunks[0]), uint16_t(mask), output); - compress(lasx_extracti128_hi(b->chunks[0]), uint16_t(mask >> 16), - output + count_ptr[0]); - compress(lasx_extracti128_lo(b->chunks[1]), uint16_t(mask >> 32), + compress(b->chunks[0], uint16_t(mask), output); + compress(b->chunks[1], uint16_t(mask >> 16), output + count_ptr[0]); + compress(b->chunks[2], uint16_t(mask >> 32), output + count_ptr[0] + count_ptr[1]); - compress(lasx_extracti128_hi(b->chunks[1]), uint16_t(mask >> 48), + compress(b->chunks[3], uint16_t(mask >> 48), output + count_ptr[0] + count_ptr[1] + count_ptr[2]); return count_ones(nmask); } +template bool is_power_of_two(T x) { return (x & (x - 1)) == 0; } + +inline size_t compress_block_single(block64 *b, uint64_t mask, char *output) { + const size_t pos64 = trailing_zeroes(mask); + const int8_t pos = pos64 & 0xf; + // Predefine the index vector + const v16u8 v1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + + switch (pos64 >> 4) { + case 0b00: { + const __m128i v0 = __lsx_vreplgr2vr_b((uint8_t)(pos - 1)); + const __m128i v2 = __lsx_vslt_b(v0, (__m128i)v1); // v1 > v0 + const __m128i sh = __lsx_vsub_b((__m128i)v1, v2); + const __m128i compressed = __lsx_vshuf_b(b->chunks[0], b->chunks[0], sh); + __lsx_vst(compressed, reinterpret_cast<__m128i *>(output + 0 * 16), 0); + __lsx_vst(b->chunks[1], reinterpret_cast<__m128i *>(output + 1 * 16 - 1), + 0); + __lsx_vst(b->chunks[2], reinterpret_cast<__m128i *>(output + 2 * 16 - 1), + 0); + __lsx_vst(b->chunks[3], reinterpret_cast<__m128i *>(output + 3 * 16 - 1), + 0); + } break; + + case 0b01: { + __lsx_vst(b->chunks[0], reinterpret_cast<__m128i *>(output + 0 * 16), 0); + + const __m128i v0 = __lsx_vreplgr2vr_b((uint8_t)(pos - 1)); + const __m128i v2 = __lsx_vslt_b(v0, (__m128i)v1); + const __m128i sh = __lsx_vsub_b((__m128i)v1, v2); + const __m128i compressed = __lsx_vshuf_b(b->chunks[1], b->chunks[1], sh); + + __lsx_vst(compressed, reinterpret_cast<__m128i *>(output + 1 * 16), 0); + __lsx_vst(b->chunks[2], reinterpret_cast<__m128i *>(output + 2 * 16 - 1), + 0); + __lsx_vst(b->chunks[3], reinterpret_cast<__m128i *>(output + 3 * 16 - 1), + 0); + } break; + + case 0b10: { + __lsx_vst(b->chunks[0], reinterpret_cast<__m128i *>(output + 0 * 16), 0); + __lsx_vst(b->chunks[1], reinterpret_cast<__m128i *>(output + 1 * 16), 0); + + const __m128i v0 = __lsx_vreplgr2vr_b((uint8_t)(pos - 1)); + const __m128i v2 = __lsx_vslt_b(v0, (__m128i)v1); + const __m128i sh = __lsx_vsub_b((__m128i)v1, v2); + const __m128i compressed = __lsx_vshuf_b(b->chunks[2], b->chunks[2], sh); + + __lsx_vst(compressed, reinterpret_cast<__m128i *>(output + 2 * 16), 0); + __lsx_vst(b->chunks[3], reinterpret_cast<__m128i *>(output + 3 * 16 - 1), + 0); + } break; + + case 0b11: { + __lsx_vst(b->chunks[0], reinterpret_cast<__m128i *>(output + 0 * 16), 0); + __lsx_vst(b->chunks[1], reinterpret_cast<__m128i *>(output + 1 * 16), 0); + __lsx_vst(b->chunks[2], reinterpret_cast<__m128i *>(output + 2 * 16), 0); + + const __m128i v0 = __lsx_vreplgr2vr_b((uint8_t)(pos - 1)); + const __m128i v2 = __lsx_vslt_b(v0, (__m128i)v1); + const __m128i sh = __lsx_vsub_b((__m128i)v1, v2); + const __m128i compressed = __lsx_vshuf_b(b->chunks[3], b->chunks[3], sh); + + __lsx_vst(compressed, reinterpret_cast<__m128i *>(output + 3 * 16), 0); + } break; + } + return 63; +} + // The caller of this function is responsible to ensure that there are 64 bytes // available from reading at src. The data is read into a block64 structure. static inline void load_block(block64 *b, const char *src) { - b->chunks[0] = __lasx_xvld(reinterpret_cast(src), 0); - b->chunks[1] = __lasx_xvld(reinterpret_cast(src), 32); + b->chunks[0] = __lsx_vld(reinterpret_cast(src), 0); + b->chunks[1] = __lsx_vld(reinterpret_cast(src), 16); + b->chunks[2] = __lsx_vld(reinterpret_cast(src), 32); + b->chunks[3] = __lsx_vld(reinterpret_cast(src), 48); } // The caller of this function is responsible to ensure that there are 128 bytes // available from reading at src. The data is read into a block64 structure. static inline void load_block(block64 *b, const char16_t *src) { - __m256i m1 = __lasx_xvld(reinterpret_cast(src), 0); - __m256i m2 = __lasx_xvld(reinterpret_cast(src), 32); - __m256i m3 = __lasx_xvld(reinterpret_cast(src), 64); - __m256i m4 = __lasx_xvld(reinterpret_cast(src), 96); - b->chunks[0] = __lasx_xvpermi_d(__lasx_xvssrlni_bu_h(m2, m1, 0), 0b11011000); - b->chunks[1] = __lasx_xvpermi_d(__lasx_xvssrlni_bu_h(m4, m3, 0), 0b11011000); + __m128i m1 = __lsx_vld(reinterpret_cast(src), 0); + __m128i m2 = __lsx_vld(reinterpret_cast(src), 16); + __m128i m3 = __lsx_vld(reinterpret_cast(src), 32); + __m128i m4 = __lsx_vld(reinterpret_cast(src), 48); + __m128i m5 = __lsx_vld(reinterpret_cast(src), 64); + __m128i m6 = __lsx_vld(reinterpret_cast(src), 80); + __m128i m7 = __lsx_vld(reinterpret_cast(src), 96); + __m128i m8 = __lsx_vld(reinterpret_cast(src), 112); + b->chunks[0] = __lsx_vssrlni_bu_h(m2, m1, 0); + b->chunks[1] = __lsx_vssrlni_bu_h(m4, m3, 0); + b->chunks[2] = __lsx_vssrlni_bu_h(m6, m5, 0); + b->chunks[3] = __lsx_vssrlni_bu_h(m8, m7, 0); } -static inline void base64_decode(char *out, __m256i str) { - __m256i t0 = __lasx_xvor_v( - __lasx_xvslli_w(str, 26), - __lasx_xvslli_w(__lasx_xvand_v(str, lasx_splat_u32(0x0000ff00)), 12)); - __m256i t1 = - __lasx_xvsrli_w(__lasx_xvand_v(str, lasx_splat_u32(0x003f0000)), 2); - __m256i t2 = __lasx_xvor_v(t0, t1); - __m256i t3 = __lasx_xvor_v(t2, __lasx_xvsrli_w(str, 16)); - __m256i pack_shuffle = ____m256i( - (__m128i)v16u8{3, 2, 1, 7, 6, 5, 11, 10, 9, 15, 14, 13, 0, 0, 0, 0}); - t3 = __lasx_xvshuf_b(t3, t3, (__m256i)pack_shuffle); +static inline void base64_decode(char *out, __m128i str) { + __m128i t0 = __lsx_vor_v( + __lsx_vslli_w(str, 26), + __lsx_vslli_w(__lsx_vand_v(str, lsx_splat_u32(0x0000FF00)), 12)); + __m128i t1 = __lsx_vsrli_w(__lsx_vand_v(str, lsx_splat_u32(0x003F0000)), 2); + __m128i t2 = __lsx_vor_v(t0, t1); + __m128i t3 = __lsx_vor_v(t2, __lsx_vsrli_w(str, 16)); + const v16u8 pack_shuffle = {3, 2, 1, 7, 6, 5, 11, 10, + 9, 15, 14, 13, 0, 0, 0, 0}; + t3 = __lsx_vshuf_b(t3, t3, (__m128i)pack_shuffle); // Store the output: - __lsx_vst(lasx_extracti128_lo(t3), out, 0); - __lsx_vst(lasx_extracti128_hi(t3), out, 12); + // we only need 12. + __lsx_vstelm_d(t3, out, 0, 0); + __lsx_vstelm_w(t3, out + 8, 0, 2); } // decode 64 bytes and output 48 bytes static inline void base64_decode_block(char *out, const char *src) { - base64_decode(out, __lasx_xvld(reinterpret_cast(src), 0)); + base64_decode(out, __lsx_vld(reinterpret_cast(src), 0)); + base64_decode(out + 12, + __lsx_vld(reinterpret_cast(src), 16)); base64_decode(out + 24, - __lasx_xvld(reinterpret_cast(src), 32)); + __lsx_vld(reinterpret_cast(src), 32)); + base64_decode(out + 36, + __lsx_vld(reinterpret_cast(src), 48)); } - static inline void base64_decode_block_safe(char *out, const char *src) { - base64_decode(out, __lasx_xvld(reinterpret_cast(src), 0)); - char buffer[32]; - base64_decode(buffer, - __lasx_xvld(reinterpret_cast(src), 32)); - std::memcpy(out + 24, buffer, 24); + base64_decode_block(out, src); } - static inline void base64_decode_block(char *out, block64 *b) { base64_decode(out, b->chunks[0]); - base64_decode(out + 24, b->chunks[1]); + base64_decode(out + 12, b->chunks[1]); + base64_decode(out + 24, b->chunks[2]); + base64_decode(out + 36, b->chunks[3]); } static inline void base64_decode_block_safe(char *out, block64 *b) { - base64_decode(out, b->chunks[0]); - char buffer[32]; - base64_decode(buffer, b->chunks[1]); - std::memcpy(out + 24, buffer, 24); + base64_decode_block(out, b); } template + typename char_type> full_result -compress_decode_base64(char *dst, const chartype *src, size_t srclen, +compress_decode_base64(char *dst, const char_type *src, size_t srclen, base64_options options, last_chunk_handling_options last_chunk_options) { const uint8_t *to_base64 = @@ -20336,19 +20412,15 @@ compress_decode_base64(char *dst, const chartype *src, size_t srclen, } return {SUCCESS, full_input_length, 0}; } - char *end_of_safe_64byte_zone = - (srclen + 3) / 4 * 3 >= 63 ? dst + (srclen + 3) / 4 * 3 - 63 : dst; - - const chartype *const srcinit = src; + const char_type *const srcinit = src; const char *const dstinit = dst; - const chartype *const srcend = src + srclen; + const char_type *const srcend = src + srclen; - constexpr size_t block_size = 6; - static_assert(block_size >= 2, "block_size must be at least two"); + constexpr size_t block_size = 10; char buffer[block_size * 64]; char *bufferptr = buffer; if (srclen >= 64) { - const chartype *const srcend64 = src + srclen - 64; + const char_type *const srcend64 = src + srclen - 64; while (src <= srcend64) { block64 b; load_block(&b, src); @@ -20356,55 +20428,49 @@ compress_decode_base64(char *dst, const chartype *src, size_t srclen, bool error = false; uint64_t badcharmask = to_base64_mask(&b, &error); - if (error && !ignore_garbage) { - src -= 64; - while (src < srcend && scalar::base64::is_eight_byte(*src) && - to_base64[uint8_t(*src)] <= 64) { - src++; + if (badcharmask) { + if (error && !ignore_garbage) { + src -= 64; + while (src < srcend && scalar::base64::is_eight_byte(*src) && + to_base64[uint8_t(*src)] <= 64) { + src++; + } + if (src < srcend) { + // should never happen + } + return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit), + size_t(dst - dstinit)}; } - return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit), - size_t(dst - dstinit)}; } + if (badcharmask != 0) { - // optimization opportunity: check for simple masks like those made of - // continuous 1s followed by continuous 0s. And masks containing a - // single bad character. - bufferptr += compress_block(&b, badcharmask, bufferptr); - } else if (bufferptr != buffer) { - copy_block(&b, bufferptr); - bufferptr += 64; - } else { - if (dst >= end_of_safe_64byte_zone) { - base64_decode_block_safe(dst, &b); + if (is_power_of_two(badcharmask)) { + bufferptr += compress_block_single(&b, badcharmask, bufferptr); } else { - base64_decode_block(dst, &b); + bufferptr += compress_block(&b, badcharmask, bufferptr); } - dst += 48; + } else { + // optimization opportunity: if bufferptr == buffer and mask == 0, we + // can avoid the call to compress_block and decode directly. + copy_block(&b, bufferptr); + bufferptr += 64; } if (bufferptr >= (block_size - 1) * 64 + buffer) { - for (size_t i = 0; i < (block_size - 2); i++) { + for (size_t i = 0; i < (block_size - 1); i++) { base64_decode_block(dst, buffer + i * 64); dst += 48; } - if (dst >= end_of_safe_64byte_zone) { - base64_decode_block_safe(dst, buffer + (block_size - 2) * 64); - } else { - base64_decode_block(dst, buffer + (block_size - 2) * 64); - } - dst += 48; std::memcpy(buffer, buffer + (block_size - 1) * 64, 64); // 64 might be too much bufferptr -= (block_size - 1) * 64; } } } - char *buffer_start = buffer; // Optimization note: if this is almost full, then it is worth our // time, otherwise, we should just decode directly. int last_block = (int)((bufferptr - buffer_start) % 64); if (last_block != 0 && srcend - src + last_block >= 64) { - while ((bufferptr - buffer_start) % 64 != 0 && src < srcend) { uint8_t val = to_base64[uint8_t(*src)]; *bufferptr = char(val); @@ -20419,11 +20485,7 @@ compress_decode_base64(char *dst, const chartype *src, size_t srclen, } for (; buffer_start + 64 <= bufferptr; buffer_start += 64) { - if (dst >= end_of_safe_64byte_zone) { - base64_decode_block_safe(dst, buffer_start); - } else { - base64_decode_block(dst, buffer_start); - } + base64_decode_block(dst, buffer_start); dst += 48; } if ((bufferptr - buffer_start) % 64 != 0) { @@ -20433,6 +20495,7 @@ compress_decode_base64(char *dst, const chartype *src, size_t srclen, (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) + (uint32_t(uint8_t(buffer_start[3])) << 0 * 6)) << 8; + // lsx is little-endian triple = scalar::u32_swap_bytes(triple); std::memcpy(dst, &triple, 4); @@ -20445,6 +20508,7 @@ compress_decode_base64(char *dst, const chartype *src, size_t srclen, (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) + (uint32_t(uint8_t(buffer_start[3])) << 0 * 6)) << 8; + // lsx is little-endian triple = scalar::u32_swap_bytes(triple); std::memcpy(dst, &triple, 3); @@ -20504,24 +20568,22 @@ compress_decode_base64(char *dst, const chartype *src, size_t srclen, } return {SUCCESS, srclen, size_t(dst - dstinit)}; } -/* end file src/lasx/lasx_base64.cpp */ -/* begin file src/lasx/lasx_find.cpp */ +/* end file src/lsx/lsx_base64.cpp */ +/* begin file src/lsx/lsx_find.cpp */ simdutf_really_inline const char *util_find(const char *start, const char *end, char character) noexcept { if (start >= end) return end; - const int step = 32; - __m256i char_vec = __lasx_xvreplgr2vr_b(static_cast(character)); + const int step = 16; + __m128i char_vec = __lsx_vreplgr2vr_b(static_cast(character)); while (end - start >= step) { - __m256i data = __lasx_xvld(reinterpret_cast(start), 0); - __m256i cmp = __lasx_xvseq_b(data, char_vec); - if (__lasx_xbnz_v(cmp)) { - __m256i res = __lasx_xvmsknz_b(cmp); - uint32_t mask0 = __lasx_xvpickve2gr_wu(res, 0); - uint32_t mask1 = __lasx_xvpickve2gr_wu(res, 4); - uint32_t mask = (mask0 | (mask1 << 16)); + __m128i data = __lsx_vld(reinterpret_cast(start), 0); + __m128i cmp = __lsx_vseq_b(data, char_vec); + if (__lsx_bnz_v(cmp)) { + uint16_t mask = + static_cast(__lsx_vpickve2gr_hu(__lsx_vmsknz_b(cmp), 0)); return start + trailing_zeroes(mask); } @@ -20544,17 +20606,15 @@ simdutf_really_inline const char16_t *util_find(const char16_t *start, if (start >= end) return end; - const int step = 16; - __m256i char_vec = __lasx_xvreplgr2vr_h(static_cast(character)); + const int step = 8; + __m128i char_vec = __lsx_vreplgr2vr_h(static_cast(character)); while (end - start >= step) { - __m256i data = __lasx_xvld(reinterpret_cast(start), 0); - __m256i cmp = __lasx_xvseq_h(data, char_vec); - if (__lasx_xbnz_v(cmp)) { - __m256i res = __lasx_xvmsknz_b(cmp); - uint32_t mask0 = __lasx_xvpickve2gr_wu(res, 0); - uint32_t mask1 = __lasx_xvpickve2gr_wu(res, 4); - uint32_t mask = (mask0 | (mask1 << 16)); + __m128i data = __lsx_vld(reinterpret_cast(start), 0); + __m128i cmp = __lsx_vseq_h(data, char_vec); + if (__lsx_bnz_v(cmp)) { + uint16_t mask = + static_cast(__lsx_vpickve2gr_hu(__lsx_vmsknz_b(cmp), 0)); return start + trailing_zeroes(mask) / 2; } @@ -20570,15 +20630,15 @@ simdutf_really_inline const char16_t *util_find(const char16_t *start, return end; } -/* end file src/lasx/lasx_find.cpp */ +/* end file src/lsx/lsx_find.cpp */ } // namespace -} // namespace lasx +} // namespace lsx } // namespace simdutf /* begin file src/generic/buf_block_reader.h */ namespace simdutf { -namespace lasx { +namespace lsx { namespace { // Walks through a buffer in block-sized increments, loading the last part with @@ -20609,40 +20669,6 @@ template struct buf_block_reader { size_t idx; }; -// Routines to print masks and text for debugging bitmask operations -simdutf_unused static char *format_input_text_64(const uint8_t *text) { - static char *buf = - reinterpret_cast(malloc(sizeof(simd8x64) + 1)); - for (size_t i = 0; i < sizeof(simd8x64); i++) { - buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]); - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -// Routines to print masks and text for debugging bitmask operations -simdutf_unused static char *format_input_text(const simd8x64 &in) { - static char *buf = - reinterpret_cast(malloc(sizeof(simd8x64) + 1)); - in.store(reinterpret_cast(buf)); - for (size_t i = 0; i < sizeof(simd8x64); i++) { - if (buf[i] < ' ') { - buf[i] = '_'; - } - } - buf[sizeof(simd8x64)] = '\0'; - return buf; -} - -simdutf_unused static char *format_mask(uint64_t mask) { - static char *buf = reinterpret_cast(malloc(64 + 1)); - for (size_t i = 0; i < 64; i++) { - buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' '; - } - buf[64] = '\0'; - return buf; -} - template simdutf_really_inline buf_block_reader::buf_block_reader(const uint8_t *_buf, size_t _len) @@ -20684,15 +20710,81 @@ simdutf_really_inline void buf_block_reader::advance() { } } // unnamed namespace -} // namespace lasx +} // namespace lsx } // namespace simdutf /* end file src/generic/buf_block_reader.h */ +/* begin file src/generic/base64lengths.h */ +namespace simdutf { +namespace lsx { +namespace { +namespace base64_lengths { + +simdutf_warn_unused size_t binary_length_from_base64(const char *input, + size_t length) { + size_t pos = 0; + size_t count = 0; + for (; pos + 64 <= length; pos += 64) { + simd8x64 block(reinterpret_cast(input + pos)); + uint64_t maybe_base64 = block.gteq(33); // >= 33 which is '!' in ASCII + count += count_ones(maybe_base64); + } + while (pos < length) { + count += (input[pos] > 0x20) ? 1 : 0; + pos++; + } + // Count padding at the end. + size_t padding = 0; + pos = length; + while (pos > 0 && padding < 2) { + char c = input[--pos]; + if (c == '=') { + padding++; + } else if (c > ' ') { + break; + } + } + return ((count - padding) * 3) / 4; +} + +simdutf_warn_unused size_t binary_length_from_base64(const char16_t *input, + size_t length) { + size_t pos = 0; + size_t count = 0; + for (; pos + 32 <= length; pos += 32) { + simd16x32 block(reinterpret_cast(input + pos)); + uint64_t maybe_base64 = block.gteq(33); // >= 33 which is '!' in ASCII + count += count_ones(maybe_base64); + } + while (pos < length) { + count += (input[pos] > 0x20) ? 1 : 0; + pos++; + } + // Count padding at the end. + size_t padding = 0; + pos = length; + while (pos > 0 && padding < 2) { + char16_t c = input[--pos]; + if (c == '=') { + padding++; + } else if (c > ' ') { + break; + } + } + return ((count - padding) * 3) / 4; +} + +} // namespace base64_lengths +} // unnamed namespace +} // namespace lsx +} // namespace simdutf +/* end file src/generic/base64lengths.h */ + // // Implementation-specific overrides // namespace simdutf { -namespace lasx { +namespace lsx { simdutf_warn_unused result implementation::base64_to_binary( const char *input, size_t length, char *output, base64_options options, @@ -20824,6 +20916,13 @@ size_t implementation::binary_to_base64(const char *input, size_t length, } } +size_t implementation::binary_to_base64_with_lines( + const char *input, size_t length, char *output, size_t line_length, + base64_options options) const noexcept { + return scalar::base64::tail_encode_base64_impl(output, input, length, + options, line_length); +} + const char *implementation::find(const char *start, const char *end, char character) const noexcept { return util_find(start, end, character); @@ -20834,14 +20933,404 @@ const char16_t *implementation::find(const char16_t *start, const char16_t *end, return util_find(start, end, character); } -} // namespace lasx +simdutf_warn_unused size_t implementation::binary_length_from_base64( + const char *input, size_t length) const noexcept { + return base64_lengths::binary_length_from_base64(input, length); +} + +simdutf_warn_unused size_t implementation::binary_length_from_base64( + const char16_t *input, size_t length) const noexcept { + return base64_lengths::binary_length_from_base64(input, length); +} + +} // namespace lsx } // namespace simdutf -/* begin file src/simdutf/lasx/end.h */ +/* begin file src/simdutf/lsx/end.h */ #undef SIMDUTF_SIMD_HAS_UNSIGNED_CMP -/* end file src/simdutf/lasx/end.h */ -/* end file src/lasx/implementation.cpp */ +/* end file src/simdutf/lsx/end.h */ +/* end file src/lsx/implementation.cpp */ +#endif + +/* begin file src/simdutf_c.cpp */ +/* begin file include/simdutf_c.h */ +/*** + * simdutf_c.h.h - C API for simdutf + * This is currently experimental. + * We are committed to keeping the C API, but there might be mistakes in our + * implementation. Please report any issues you find. + */ + +#ifndef SIMDUTF_C_H +#define SIMDUTF_C_H + +#include +#include +#include + +#ifdef __has_include + #if __has_include() + #include + #else // __has_include() + #define char16_t uint16_t + #define char32_t uint32_t + #endif // __has_include() +#else // __has_include() + #define char16_t uint16_t + #define char32_t uint32_t +#endif // __has_include + +#ifdef __cplusplus +extern "C" { #endif +/* C-friendly subset of simdutf errors */ +typedef enum simdutf_error_code { + SIMDUTF_ERROR_SUCCESS = 0, + SIMDUTF_ERROR_HEADER_BITS, + SIMDUTF_ERROR_TOO_SHORT, + SIMDUTF_ERROR_TOO_LONG, + SIMDUTF_ERROR_OVERLONG, + SIMDUTF_ERROR_TOO_LARGE, + SIMDUTF_ERROR_SURROGATE, + SIMDUTF_ERROR_INVALID_BASE64_CHARACTER, + SIMDUTF_ERROR_BASE64_INPUT_REMAINDER, + SIMDUTF_ERROR_BASE64_EXTRA_BITS, + SIMDUTF_ERROR_OUTPUT_BUFFER_TOO_SMALL, + SIMDUTF_ERROR_OTHER +} simdutf_error_code; + +typedef struct simdutf_result { + simdutf_error_code error; + size_t count; /* position of error or number of code units validated */ +} simdutf_result; + +typedef struct simdutf_full_result { + simdutf_error_code error; + size_t input_count; /* number of input units consumed */ + size_t output_count; /* number of output bytes written */ +} simdutf_full_result; + +typedef enum simdutf_encoding_type { + SIMDUTF_ENCODING_UNSPECIFIED = 0, + SIMDUTF_ENCODING_UTF8 = 1, + SIMDUTF_ENCODING_UTF16_LE = 2, + SIMDUTF_ENCODING_UTF16_BE = 4, + SIMDUTF_ENCODING_UTF32_LE = 8, + SIMDUTF_ENCODING_UTF32_BE = 16 +} simdutf_encoding_type; + +/* Validate UTF-8: returns true iff input is valid UTF-8 */ +bool simdutf_validate_utf8(const char *buf, size_t len); + +/* Validate UTF-8 with detailed result */ +simdutf_result simdutf_validate_utf8_with_errors(const char *buf, size_t len); + +/* Encoding detection */ +simdutf_encoding_type simdutf_autodetect_encoding(const char *input, + size_t length); +int simdutf_detect_encodings(const char *input, size_t length); + +/* ASCII validation */ +bool simdutf_validate_ascii(const char *buf, size_t len); +simdutf_result simdutf_validate_ascii_with_errors(const char *buf, size_t len); + +/* UTF-16 ASCII checks */ +bool simdutf_validate_utf16_as_ascii(const char16_t *buf, size_t len); +bool simdutf_validate_utf16be_as_ascii(const char16_t *buf, size_t len); +bool simdutf_validate_utf16le_as_ascii(const char16_t *buf, size_t len); + +/* UTF-16/UTF-8/UTF-32 validation (native/endian-specific) */ +bool simdutf_validate_utf16(const char16_t *buf, size_t len); +bool simdutf_validate_utf16le(const char16_t *buf, size_t len); +bool simdutf_validate_utf16be(const char16_t *buf, size_t len); +simdutf_result simdutf_validate_utf16_with_errors(const char16_t *buf, + size_t len); +simdutf_result simdutf_validate_utf16le_with_errors(const char16_t *buf, + size_t len); +simdutf_result simdutf_validate_utf16be_with_errors(const char16_t *buf, + size_t len); + +bool simdutf_validate_utf32(const char32_t *buf, size_t len); +simdutf_result simdutf_validate_utf32_with_errors(const char32_t *buf, + size_t len); + +/* to_well_formed UTF-16 helpers */ +void simdutf_to_well_formed_utf16le(const char16_t *input, size_t len, + char16_t *output); +void simdutf_to_well_formed_utf16be(const char16_t *input, size_t len, + char16_t *output); +void simdutf_to_well_formed_utf16(const char16_t *input, size_t len, + char16_t *output); + +/* Counting */ +size_t simdutf_count_utf16(const char16_t *input, size_t length); +size_t simdutf_count_utf16le(const char16_t *input, size_t length); +size_t simdutf_count_utf16be(const char16_t *input, size_t length); +size_t simdutf_count_utf8(const char *input, size_t length); + +/* Length estimators */ +size_t simdutf_utf8_length_from_latin1(const char *input, size_t length); +size_t simdutf_latin1_length_from_utf8(const char *input, size_t length); +size_t simdutf_latin1_length_from_utf16(size_t length); +size_t simdutf_latin1_length_from_utf32(size_t length); +size_t simdutf_utf16_length_from_utf8(const char *input, size_t length); +size_t simdutf_utf32_length_from_utf8(const char *input, size_t length); +size_t simdutf_utf8_length_from_utf16(const char16_t *input, size_t length); +size_t simdutf_utf8_length_from_utf32(const char32_t *input, size_t length); +simdutf_result +simdutf_utf8_length_from_utf16_with_replacement(const char16_t *input, + size_t length); +size_t simdutf_utf8_length_from_utf16le(const char16_t *input, size_t length); +size_t simdutf_utf8_length_from_utf16be(const char16_t *input, size_t length); +simdutf_result +simdutf_utf8_length_from_utf16le_with_replacement(const char16_t *input, + size_t length); +simdutf_result +simdutf_utf8_length_from_utf16be_with_replacement(const char16_t *input, + size_t length); + +/* Conversions: latin1 <-> utf8, utf8 <-> utf16/utf32, utf16 <-> utf8, etc. */ +size_t simdutf_convert_latin1_to_utf8(const char *input, size_t length, + char *output); +size_t simdutf_convert_latin1_to_utf8_safe(const char *input, size_t length, + char *output, size_t utf8_len); +size_t simdutf_convert_latin1_to_utf16le(const char *input, size_t length, + char16_t *output); +size_t simdutf_convert_latin1_to_utf16be(const char *input, size_t length, + char16_t *output); +size_t simdutf_convert_latin1_to_utf16(const char *input, size_t length, + char16_t *output); +size_t simdutf_convert_latin1_to_utf32(const char *input, size_t length, + char32_t *output); + +size_t simdutf_convert_utf8_to_latin1(const char *input, size_t length, + char *output); +size_t simdutf_convert_utf8_to_utf16le(const char *input, size_t length, + char16_t *output); +size_t simdutf_convert_utf8_to_utf16be(const char *input, size_t length, + char16_t *output); +size_t simdutf_convert_utf8_to_utf16(const char *input, size_t length, + char16_t *output); + +size_t simdutf_convert_utf8_to_utf32(const char *input, size_t length, + char32_t *output); +simdutf_result simdutf_convert_utf8_to_latin1_with_errors(const char *input, + size_t length, + char *output); +simdutf_result simdutf_convert_utf8_to_utf16_with_errors(const char *input, + size_t length, + char16_t *output); +simdutf_result simdutf_convert_utf8_to_utf16le_with_errors(const char *input, + size_t length, + char16_t *output); +simdutf_result simdutf_convert_utf8_to_utf16be_with_errors(const char *input, + size_t length, + char16_t *output); +simdutf_result simdutf_convert_utf8_to_utf32_with_errors(const char *input, + size_t length, + char32_t *output); + +/* Conversions assuming valid input */ +size_t simdutf_convert_valid_utf8_to_latin1(const char *input, size_t length, + char *output); +size_t simdutf_convert_valid_utf8_to_utf16le(const char *input, size_t length, + char16_t *output); +size_t simdutf_convert_valid_utf8_to_utf16be(const char *input, size_t length, + char16_t *output); +size_t simdutf_convert_valid_utf8_to_utf32(const char *input, size_t length, + char32_t *output); + +/* UTF-16 -> UTF-8 and related conversions */ +size_t simdutf_convert_utf16_to_utf8(const char16_t *input, size_t length, + char *output); +size_t simdutf_convert_utf16le_to_utf8(const char16_t *input, size_t length, + char *output); +size_t simdutf_convert_utf16be_to_utf8(const char16_t *input, size_t length, + char *output); +size_t simdutf_convert_utf16_to_utf8_safe(const char16_t *input, size_t length, + char *output, size_t utf8_len); +size_t simdutf_convert_utf16_to_latin1(const char16_t *input, size_t length, + char *output); +size_t simdutf_convert_utf16le_to_latin1(const char16_t *input, size_t length, + char *output); +size_t simdutf_convert_utf16be_to_latin1(const char16_t *input, size_t length, + char *output); +simdutf_result +simdutf_convert_utf16_to_latin1_with_errors(const char16_t *input, + size_t length, char *output); +simdutf_result +simdutf_convert_utf16le_to_latin1_with_errors(const char16_t *input, + size_t length, char *output); +simdutf_result +simdutf_convert_utf16be_to_latin1_with_errors(const char16_t *input, + size_t length, char *output); + +simdutf_result simdutf_convert_utf16_to_utf8_with_errors(const char16_t *input, + size_t length, + char *output); +simdutf_result +simdutf_convert_utf16le_to_utf8_with_errors(const char16_t *input, + size_t length, char *output); +simdutf_result +simdutf_convert_utf16be_to_utf8_with_errors(const char16_t *input, + size_t length, char *output); + +size_t simdutf_convert_valid_utf16_to_utf8(const char16_t *input, size_t length, + char *output); +size_t simdutf_convert_valid_utf16_to_latin1(const char16_t *input, + size_t length, char *output); +size_t simdutf_convert_valid_utf16le_to_latin1(const char16_t *input, + size_t length, char *output); +size_t simdutf_convert_valid_utf16be_to_latin1(const char16_t *input, + size_t length, char *output); + +size_t simdutf_convert_valid_utf16le_to_utf8(const char16_t *input, + size_t length, char *output); +size_t simdutf_convert_valid_utf16be_to_utf8(const char16_t *input, + size_t length, char *output); + +/* UTF-16 <-> UTF-32 conversions */ +size_t simdutf_convert_utf16_to_utf32(const char16_t *input, size_t length, + char32_t *output); +size_t simdutf_convert_utf16le_to_utf32(const char16_t *input, size_t length, + char32_t *output); +size_t simdutf_convert_utf16be_to_utf32(const char16_t *input, size_t length, + char32_t *output); +simdutf_result simdutf_convert_utf16_to_utf32_with_errors(const char16_t *input, + size_t length, + char32_t *output); +simdutf_result +simdutf_convert_utf16le_to_utf32_with_errors(const char16_t *input, + size_t length, char32_t *output); +simdutf_result +simdutf_convert_utf16be_to_utf32_with_errors(const char16_t *input, + size_t length, char32_t *output); + +/* Valid UTF-16 conversions */ +size_t simdutf_convert_valid_utf16_to_utf32(const char16_t *input, + size_t length, char32_t *output); +size_t simdutf_convert_valid_utf16le_to_utf32(const char16_t *input, + size_t length, char32_t *output); +size_t simdutf_convert_valid_utf16be_to_utf32(const char16_t *input, + size_t length, char32_t *output); + +/* UTF-32 -> ... conversions */ +size_t simdutf_convert_utf32_to_utf8(const char32_t *input, size_t length, + char *output); +simdutf_result simdutf_convert_utf32_to_utf8_with_errors(const char32_t *input, + size_t length, + char *output); +size_t simdutf_convert_valid_utf32_to_utf8(const char32_t *input, size_t length, + char *output); + +size_t simdutf_convert_utf32_to_utf16(const char32_t *input, size_t length, + char16_t *output); +size_t simdutf_convert_utf32_to_utf16le(const char32_t *input, size_t length, + char16_t *output); +size_t simdutf_convert_utf32_to_utf16be(const char32_t *input, size_t length, + char16_t *output); +simdutf_result +simdutf_convert_utf32_to_latin1_with_errors(const char32_t *input, + size_t length, char *output); + +/* --- Find helpers --- */ +const char *simdutf_find(const char *start, const char *end, char character); +const char16_t *simdutf_find_utf16(const char16_t *start, const char16_t *end, + char16_t character); + +/* --- Base64 enums and helpers --- */ +typedef enum simdutf_base64_options { + SIMDUTF_BASE64_DEFAULT = 0, + SIMDUTF_BASE64_URL = 1, + SIMDUTF_BASE64_DEFAULT_NO_PADDING = 2, + SIMDUTF_BASE64_URL_WITH_PADDING = 3, + SIMDUTF_BASE64_DEFAULT_ACCEPT_GARBAGE = 4, + SIMDUTF_BASE64_URL_ACCEPT_GARBAGE = 5, + SIMDUTF_BASE64_DEFAULT_OR_URL = 8, + SIMDUTF_BASE64_DEFAULT_OR_URL_ACCEPT_GARBAGE = 12 +} simdutf_base64_options; + +typedef enum simdutf_last_chunk_handling_options { + SIMDUTF_LAST_CHUNK_LOOSE = 0, + SIMDUTF_LAST_CHUNK_STRICT = 1, + SIMDUTF_LAST_CHUNK_STOP_BEFORE_PARTIAL = 2, + SIMDUTF_LAST_CHUNK_ONLY_FULL_CHUNKS = 3 +} simdutf_last_chunk_handling_options; + +/* maximal binary length estimators */ +size_t simdutf_maximal_binary_length_from_base64(const char *input, + size_t length); +size_t simdutf_maximal_binary_length_from_base64_utf16(const char16_t *input, + size_t length); + +/* base64 decoding/encoding */ +simdutf_result simdutf_base64_to_binary( + const char *input, size_t length, char *output, + simdutf_base64_options options, + simdutf_last_chunk_handling_options last_chunk_options); +simdutf_result simdutf_base64_to_binary_utf16( + const char16_t *input, size_t length, char *output, + simdutf_base64_options options, + simdutf_last_chunk_handling_options last_chunk_options); + +size_t simdutf_base64_length_from_binary(size_t length, + simdutf_base64_options options); +size_t simdutf_base64_length_from_binary_with_lines( + size_t length, simdutf_base64_options options, size_t line_length); + +size_t simdutf_binary_to_base64(const char *input, size_t length, char *output, + simdutf_base64_options options); +size_t simdutf_binary_to_base64_with_lines(const char *input, size_t length, + char *output, size_t line_length, + simdutf_base64_options options); + +/* safe decoding that provides an in/out outlen parameter */ +simdutf_result simdutf_base64_to_binary_safe( + const char *input, size_t length, char *output, size_t *outlen, + simdutf_base64_options options, + simdutf_last_chunk_handling_options last_chunk_options, + bool decode_up_to_bad_char); +simdutf_result simdutf_base64_to_binary_safe_utf16( + const char16_t *input, size_t length, char *output, size_t *outlen, + simdutf_base64_options options, + simdutf_last_chunk_handling_options last_chunk_options, + bool decode_up_to_bad_char); + +/* detailed decoding returning input_count and output_count */ +simdutf_full_result simdutf_base64_to_binary_details( + const char *input, size_t length, char *output, + simdutf_base64_options options, + simdutf_last_chunk_handling_options last_chunk_options); +simdutf_full_result simdutf_base64_to_binary_details_utf16( + const char16_t *input, size_t length, char *output, + simdutf_base64_options options, + simdutf_last_chunk_handling_options last_chunk_options); + +/* single-character base64 validation */ +bool simdutf_base64_valid(char input, simdutf_base64_options options); +bool simdutf_base64_valid_utf16(char16_t input, simdutf_base64_options options); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SIMDUTF_C_H */ +/* end file include/simdutf_c.h */ + +static simdutf_result to_c_result(const simdutf::result &r) { + simdutf_result out; + out.error = static_cast(r.error); + out.count = r.count; + return out; +} + +/* The C wrapper depends on the library features. Only expose the C API + when all relevant feature is enabled. This helps the + single-header generator to omit the C wrapper when features are + disabled. */ +// clang-format off +// clang-format on +/* end file src/simdutf_c.cpp */ SIMDUTF_POP_DISABLE_WARNINGS /* end file src/simdutf.cpp */ diff --git a/cpp/simdutf.h b/cpp/simdutf.h index 3f8d4f3..3900b0c 100644 --- a/cpp/simdutf.h +++ b/cpp/simdutf.h @@ -1,4 +1,4 @@ -/* auto-generated on 2025-09-10 16:01:38 -0400. Do not edit! */ +/* auto-generated on 2026-05-07 06:54:06 +0200. Do not edit! */ /* begin file include/simdutf.h */ #ifndef SIMDUTF_H #define SIMDUTF_H @@ -20,6 +20,11 @@ #endif #endif +// C++ 26 +#if !defined(SIMDUTF_CPLUSPLUS26) && (SIMDUTF_CPLUSPLUS >= 202602L) + #define SIMDUTF_CPLUSPLUS26 1 +#endif + // C++ 23 #if !defined(SIMDUTF_CPLUSPLUS23) && (SIMDUTF_CPLUSPLUS >= 202302L) #define SIMDUTF_CPLUSPLUS23 1 @@ -45,8 +50,8 @@ #define SIMDUTF_CPLUSPLUS11 1 #endif -#ifndef SIMDUTF_CPLUSPLUS11 - #error simdutf requires a compiler compliant with the C++11 standard +#ifndef SIMDUTF_CPLUSPLUS17 + #error simdutf requires a compiler compliant with the C++17 standard #endif #endif // SIMDUTF_COMPILER_CHECK_H @@ -63,6 +68,7 @@ #include #include #include +#include #include #ifndef _WIN32 // strcasecmp, strncasecmp @@ -200,9 +206,25 @@ #elif defined(__loongarch_lp64) #if defined(__loongarch_sx) && defined(__loongarch_asx) #define SIMDUTF_IS_LSX 1 - #define SIMDUTF_IS_LASX 1 + #define SIMDUTF_IS_LASX 1 // We can always run both #elif defined(__loongarch_sx) #define SIMDUTF_IS_LSX 1 + // Adjust for runtime dispatching support. + #if defined(__GNUC__) && !defined(__clang__) && \ + !defined(__INTEL_COMPILER) && !defined(__NVCOMPILER) + #if __GNUC__ > 15 || (__GNUC__ == 15 && __GNUC_MINOR__ >= 0) + // We are ok, we will support runtime dispatch for LASX. + #else + // We disable runtime dispatch for LASX, which means that we will not be + // able to use LASX even if it is supported by the hardware. Loongson + // users should update to GCC 15 or better. + #define SIMDUTF_IMPLEMENTATION_LASX 0 + #endif + #else + // We are not using GCC, so we assume that we can support runtime dispatch + // for LASX. https://godbolt.org/z/jcMnrjYhs + #define SIMDUTF_IMPLEMENTATION_LASX 0 + #endif #endif #else // The simdutf library is designed @@ -250,7 +272,7 @@ // // We are going to use runtime dispatch. -#ifdef SIMDUTF_IS_X86_64 +#if defined(SIMDUTF_IS_X86_64) || defined(SIMDUTF_IS_LSX) #ifdef __clang__ // clang does not have GCC push pop // warning: clang attribute push can't be used within a namespace in clang @@ -267,7 +289,7 @@ #define SIMDUTF_UNTARGET_REGION _Pragma("GCC pop_options") #endif // clang then gcc -#endif // x86 +#endif // defined(SIMDUTF_IS_X86_64) || defined(SIMDUTF_IS_LSX) // Default target region macros don't do anything. #ifndef SIMDUTF_TARGET_REGION @@ -526,6 +548,14 @@ #endif // MSC_VER +// Will evaluate to constexpr in C++23 or later. This makes it possible to mark +// functions constexpr if the "if consteval" feature is available to use. +#if SIMDUTF_CPLUSPLUS23 + #define simdutf_constexpr23 constexpr +#else + #define simdutf_constexpr23 +#endif + #ifndef SIMDUTF_DLLIMPORTEXPORT #if defined(SIMDUTF_VISUAL_STUDIO) // Visual Studio /** @@ -576,7 +606,13 @@ /* begin file include/simdutf/encoding_types.h */ #ifndef SIMDUTF_ENCODING_TYPES_H #define SIMDUTF_ENCODING_TYPES_H -#include +#include + +#if !defined(SIMDUTF_NO_STD_TEXT_ENCODING) && \ + defined(__cpp_lib_text_encoding) && __cpp_lib_text_encoding >= 202306L + #define SIMDUTF_HAS_STD_TEXT_ENCODING 1 + #include +#endif namespace simdutf { @@ -591,11 +627,27 @@ enum encoding_type { unspecified = 0 }; -enum endianness { LITTLE = 0, BIG = 1 }; +#ifndef SIMDUTF_IS_BIG_ENDIAN + #error "SIMDUTF_IS_BIG_ENDIAN needs to be defined." +#endif + +enum endianness { + LITTLE = 0, + BIG = 1, + NATIVE = +#if SIMDUTF_IS_BIG_ENDIAN + BIG +#else + LITTLE +#endif +}; -bool match_system(endianness e); +simdutf_warn_unused simdutf_really_inline constexpr bool +match_system(endianness e) { + return e == endianness::NATIVE; +} -std::string to_string(encoding_type bom); +simdutf_warn_unused std::string_view to_string(encoding_type bom); // Note that BOM for UTF8 is discouraged. namespace BOM { @@ -607,23 +659,144 @@ namespace BOM { * @return the corresponding encoding */ -encoding_type check_bom(const uint8_t *byte, size_t length); -encoding_type check_bom(const char *byte, size_t length); +simdutf_warn_unused encoding_type check_bom(const uint8_t *byte, size_t length); +simdutf_warn_unused encoding_type check_bom(const char *byte, size_t length); /** * Returns the size, in bytes, of the BOM for a given encoding type. * Note that UTF8 BOM are discouraged. * @param bom the encoding type * @return the size in bytes of the corresponding BOM */ -size_t bom_byte_size(encoding_type bom); +simdutf_warn_unused size_t bom_byte_size(encoding_type bom); } // namespace BOM + +#ifdef SIMDUTF_HAS_STD_TEXT_ENCODING +/** + * Convert a simdutf encoding type to a std::text_encoding. + * + * @param enc the simdutf encoding type + * @return the corresponding std::text_encoding, or + * std::text_encoding::id::unknown for unspecified/unsupported + */ +simdutf_warn_unused constexpr std::text_encoding +to_std_encoding(encoding_type enc) noexcept { + switch (enc) { + case UTF8: + return std::text_encoding(std::text_encoding::id::UTF8); + case UTF16_LE: + return std::text_encoding(std::text_encoding::id::UTF16LE); + case UTF16_BE: + return std::text_encoding(std::text_encoding::id::UTF16BE); + case UTF32_LE: + return std::text_encoding(std::text_encoding::id::UTF32LE); + case UTF32_BE: + return std::text_encoding(std::text_encoding::id::UTF32BE); + case Latin1: + return std::text_encoding(std::text_encoding::id::ISOLatin1); + case unspecified: + default: + return std::text_encoding(std::text_encoding::id::unknown); + } +} + +/** + * Convert a std::text_encoding to a simdutf encoding type. + * + * @param enc the std::text_encoding + * @return the corresponding simdutf encoding type, or + * encoding_type::unspecified if the encoding is not supported + */ +simdutf_warn_unused constexpr encoding_type +from_std_encoding(const std::text_encoding &enc) noexcept { + switch (enc.mib()) { + case std::text_encoding::id::UTF8: + return UTF8; + case std::text_encoding::id::UTF16LE: + return UTF16_LE; + case std::text_encoding::id::UTF16BE: + return UTF16_BE; + case std::text_encoding::id::UTF32LE: + return UTF32_LE; + case std::text_encoding::id::UTF32BE: + return UTF32_BE; + case std::text_encoding::id::ISOLatin1: + return Latin1; + default: + return unspecified; + } +} + +/** + * Get the native-endian UTF-16 encoding type for this system. + * + * @return UTF16_LE on little-endian systems, UTF16_BE on big-endian systems + */ +simdutf_warn_unused constexpr encoding_type native_utf16_encoding() noexcept { + #if SIMDUTF_IS_BIG_ENDIAN + return UTF16_BE; + #else + return UTF16_LE; + #endif +} + +/** + * Get the native-endian UTF-32 encoding type for this system. + * + * @return UTF32_LE on little-endian systems, UTF32_BE on big-endian systems + */ +simdutf_warn_unused constexpr encoding_type native_utf32_encoding() noexcept { + #if SIMDUTF_IS_BIG_ENDIAN + return UTF32_BE; + #else + return UTF32_LE; + #endif +} + +/** + * Convert a std::text_encoding to a simdutf encoding type, + * using native endianness for UTF-16/UTF-32 without explicit endianness. + * + * When the input is std::text_encoding::id::UTF16 or UTF32 (without LE/BE + * suffix), this returns the native-endian simdutf variant. + * + * @param enc the std::text_encoding + * @return the corresponding simdutf encoding type, or + * encoding_type::unspecified if the encoding is not supported + */ +simdutf_warn_unused constexpr encoding_type +from_std_encoding_native(const std::text_encoding &enc) noexcept { + switch (enc.mib()) { + case std::text_encoding::id::UTF8: + return UTF8; + case std::text_encoding::id::UTF16: + return native_utf16_encoding(); + case std::text_encoding::id::UTF16LE: + return UTF16_LE; + case std::text_encoding::id::UTF16BE: + return UTF16_BE; + case std::text_encoding::id::UTF32: + return native_utf32_encoding(); + case std::text_encoding::id::UTF32LE: + return UTF32_LE; + case std::text_encoding::id::UTF32BE: + return UTF32_BE; + case std::text_encoding::id::ISOLatin1: + return Latin1; + default: + return unspecified; + } +} +#endif // SIMDUTF_HAS_STD_TEXT_ENCODING + } // namespace simdutf #endif /* end file include/simdutf/encoding_types.h */ /* begin file include/simdutf/error.h */ #ifndef SIMDUTF_ERROR_H #define SIMDUTF_ERROR_H +#include + namespace simdutf { enum error_code { @@ -641,9 +814,19 @@ enum error_code { // U+10FFFF,less than or equal than U+7F for ASCII OR less than // equal than U+FF for Latin1 SURROGATE, // The decoded character must be not be in U+D800...DFFF (UTF-8 or - // UTF-32) OR a high surrogate must be followed by a low surrogate + // UTF-32) + // OR + // a high surrogate must be followed by a low surrogate // and a low surrogate must be preceded by a high surrogate - // (UTF-16) OR there must be no surrogate at all (Latin1) + // (UTF-16) + // OR + // there must be no surrogate at all and one is + // found (Latin1 functions) + // OR + // *specifically* for the function + // utf8_length_from_utf16_with_replacement, a surrogate (whether + // in error or not) has been found (I.e., whether we are in the + // Basic Multilingual Plane or not). INVALID_BASE64_CHARACTER, // Found a character that cannot be part of a valid // base64 string. This may include a misplaced // padding character ('='). @@ -655,7 +838,7 @@ enum error_code { OUTPUT_BUFFER_TOO_SMALL, // The provided buffer is too small. OTHER // Not related to validation/transcoding. }; -#if SIMDUTF_CPLUSPLUS17 + inline std::string_view error_to_string(error_code code) noexcept { switch (code) { case SUCCESS: @@ -684,7 +867,6 @@ inline std::string_view error_to_string(error_code code) noexcept { return "OTHER"; } } -#endif struct result { error_code error; @@ -692,17 +874,18 @@ struct result { // case of success, indicates the number of code units // validated/written. - simdutf_really_inline result() noexcept + simdutf_really_inline simdutf_constexpr23 result() noexcept : error{error_code::SUCCESS}, count{0} {} - simdutf_really_inline result(error_code err, size_t pos) noexcept + simdutf_really_inline simdutf_constexpr23 result(error_code err, + size_t pos) noexcept : error{err}, count{pos} {} - simdutf_really_inline bool is_ok() const noexcept { + simdutf_really_inline simdutf_constexpr23 bool is_ok() const noexcept { return error == error_code::SUCCESS; } - simdutf_really_inline bool is_err() const noexcept { + simdutf_really_inline simdutf_constexpr23 bool is_err() const noexcept { return error != error_code::SUCCESS; } }; @@ -714,18 +897,19 @@ struct full_result { bool padding_error = false; // true if the error is due to padding, only // meaningful when error is not SUCCESS - simdutf_really_inline full_result() noexcept + simdutf_really_inline simdutf_constexpr23 full_result() noexcept : error{error_code::SUCCESS}, input_count{0}, output_count{0} {} - simdutf_really_inline full_result(error_code err, size_t pos_in, - size_t pos_out) noexcept + simdutf_really_inline simdutf_constexpr23 full_result(error_code err, + size_t pos_in, + size_t pos_out) noexcept : error{err}, input_count{pos_in}, output_count{pos_out} {} - simdutf_really_inline full_result(error_code err, size_t pos_in, - size_t pos_out, bool padding_err) noexcept + simdutf_really_inline simdutf_constexpr23 full_result( + error_code err, size_t pos_in, size_t pos_out, bool padding_err) noexcept : error{err}, input_count{pos_in}, output_count{pos_out}, padding_error{padding_err} {} - simdutf_really_inline operator result() const noexcept { + simdutf_really_inline simdutf_constexpr23 operator result() const noexcept { if (error == error_code::SUCCESS) { return result{error, output_count}; } else { @@ -749,18 +933,18 @@ SIMDUTF_DISABLE_UNDESIRED_WARNINGS #define SIMDUTF_SIMDUTF_VERSION_H /** The version of simdutf being used (major.minor.revision) */ -#define SIMDUTF_VERSION "7.4.0" +#define SIMDUTF_VERSION "9.0.0" namespace simdutf { enum { /** * The major version (MAJOR.minor.revision) of simdutf being used. */ - SIMDUTF_VERSION_MAJOR = 7, + SIMDUTF_VERSION_MAJOR = 9, /** * The minor version (major.MINOR.revision) of simdutf being used. */ - SIMDUTF_VERSION_MINOR = 4, + SIMDUTF_VERSION_MINOR = 0, /** * The revision (major.minor.REVISION) of simdutf being used. */ @@ -776,7 +960,6 @@ enum { #if !defined(SIMDUTF_NO_THREADS) #include #endif -#include #ifdef SIMDUTF_INTERNAL_TESTS #include #endif @@ -1107,14 +1290,13 @@ static inline uint32_t detect_supported_architectures() { #endif // SIMDutf_INTERNAL_ISADETECTION_H /* end file include/simdutf/internal/isadetection.h */ +#include #if SIMDUTF_SPAN #include #include #include #include -#endif -#if SIMDUTF_CPLUSPLUS17 - #include + #include // for std::unreachable #endif // The following defines are conditionally enabled/disabled during amalgamation. // By default all features are enabled, regular code shouldn't check them. Only @@ -1133,20 +1315,163 @@ static inline uint32_t detect_supported_architectures() { #define SIMDUTF_FEATURE_UTF32 0 #define SIMDUTF_FEATURE_BASE64 1 +#if SIMDUTF_CPLUSPLUS23 +/* begin file include/simdutf/constexpr_ptr.h */ +#ifndef SIMDUTF_CONSTEXPR_PTR_H +#define SIMDUTF_CONSTEXPR_PTR_H + +#include + namespace simdutf { +namespace detail { +/** + * The constexpr_ptr class is a workaround for reinterpret_cast not being + * allowed during constant evaluation. + */ +template + requires(sizeof(to) == sizeof(from)) +struct constexpr_ptr { + const from *p; + + constexpr explicit constexpr_ptr(const from *ptr) noexcept : p(ptr) {} + + constexpr to operator*() const noexcept { return static_cast(*p); } + + constexpr constexpr_ptr &operator++() noexcept { + ++p; + return *this; + } + + constexpr constexpr_ptr operator++(int) noexcept { + auto old = *this; + ++p; + return old; + } + + constexpr constexpr_ptr &operator--() noexcept { + --p; + return *this; + } + + constexpr constexpr_ptr operator--(int) noexcept { + auto old = *this; + --p; + return old; + } + + constexpr constexpr_ptr &operator+=(std::ptrdiff_t n) noexcept { + p += n; + return *this; + } + + constexpr constexpr_ptr &operator-=(std::ptrdiff_t n) noexcept { + p -= n; + return *this; + } + + constexpr constexpr_ptr operator+(std::ptrdiff_t n) const noexcept { + return constexpr_ptr{p + n}; + } + + constexpr constexpr_ptr operator-(std::ptrdiff_t n) const noexcept { + return constexpr_ptr{p - n}; + } + + constexpr std::ptrdiff_t operator-(const constexpr_ptr &o) const noexcept { + return p - o.p; + } + + constexpr to operator[](std::ptrdiff_t n) const noexcept { + return static_cast(*(p + n)); + } + + // to prevent compilation errors for memcpy, even if it is never + // called during constant evaluation + constexpr operator const void *() const noexcept { return p; } +}; + +template +constexpr constexpr_ptr constexpr_cast_ptr(from *p) noexcept { + return constexpr_ptr{p}; +} + +/** + * helper type for constexpr_writeptr, so it is possible to + * do "*ptr = val;" + */ +template +struct constexpr_write_ptr_proxy { + + constexpr explicit constexpr_write_ptr_proxy(TargetType *raw) : p(raw) {} + + constexpr constexpr_write_ptr_proxy &operator=(SrcType v) { + *p = static_cast(v); + return *this; + } + + TargetType *p; +}; + +/** + * helper for working around reinterpret_cast not being allowed during constexpr + * evaluation. will try to act as a SrcType* but actually write to the pointer + * given in the constructor, which is of another type TargetType + */ +template struct constexpr_write_ptr { + constexpr explicit constexpr_write_ptr(TargetType *raw) : p(raw) {} + + constexpr constexpr_write_ptr_proxy operator*() const { + return constexpr_write_ptr_proxy{p}; + } + + constexpr constexpr_write_ptr_proxy + operator[](std::ptrdiff_t n) const { + return constexpr_write_ptr_proxy{p + n}; + } + + constexpr constexpr_write_ptr &operator++() { + ++p; + return *this; + } + + constexpr constexpr_write_ptr operator++(int) { + constexpr_write_ptr old = *this; + ++p; + return old; + } + + constexpr std::ptrdiff_t operator-(const constexpr_write_ptr &other) const { + return p - other.p; + } + + TargetType *p; +}; + +template +constexpr auto constexpr_cast_writeptr(TargetType *raw) { + return constexpr_write_ptr{raw}; +} + +} // namespace detail +} // namespace simdutf +#endif +/* end file include/simdutf/constexpr_ptr.h */ +#endif #if SIMDUTF_SPAN /// helpers placed in namespace detail are not a part of the public API +namespace simdutf { namespace detail { /** * matches a byte, in the many ways C++ allows. note that these * are all distinct types. */ template -concept byte_like = std::is_same_v || // - std::is_same_v || // - std::is_same_v || // - std::is_same_v; +concept byte_like = std::is_same_v || // + std::is_same_v || // + std::is_same_v || // + std::is_same_v || // + std::is_same_v; template concept is_byte_like = byte_like>; @@ -1179,165 +1504,5619 @@ concept output_span_of_byte_like = requires(T &t) { { *t.data() } noexcept -> is_byte_like; { *t.data() } noexcept -> is_mutable; }; -} // namespace detail -#endif /** - * Validate the ASCII string as a UTF-16LE sequence. - * An UTF-16 sequence is considered an ASCII sequence - * if it could be converted to an ASCII string losslessly. - * - * Overridden by each implementation. - * - * @param buf the UTF-16LE string to validate. - * @param len the length of the string in bytes. - * @return true if and only if the string is valid ASCII. + * a pointer like object, when indexed, results in a byte like result. + * valid examples: char*, const char*, std::array + * invalid examples: int*, std::array */ -simdutf_warn_unused bool validate_utf16le_as_ascii(const char16_t *buf, - size_t len) noexcept; -#if SIMDUTF_SPAN -simdutf_really_inline simdutf_warn_unused bool -validate_utf16le_as_ascii(std::span input) noexcept { - return validate_utf16le_as_ascii(input.data(), input.size()); -} -#endif // SIMDUTF_SPAN - - #ifndef SIMDUTF_NEED_TRAILING_ZEROES - #define SIMDUTF_NEED_TRAILING_ZEROES 1 - #endif -// base64_options are used to specify the base64 encoding options. -// ASCII spaces are ' ', '\t', '\n', '\r', '\f' -// garbage characters are characters that are not part of the base64 alphabet -// nor ASCII spaces. -constexpr uint64_t base64_reverse_padding = - 2; /* modifier for base64_default and base64_url */ -enum base64_options : uint64_t { - base64_default = 0, /* standard base64 format (with padding) */ - base64_url = 1, /* base64url format (no padding) */ - base64_default_no_padding = - base64_default | - base64_reverse_padding, /* standard base64 format without padding */ - base64_url_with_padding = - base64_url | base64_reverse_padding, /* base64url with padding */ - base64_default_accept_garbage = - 4, /* standard base64 format accepting garbage characters, the input stops - with the first '=' if any */ - base64_url_accept_garbage = - 5, /* base64url format accepting garbage characters, the input stops with - the first '=' if any */ - base64_default_or_url = - 8, /* standard/base64url hybrid format (only meaningful for decoding!) */ - base64_default_or_url_accept_garbage = - 12, /* standard/base64url hybrid format accepting garbage characters - (only meaningful for decoding!), the input stops with the first '=' - if any */ +template +concept indexes_into_byte_like = requires(InputPtr p) { + { std::decay_t{} } -> simdutf::detail::byte_like; +}; +template +concept indexes_into_utf16 = requires(InputPtr p) { + { std::decay_t{} } -> std::same_as; +}; +template +concept indexes_into_utf32 = requires(InputPtr p) { + { std::decay_t{} } -> std::same_as; }; - #if SIMDUTF_CPLUSPLUS17 -inline std::string_view to_string(base64_options options) { - switch (options) { - case base64_default: - return "base64_default"; - case base64_url: - return "base64_url"; - case base64_reverse_padding: - return "base64_reverse_padding"; - case base64_url_with_padding: - return "base64_url_with_padding"; - case base64_default_accept_garbage: - return "base64_default_accept_garbage"; - case base64_url_accept_garbage: - return "base64_url_accept_garbage"; - case base64_default_or_url: - return "base64_default_or_url"; - case base64_default_or_url_accept_garbage: - return "base64_default_or_url_accept_garbage"; - } - return ""; -} - #endif // SIMDUTF_CPLUSPLUS17 +template +concept index_assignable_from_char = requires(InputPtr p, char s) { + { p[0] = s }; +}; -// last_chunk_handling_options are used to specify the handling of the last -// chunk in base64 decoding. -// https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64 -enum last_chunk_handling_options : uint64_t { - loose = 0, /* standard base64 format, decode partial final chunk */ - strict = 1, /* error when the last chunk is partial, 2 or 3 chars, and - unpadded, or non-zero bit padding */ - stop_before_partial = - 2, /* if the last chunk is partial, ignore it (no error) */ - only_full_chunks = - 3 /* only decode full blocks (4 base64 characters, no padding) */ +/** + * a pointer like object that results in a uint32_t when indexed. + * valid examples: uint32_t* + */ +template +concept indexes_into_uint32 = requires(InputPtr p) { + { std::decay_t{} } -> std::same_as; }; +} // namespace detail +} // namespace simdutf +#endif // SIMDUTF_SPAN -inline bool is_partial(last_chunk_handling_options options) { - return (options == stop_before_partial) || (options == only_full_chunks); +// these includes are needed for constexpr support. they are +// not part of the public api. +/* begin file include/simdutf/scalar/swap_bytes.h */ +#ifndef SIMDUTF_SWAP_BYTES_H +#define SIMDUTF_SWAP_BYTES_H + +namespace simdutf { +namespace scalar { + +constexpr inline simdutf_warn_unused uint16_t +u16_swap_bytes(const uint16_t word) { + return uint16_t((word >> 8) | (word << 8)); } - #if SIMDUTF_CPLUSPLUS17 -inline std::string_view to_string(last_chunk_handling_options options) { - switch (options) { - case loose: - return "loose"; - case strict: - return "strict"; - case stop_before_partial: - return "stop_before_partial"; - case only_full_chunks: - return "only_full_chunks"; - } - return ""; +constexpr inline simdutf_warn_unused uint32_t +u32_swap_bytes(const uint32_t word) { + return ((word >> 24) & 0xff) | // move byte 3 to byte 0 + ((word << 8) & 0xff0000) | // move byte 1 to byte 2 + ((word >> 8) & 0xff00) | // move byte 2 to byte 1 + ((word << 24) & 0xff000000); // byte 0 to byte 3 } - #endif -/** - * Provide the maximal binary length in bytes given the base64 input. - * In general, if the input contains ASCII spaces, the result will be less than - * the maximum length. - * - * @param input the base64 input to process - * @param length the length of the base64 input in bytes - * @return maximum number of binary bytes - */ -simdutf_warn_unused size_t -maximal_binary_length_from_base64(const char *input, size_t length) noexcept; - #if SIMDUTF_SPAN -simdutf_really_inline simdutf_warn_unused size_t -maximal_binary_length_from_base64( - const detail::input_span_of_byte_like auto &input) noexcept { - return maximal_binary_length_from_base64( - reinterpret_cast(input.data()), input.size()); +namespace utf32 { +template constexpr uint32_t swap_if_needed(uint32_t c) { + return !match_system(big_endian) ? scalar::u32_swap_bytes(c) : c; } - #endif // SIMDUTF_SPAN +} // namespace utf32 -/** - * Provide the maximal binary length in bytes given the base64 input. - * In general, if the input contains ASCII spaces, the result will be less than - * the maximum length. - * - * @param input the base64 input to process, in ASCII stored as 16-bit - * units - * @param length the length of the base64 input in 16-bit units - * @return maximal number of binary bytes - */ -simdutf_warn_unused size_t maximal_binary_length_from_base64( - const char16_t *input, size_t length) noexcept; - #if SIMDUTF_SPAN -simdutf_really_inline simdutf_warn_unused size_t -maximal_binary_length_from_base64(std::span input) noexcept { - return maximal_binary_length_from_base64(input.data(), input.size()); +namespace utf16 { +template constexpr uint16_t swap_if_needed(uint16_t c) { + return !match_system(big_endian) ? scalar::u16_swap_bytes(c) : c; } - #endif // SIMDUTF_SPAN +} // namespace utf16 -/** - * Convert a base64 input to a binary output. - * - * This function follows the WHATWG forgiving-base64 format, which means that it - * will ignore any ASCII spaces in the input. You may provide a padded input - * (with one or two equal signs at the end) or an unpadded input (without any - * equal signs at the end). - * - * See https://infra.spec.whatwg.org/#forgiving-base64-decode +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/swap_bytes.h */ +/* begin file include/simdutf/scalar/ascii.h */ +#ifndef SIMDUTF_ASCII_H +#define SIMDUTF_ASCII_H + +namespace simdutf { +namespace scalar { +namespace { +namespace ascii { + +template +#if SIMDUTF_CPLUSPLUS20 + requires simdutf::detail::indexes_into_byte_like +#endif +simdutf_warn_unused simdutf_constexpr23 bool validate(InputPtr data, + size_t len) noexcept { + uint64_t pos = 0; + +#if SIMDUTF_CPLUSPLUS23 + // avoid memcpy during constant evaluation + if !consteval +#endif + // process in blocks of 16 bytes when possible + { + for (; pos + 16 <= len; pos += 16) { + uint64_t v1; + std::memcpy(&v1, data + pos, sizeof(uint64_t)); + uint64_t v2; + std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); + uint64_t v{v1 | v2}; + if ((v & 0x8080808080808080) != 0) { + return false; + } + } + } + + // process the tail byte-by-byte + for (; pos < len; pos++) { + if (static_cast(data[pos]) >= 0b10000000) { + return false; + } + } + return true; +} +template +#if SIMDUTF_CPLUSPLUS20 + requires simdutf::detail::indexes_into_byte_like +#endif +simdutf_warn_unused simdutf_constexpr23 result +validate_with_errors(InputPtr data, size_t len) noexcept { + size_t pos = 0; +#if SIMDUTF_CPLUSPLUS23 + // avoid memcpy during constant evaluation + if !consteval +#endif + { + // process in blocks of 16 bytes when possible + for (; pos + 16 <= len; pos += 16) { + uint64_t v1; + std::memcpy(&v1, data + pos, sizeof(uint64_t)); + uint64_t v2; + std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); + uint64_t v{v1 | v2}; + if ((v & 0x8080808080808080) != 0) { + for (; pos < len; pos++) { + if (static_cast(data[pos]) >= 0b10000000) { + return result(error_code::TOO_LARGE, pos); + } + } + } + } + } + + // process the tail byte-by-byte + for (; pos < len; pos++) { + if (static_cast(data[pos]) >= 0b10000000) { + return result(error_code::TOO_LARGE, pos); + } + } + return result(error_code::SUCCESS, pos); +} + +} // namespace ascii +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/ascii.h */ +/* begin file include/simdutf/scalar/atomic_util.h */ +#ifndef SIMDUTF_ATOMIC_UTIL_H +#define SIMDUTF_ATOMIC_UTIL_H +#if SIMDUTF_ATOMIC_REF + #include +namespace simdutf { +namespace scalar { + +// This function is a memcpy that uses atomic operations to read from the +// source. +inline void memcpy_atomic_read(char *dst, const char *src, size_t len) { + static_assert(std::atomic_ref::required_alignment == sizeof(char), + "std::atomic_ref requires the same alignment as char_type"); + // We expect all 64-bit systems to be able to read 64-bit words from an + // aligned memory region atomically. You might be able to do better on + // specific systems, e.g., x64 systems can read 128-bit words atomically. + constexpr size_t alignment = sizeof(uint64_t); + + // Lambda for atomic byte-by-byte copy + auto bbb_memcpy_atomic_read = [](char *bytedst, const char *bytesrc, + size_t bytelen) noexcept { + char *mutable_src = const_cast(bytesrc); + for (size_t j = 0; j < bytelen; ++j) { + bytedst[j] = + std::atomic_ref(mutable_src[j]).load(std::memory_order_relaxed); + } + }; + + // Handle unaligned start + size_t offset = reinterpret_cast(src) % alignment; + if (offset) { + size_t to_align = std::min(len, alignment - offset); + bbb_memcpy_atomic_read(dst, src, to_align); + src += to_align; + dst += to_align; + len -= to_align; + } + + // Process aligned 64-bit chunks + while (len >= alignment) { + auto *src_aligned = reinterpret_cast(const_cast(src)); + const auto dst_value = + std::atomic_ref(*src_aligned).load(std::memory_order_relaxed); + std::memcpy(dst, &dst_value, sizeof(uint64_t)); + src += alignment; + dst += alignment; + len -= alignment; + } + + // Handle remaining bytes + if (len) { + bbb_memcpy_atomic_read(dst, src, len); + } +} + +// This function is a memcpy that uses atomic operations to write to the +// destination. +inline void memcpy_atomic_write(char *dst, const char *src, size_t len) { + static_assert(std::atomic_ref::required_alignment == sizeof(char), + "std::atomic_ref requires the same alignment as char"); + // We expect all 64-bit systems to be able to write 64-bit words to an aligned + // memory region atomically. + // You might be able to do better on specific systems, e.g., x64 systems can + // write 128-bit words atomically. + constexpr size_t alignment = sizeof(uint64_t); + + // Lambda for atomic byte-by-byte write + auto bbb_memcpy_atomic_write = [](char *bytedst, const char *bytesrc, + size_t bytelen) noexcept { + for (size_t j = 0; j < bytelen; ++j) { + std::atomic_ref(bytedst[j]) + .store(bytesrc[j], std::memory_order_relaxed); + } + }; + + // Handle unaligned start + size_t offset = reinterpret_cast(dst) % alignment; + if (offset) { + size_t to_align = std::min(len, alignment - offset); + bbb_memcpy_atomic_write(dst, src, to_align); + dst += to_align; + src += to_align; + len -= to_align; + } + + // Process aligned 64-bit chunks + while (len >= alignment) { + auto *dst_aligned = reinterpret_cast(dst); + uint64_t src_val; + std::memcpy(&src_val, src, sizeof(uint64_t)); // Non-atomic read from src + std::atomic_ref(*dst_aligned) + .store(src_val, std::memory_order_relaxed); + dst += alignment; + src += alignment; + len -= alignment; + } + + // Handle remaining bytes + if (len) { + bbb_memcpy_atomic_write(dst, src, len); + } +} +} // namespace scalar +} // namespace simdutf +#endif // SIMDUTF_ATOMIC_REF +#endif // SIMDUTF_ATOMIC_UTIL_H +/* end file include/simdutf/scalar/atomic_util.h */ +/* begin file include/simdutf/scalar/latin1.h */ +#ifndef SIMDUTF_LATIN1_H +#define SIMDUTF_LATIN1_H + +namespace simdutf { +namespace scalar { +namespace { +namespace latin1 { + +simdutf_really_inline size_t utf8_length_from_latin1(const char *buf, + size_t len) { + const uint8_t *c = reinterpret_cast(buf); + size_t answer = 0; + for (size_t i = 0; i < len; i++) { + if ((c[i] >> 7)) { + answer++; + } + } + return answer + len; +} + +} // namespace latin1 +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/latin1.h */ +/* begin file include/simdutf/scalar/latin1_to_utf16/latin1_to_utf16.h */ +#ifndef SIMDUTF_LATIN1_TO_UTF16_H +#define SIMDUTF_LATIN1_TO_UTF16_H + +namespace simdutf { +namespace scalar { +namespace { +namespace latin1_to_utf16 { + +template +#if SIMDUTF_CPLUSPLUS20 + requires simdutf::detail::indexes_into_byte_like +#endif +simdutf_constexpr23 size_t convert(InputPtr data, size_t len, + char16_t *utf16_output) { + size_t pos = 0; + char16_t *start{utf16_output}; + + while (pos < len) { + uint16_t word = + uint8_t(data[pos]); // extend Latin-1 char to 16-bit Unicode code point + *utf16_output++ = + char16_t(match_system(big_endian) ? word : u16_swap_bytes(word)); + pos++; + } + + return utf16_output - start; +} + +template +inline result convert_with_errors(const char *buf, size_t len, + char16_t *utf16_output) { + const uint8_t *data = reinterpret_cast(buf); + size_t pos = 0; + char16_t *start{utf16_output}; + + while (pos < len) { + uint16_t word = + uint16_t(data[pos]); // extend Latin-1 char to 16-bit Unicode code point + *utf16_output++ = + char16_t(match_system(big_endian) ? word : u16_swap_bytes(word)); + pos++; + } + + return result(error_code::SUCCESS, utf16_output - start); +} + +} // namespace latin1_to_utf16 +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/latin1_to_utf16/latin1_to_utf16.h */ +/* begin file include/simdutf/scalar/latin1_to_utf32/latin1_to_utf32.h */ +#ifndef SIMDUTF_LATIN1_TO_UTF32_H +#define SIMDUTF_LATIN1_TO_UTF32_H + +namespace simdutf { +namespace scalar { +namespace { +namespace latin1_to_utf32 { + +template +#if SIMDUTF_CPLUSPLUS20 + requires simdutf::detail::indexes_into_byte_like +#endif +simdutf_constexpr23 size_t convert(InputPtr data, size_t len, + char32_t *utf32_output) { + char32_t *start{utf32_output}; + for (size_t i = 0; i < len; i++) { + *utf32_output++ = uint8_t(data[i]); + } + return utf32_output - start; +} + +} // namespace latin1_to_utf32 +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/latin1_to_utf32/latin1_to_utf32.h */ +/* begin file include/simdutf/scalar/latin1_to_utf8/latin1_to_utf8.h */ +#ifndef SIMDUTF_LATIN1_TO_UTF8_H +#define SIMDUTF_LATIN1_TO_UTF8_H + +namespace simdutf { +namespace scalar { +namespace { +namespace latin1_to_utf8 { + +template +#if SIMDUTF_CPLUSPLUS20 + requires(simdutf::detail::indexes_into_byte_like && + simdutf::detail::index_assignable_from_char) +#endif +simdutf_constexpr23 size_t convert(InputPtr data, size_t len, + OutputPtr utf8_output) { + // const unsigned char *data = reinterpret_cast(buf); + size_t pos = 0; + size_t utf8_pos = 0; + + while (pos < len) { +#if SIMDUTF_CPLUSPLUS23 + if !consteval +#endif + { + // try to convert the next block of 16 ASCII bytes + if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that + // they are ascii + uint64_t v1; + ::memcpy(&v1, data + pos, sizeof(uint64_t)); + uint64_t v2; + ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); + uint64_t v{v1 | + v2}; // We are only interested in these bits: 1000 1000 1000 + // 1000, so it makes sense to concatenate everything + if ((v & 0x8080808080808080) == + 0) { // if NONE of these are set, e.g. all of them are zero, then + // everything is ASCII + size_t final_pos = pos + 16; + while (pos < final_pos) { + utf8_output[utf8_pos++] = char(data[pos]); + pos++; + } + continue; + } + } // if (pos + 16 <= len) + } // !consteval scope + + unsigned char byte = data[pos]; + if ((byte & 0x80) == 0) { // if ASCII + // will generate one UTF-8 bytes + utf8_output[utf8_pos++] = char(byte); + pos++; + } else { + // will generate two UTF-8 bytes + utf8_output[utf8_pos++] = char((byte >> 6) | 0b11000000); + utf8_output[utf8_pos++] = char((byte & 0b111111) | 0b10000000); + pos++; + } + } // while + return utf8_pos; +} + +simdutf_really_inline size_t convert(const char *buf, size_t len, + char *utf8_output) { + return convert(reinterpret_cast(buf), len, + utf8_output); +} + +inline size_t convert_safe(const char *buf, size_t len, char *utf8_output, + size_t utf8_len) { + const unsigned char *data = reinterpret_cast(buf); + size_t pos = 0; + size_t skip_pos = 0; + size_t utf8_pos = 0; + while (pos < len && utf8_pos < utf8_len) { + // try to convert the next block of 16 ASCII bytes + if (pos >= skip_pos && pos + 16 <= len && + utf8_pos + 16 <= utf8_len) { // if it is safe to read 16 more bytes, + // check that they are ascii + uint64_t v1; + ::memcpy(&v1, data + pos, sizeof(uint64_t)); + uint64_t v2; + ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); + uint64_t v{v1 | + v2}; // We are only interested in these bits: 1000 1000 1000 + // 1000, so it makes sense to concatenate everything + if ((v & 0x8080808080808080) == + 0) { // if NONE of these are set, e.g. all of them are zero, then + // everything is ASCII + ::memcpy(utf8_output + utf8_pos, buf + pos, 16); + utf8_pos += 16; + pos += 16; + } else { + // At least one of the next 16 bytes are not ASCII, we will process them + // one by one + skip_pos = pos + 16; + } + } else { + const auto byte = data[pos]; + if ((byte & 0x80) == 0) { // if ASCII + // will generate one UTF-8 bytes + utf8_output[utf8_pos++] = char(byte); + pos++; + } else if (utf8_pos + 2 <= utf8_len) { + // will generate two UTF-8 bytes + utf8_output[utf8_pos++] = char((byte >> 6) | 0b11000000); + utf8_output[utf8_pos++] = char((byte & 0b111111) | 0b10000000); + pos++; + } else { + break; + } + } + } + return utf8_pos; +} + +template +#if SIMDUTF_CPLUSPLUS20 + requires(simdutf::detail::indexes_into_byte_like && + simdutf::detail::index_assignable_from_char) +#endif +simdutf_constexpr23 size_t convert_safe_constexpr(InputPtr data, size_t len, + OutputPtr utf8_output, + size_t utf8_len) { + size_t pos = 0; + size_t utf8_pos = 0; + while (pos < len && utf8_pos < utf8_len) { + const unsigned char byte = data[pos]; + if ((byte & 0x80) == 0) { // if ASCII + // will generate one UTF-8 bytes + utf8_output[utf8_pos++] = char(byte); + pos++; + } else if (utf8_pos + 2 <= utf8_len) { + // will generate two UTF-8 bytes + utf8_output[utf8_pos++] = char((byte >> 6) | 0b11000000); + utf8_output[utf8_pos++] = char((byte & 0b111111) | 0b10000000); + pos++; + } else { + break; + } + } + return utf8_pos; +} + +template +#if SIMDUTF_CPLUSPLUS20 + requires simdutf::detail::indexes_into_byte_like +#endif +simdutf_constexpr23 simdutf_warn_unused size_t +utf8_length_from_latin1(InputPtr input, size_t length) noexcept { + size_t answer = length; + size_t i = 0; + +#if SIMDUTF_CPLUSPLUS23 + if !consteval +#endif + { + auto pop = [](uint64_t v) { + return (size_t)(((v >> 7) & UINT64_C(0x0101010101010101)) * + UINT64_C(0x0101010101010101) >> + 56); + }; + for (; i + 32 <= length; i += 32) { + uint64_t v; + memcpy(&v, input + i, 8); + answer += pop(v); + memcpy(&v, input + i + 8, sizeof(v)); + answer += pop(v); + memcpy(&v, input + i + 16, sizeof(v)); + answer += pop(v); + memcpy(&v, input + i + 24, sizeof(v)); + answer += pop(v); + } + for (; i + 8 <= length; i += 8) { + uint64_t v; + memcpy(&v, input + i, sizeof(v)); + answer += pop(v); + } + } // !consteval scope + for (; i + 1 <= length; i += 1) { + answer += static_cast(input[i]) >> 7; + } + return answer; +} + +} // namespace latin1_to_utf8 +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/latin1_to_utf8/latin1_to_utf8.h */ +/* begin file include/simdutf/scalar/utf16.h */ +#ifndef SIMDUTF_UTF16_H +#define SIMDUTF_UTF16_H + +namespace simdutf { +namespace scalar { +namespace utf16 { + +template +simdutf_warn_unused simdutf_constexpr23 bool +validate_as_ascii(const char16_t *data, size_t len) noexcept { + for (size_t pos = 0; pos < len; pos++) { + char16_t word = scalar::utf16::swap_if_needed(data[pos]); + if (word >= 0x80) { + return false; + } + } + return true; +} + +template +inline simdutf_warn_unused simdutf_constexpr23 bool +validate(const char16_t *data, size_t len) noexcept { + uint64_t pos = 0; + while (pos < len) { + char16_t word = scalar::utf16::swap_if_needed(data[pos]); + if ((word & 0xF800) == 0xD800) { + if (pos + 1 >= len) { + return false; + } + char16_t diff = char16_t(word - 0xD800); + if (diff > 0x3FF) { + return false; + } + char16_t next_word = !match_system(big_endian) + ? u16_swap_bytes(data[pos + 1]) + : data[pos + 1]; + char16_t diff2 = char16_t(next_word - 0xDC00); + if (diff2 > 0x3FF) { + return false; + } + pos += 2; + } else { + pos++; + } + } + return true; +} + +template +inline simdutf_warn_unused simdutf_constexpr23 result +validate_with_errors(const char16_t *data, size_t len) noexcept { + size_t pos = 0; + while (pos < len) { + char16_t word = scalar::utf16::swap_if_needed(data[pos]); + if ((word & 0xF800) == 0xD800) { + if (pos + 1 >= len) { + return result(error_code::SURROGATE, pos); + } + char16_t diff = char16_t(word - 0xD800); + if (diff > 0x3FF) { + return result(error_code::SURROGATE, pos); + } + char16_t next_word = !match_system(big_endian) + ? u16_swap_bytes(data[pos + 1]) + : data[pos + 1]; + char16_t diff2 = uint16_t(next_word - 0xDC00); + if (diff2 > 0x3FF) { + return result(error_code::SURROGATE, pos); + } + pos += 2; + } else { + pos++; + } + } + return result(error_code::SUCCESS, pos); +} + +template +simdutf_constexpr23 size_t count_code_points(const char16_t *p, size_t len) { + // We are not BOM aware. + size_t counter{0}; + for (size_t i = 0; i < len; i++) { + char16_t word = scalar::utf16::swap_if_needed(p[i]); + counter += ((word & 0xFC00) != 0xDC00); + } + return counter; +} + +template +simdutf_constexpr23 size_t utf8_length_from_utf16(const char16_t *p, + size_t len) { + // We are not BOM aware. + size_t counter{0}; + for (size_t i = 0; i < len; i++) { + char16_t word = scalar::utf16::swap_if_needed(p[i]); + counter++; // ASCII + counter += static_cast( + word > + 0x7F); // non-ASCII is at least 2 bytes, surrogates are 2*2 == 4 bytes + counter += static_cast((word > 0x7FF && word <= 0xD7FF) || + (word >= 0xE000)); // three-byte + } + return counter; +} + +template +simdutf_constexpr23 size_t utf32_length_from_utf16(const char16_t *p, + size_t len) { + // We are not BOM aware. + size_t counter{0}; + for (size_t i = 0; i < len; i++) { + char16_t word = scalar::utf16::swap_if_needed(p[i]); + counter += ((word & 0xFC00) != 0xDC00); + } + return counter; +} + +simdutf_really_inline simdutf_constexpr23 void +change_endianness_utf16(const char16_t *input, size_t size, char16_t *output) { + for (size_t i = 0; i < size; i++) { + *output++ = char16_t(input[i] >> 8 | input[i] << 8); + } +} + +template +simdutf_warn_unused simdutf_constexpr23 size_t +trim_partial_utf16(const char16_t *input, size_t length) { + if (length == 0) { + return 0; + } + uint16_t last_word = uint16_t(input[length - 1]); + last_word = scalar::utf16::swap_if_needed(last_word); + length -= ((last_word & 0xFC00) == 0xD800); + return length; +} + +template constexpr bool is_high_surrogate(char16_t c) { + c = scalar::utf16::swap_if_needed(c); + return (0xd800 <= c && c <= 0xdbff); +} + +template constexpr bool is_low_surrogate(char16_t c) { + c = scalar::utf16::swap_if_needed(c); + return (0xdc00 <= c && c <= 0xdfff); +} + +simdutf_really_inline constexpr bool high_surrogate(char16_t c) { + return (0xd800 <= c && c <= 0xdbff); +} + +simdutf_really_inline constexpr bool low_surrogate(char16_t c) { + return (0xdc00 <= c && c <= 0xdfff); +} + +template +simdutf_constexpr23 result +utf8_length_from_utf16_with_replacement(const char16_t *p, size_t len) { + bool any_surrogates = false; + // We are not BOM aware. + size_t counter{0}; + for (size_t i = 0; i < len; i++) { + if (is_high_surrogate(p[i])) { + any_surrogates = true; + // surrogate pair + if (i + 1 < len && is_low_surrogate(p[i + 1])) { + counter += 4; + i++; // skip low surrogate + } else { + counter += 3; // unpaired high surrogate replaced by U+FFFD + } + continue; + } else if (is_low_surrogate(p[i])) { + any_surrogates = true; + counter += 3; // unpaired low surrogate replaced by U+FFFD + continue; + } + char16_t word = !match_system(big_endian) ? u16_swap_bytes(p[i]) : p[i]; + counter++; // at least 1 byte + counter += + static_cast(word > 0x7F); // non-ASCII is at least 2 bytes + counter += static_cast(word > 0x7FF); // three-byte + } + return {any_surrogates ? error_code::SURROGATE : error_code::SUCCESS, + counter}; +} + +// variable templates are a C++14 extension +template constexpr char16_t replacement() { + return !match_system(big_endian) ? scalar::u16_swap_bytes(0xfffd) : 0xfffd; +} + +template +simdutf_constexpr23 void to_well_formed_utf16(const char16_t *input, size_t len, + char16_t *output) { + const char16_t replacement = utf16::replacement(); + bool high_surrogate_prev = false, high_surrogate, low_surrogate; + size_t i = 0; + for (; i < len; i++) { + char16_t c = input[i]; + high_surrogate = is_high_surrogate(c); + low_surrogate = is_low_surrogate(c); + if (high_surrogate_prev && !low_surrogate) { + output[i - 1] = replacement; + } + + if (!high_surrogate_prev && low_surrogate) { + output[i] = replacement; + } else { + output[i] = input[i]; + } + high_surrogate_prev = high_surrogate; + } + + /* string may not end with high surrogate */ + if (high_surrogate_prev) { + output[i - 1] = replacement; + } +} + +} // namespace utf16 +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/utf16.h */ +/* begin file include/simdutf/scalar/utf16_to_latin1/utf16_to_latin1.h */ +#ifndef SIMDUTF_UTF16_TO_LATIN1_H +#define SIMDUTF_UTF16_TO_LATIN1_H + +#include // for std::memcpy + +namespace simdutf { +namespace scalar { +namespace { +namespace utf16_to_latin1 { + +template +#if SIMDUTF_CPLUSPLUS20 + requires(simdutf::detail::indexes_into_utf16 && + simdutf::detail::index_assignable_from_char) +#endif +simdutf_constexpr23 size_t convert(InputPtr data, size_t len, + OutputPtr latin_output) { + if (len == 0) { + return 0; + } + size_t pos = 0; + const auto latin_output_start = latin_output; + uint16_t word = 0; + uint16_t too_large = 0; + + while (pos < len) { + word = !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos]; + too_large |= word; + *latin_output++ = char(word & 0xFF); + pos++; + } + if ((too_large & 0xFF00) != 0) { + return 0; + } + + return latin_output - latin_output_start; +} + +template +#if SIMDUTF_CPLUSPLUS20 + requires(simdutf::detail::indexes_into_utf16 && + simdutf::detail::index_assignable_from_char) +#endif +simdutf_constexpr23 result convert_with_errors(InputPtr data, size_t len, + OutputPtr latin_output) { + if (len == 0) { + return result(error_code::SUCCESS, 0); + } + size_t pos = 0; + auto start = latin_output; + uint16_t word; + + while (pos < len) { +#if SIMDUTF_CPLUSPLUS23 + if !consteval +#endif + { + if (pos + 16 <= len) { // if it is safe to read 32 more bytes, check that + // they are Latin1 + uint64_t v1, v2, v3, v4; + ::memcpy(&v1, data + pos, sizeof(uint64_t)); + ::memcpy(&v2, data + pos + 4, sizeof(uint64_t)); + ::memcpy(&v3, data + pos + 8, sizeof(uint64_t)); + ::memcpy(&v4, data + pos + 12, sizeof(uint64_t)); + + if constexpr (!match_system(big_endian)) { + v1 = (v1 >> 8) | (v1 << (64 - 8)); + } + if constexpr (!match_system(big_endian)) { + v2 = (v2 >> 8) | (v2 << (64 - 8)); + } + if constexpr (!match_system(big_endian)) { + v3 = (v3 >> 8) | (v3 << (64 - 8)); + } + if constexpr (!match_system(big_endian)) { + v4 = (v4 >> 8) | (v4 << (64 - 8)); + } + + if (((v1 | v2 | v3 | v4) & 0xFF00FF00FF00FF00) == 0) { + size_t final_pos = pos + 16; + while (pos < final_pos) { + *latin_output++ = !match_system(big_endian) + ? char(u16_swap_bytes(data[pos])) + : char(data[pos]); + pos++; + } + continue; + } + } + } + + word = !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos]; + if ((word & 0xFF00) == 0) { + *latin_output++ = char(word & 0xFF); + pos++; + } else { + return result(error_code::TOO_LARGE, pos); + } + } + return result(error_code::SUCCESS, latin_output - start); +} + +} // namespace utf16_to_latin1 +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/utf16_to_latin1/utf16_to_latin1.h */ +/* begin file include/simdutf/scalar/utf16_to_latin1/valid_utf16_to_latin1.h */ +#ifndef SIMDUTF_VALID_UTF16_TO_LATIN1_H +#define SIMDUTF_VALID_UTF16_TO_LATIN1_H + +namespace simdutf { +namespace scalar { +namespace { +namespace utf16_to_latin1 { + +template +simdutf_constexpr23 inline size_t +convert_valid_impl(InputIterator data, size_t len, + OutputIterator latin_output) { + static_assert( + std::is_same::type, uint16_t>::value, + "must decay to uint16_t"); + size_t pos = 0; + const auto start = latin_output; + uint16_t word = 0; + + while (pos < len) { + word = !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos]; + *latin_output++ = char(word); + pos++; + } + + return latin_output - start; +} + +template +simdutf_really_inline size_t convert_valid(const char16_t *buf, size_t len, + char *latin_output) { + return convert_valid_impl(reinterpret_cast(buf), + len, latin_output); +} +} // namespace utf16_to_latin1 +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/utf16_to_latin1/valid_utf16_to_latin1.h */ +/* begin file include/simdutf/scalar/utf16_to_utf32/utf16_to_utf32.h */ +#ifndef SIMDUTF_UTF16_TO_UTF32_H +#define SIMDUTF_UTF16_TO_UTF32_H + +namespace simdutf { +namespace scalar { +namespace { +namespace utf16_to_utf32 { + +template +simdutf_constexpr23 size_t convert(const char16_t *data, size_t len, + char32_t *utf32_output) { + size_t pos = 0; + char32_t *start{utf32_output}; + while (pos < len) { + uint16_t word = + !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos]; + if ((word & 0xF800) != 0xD800) { + // No surrogate pair, extend 16-bit word to 32-bit word + *utf32_output++ = char32_t(word); + pos++; + } else { + // must be a surrogate pair + uint16_t diff = uint16_t(word - 0xD800); + if (diff > 0x3FF) { + return 0; + } + if (pos + 1 >= len) { + return 0; + } // minimal bound checking + uint16_t next_word = !match_system(big_endian) + ? u16_swap_bytes(data[pos + 1]) + : data[pos + 1]; + uint16_t diff2 = uint16_t(next_word - 0xDC00); + if (diff2 > 0x3FF) { + return 0; + } + uint32_t value = (diff << 10) + diff2 + 0x10000; + *utf32_output++ = char32_t(value); + pos += 2; + } + } + return utf32_output - start; +} + +template +simdutf_constexpr23 result convert_with_errors(const char16_t *data, size_t len, + char32_t *utf32_output) { + size_t pos = 0; + char32_t *start{utf32_output}; + while (pos < len) { + uint16_t word = + !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos]; + if ((word & 0xF800) != 0xD800) { + // No surrogate pair, extend 16-bit word to 32-bit word + *utf32_output++ = char32_t(word); + pos++; + } else { + // must be a surrogate pair + uint16_t diff = uint16_t(word - 0xD800); + if (diff > 0x3FF) { + return result(error_code::SURROGATE, pos); + } + if (pos + 1 >= len) { + return result(error_code::SURROGATE, pos); + } // minimal bound checking + uint16_t next_word = !match_system(big_endian) + ? u16_swap_bytes(data[pos + 1]) + : data[pos + 1]; + uint16_t diff2 = uint16_t(next_word - 0xDC00); + if (diff2 > 0x3FF) { + return result(error_code::SURROGATE, pos); + } + uint32_t value = (diff << 10) + diff2 + 0x10000; + *utf32_output++ = char32_t(value); + pos += 2; + } + } + return result(error_code::SUCCESS, utf32_output - start); +} + +} // namespace utf16_to_utf32 +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/utf16_to_utf32/utf16_to_utf32.h */ +/* begin file include/simdutf/scalar/utf16_to_utf32/valid_utf16_to_utf32.h */ +#ifndef SIMDUTF_VALID_UTF16_TO_UTF32_H +#define SIMDUTF_VALID_UTF16_TO_UTF32_H + +namespace simdutf { +namespace scalar { +namespace { +namespace utf16_to_utf32 { + +template +simdutf_constexpr23 size_t convert_valid(const char16_t *data, size_t len, + char32_t *utf32_output) { + size_t pos = 0; + char32_t *start{utf32_output}; + while (pos < len) { + uint16_t word = + !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos]; + if ((word & 0xF800) != 0xD800) { + // No surrogate pair, extend 16-bit word to 32-bit word + *utf32_output++ = char32_t(word); + pos++; + } else { + // must be a surrogate pair + uint16_t diff = uint16_t(word - 0xD800); + if (pos + 1 >= len) { + return 0; + } // minimal bound checking + uint16_t next_word = !match_system(big_endian) + ? u16_swap_bytes(data[pos + 1]) + : data[pos + 1]; + uint16_t diff2 = uint16_t(next_word - 0xDC00); + uint32_t value = (diff << 10) + diff2 + 0x10000; + *utf32_output++ = char32_t(value); + pos += 2; + } + } + return utf32_output - start; +} + +} // namespace utf16_to_utf32 +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/utf16_to_utf32/valid_utf16_to_utf32.h */ +/* begin file include/simdutf/scalar/utf16_to_utf8/utf16_to_utf8.h */ +#ifndef SIMDUTF_UTF16_TO_UTF8_H +#define SIMDUTF_UTF16_TO_UTF8_H + +namespace simdutf { +namespace scalar { +namespace { +namespace utf16_to_utf8 { + +template +#if SIMDUTF_CPLUSPLUS20 + requires simdutf::detail::indexes_into_utf16 +// FIXME constrain output as well +#endif +simdutf_constexpr23 size_t convert(InputPtr data, size_t len, + OutputPtr utf8_output) { + size_t pos = 0; + const auto start = utf8_output; + while (pos < len) { +#if SIMDUTF_CPLUSPLUS23 + if !consteval +#endif + { + // try to convert the next block of 8 bytes + if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that + // they are ascii + uint64_t v; + ::memcpy(&v, data + pos, sizeof(uint64_t)); + if constexpr (!match_system(big_endian)) { + v = (v >> 8) | (v << (64 - 8)); + } + if ((v & 0xFF80FF80FF80FF80) == 0) { + size_t final_pos = pos + 4; + while (pos < final_pos) { + *utf8_output++ = !match_system(big_endian) + ? char(u16_swap_bytes(data[pos])) + : char(data[pos]); + pos++; + } + continue; + } + } + } + uint16_t word = + !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos]; + if ((word & 0xFF80) == 0) { + // will generate one UTF-8 bytes + *utf8_output++ = char(word); + pos++; + } else if ((word & 0xF800) == 0) { + // will generate two UTF-8 bytes + // we have 0b110XXXXX 0b10XXXXXX + *utf8_output++ = char((word >> 6) | 0b11000000); + *utf8_output++ = char((word & 0b111111) | 0b10000000); + pos++; + } else if ((word & 0xF800) != 0xD800) { + // will generate three UTF-8 bytes + // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX + *utf8_output++ = char((word >> 12) | 0b11100000); + *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000); + *utf8_output++ = char((word & 0b111111) | 0b10000000); + pos++; + } else { + // must be a surrogate pair + if (pos + 1 >= len) { + return 0; + } + uint16_t diff = uint16_t(word - 0xD800); + if (diff > 0x3FF) { + return 0; + } + uint16_t next_word = !match_system(big_endian) + ? u16_swap_bytes(data[pos + 1]) + : data[pos + 1]; + uint16_t diff2 = uint16_t(next_word - 0xDC00); + if (diff2 > 0x3FF) { + return 0; + } + uint32_t value = (diff << 10) + diff2 + 0x10000; + // will generate four UTF-8 bytes + // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX + *utf8_output++ = char((value >> 18) | 0b11110000); + *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000); + *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000); + *utf8_output++ = char((value & 0b111111) | 0b10000000); + pos += 2; + } + } + return utf8_output - start; +} + +template +#if SIMDUTF_CPLUSPLUS20 + requires(simdutf::detail::indexes_into_utf16 && + simdutf::detail::index_assignable_from_char) +#endif +simdutf_constexpr23 full_result convert_with_errors(InputPtr data, size_t len, + OutputPtr utf8_output, + size_t utf8_len = 0) { + if (check_output && utf8_len == 0) { + return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, 0, 0); + } + + size_t pos = 0; + auto start = utf8_output; + auto end = utf8_output + utf8_len; + + while (pos < len) { +#if SIMDUTF_CPLUSPLUS23 + if !consteval +#endif + { + // try to convert the next block of 8 bytes + if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that + // they are ascii + uint64_t v; + ::memcpy(&v, data + pos, sizeof(uint64_t)); + if constexpr (!match_system(big_endian)) + v = (v >> 8) | (v << (64 - 8)); + if ((v & 0xFF80FF80FF80FF80) == 0) { + size_t final_pos = pos + 4; + while (pos < final_pos) { + if (check_output && size_t(end - utf8_output) < 1) { + return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos, + utf8_output - start); + } + *utf8_output++ = !match_system(big_endian) + ? char(u16_swap_bytes(data[pos])) + : char(data[pos]); + pos++; + } + continue; + } + } + } + + uint16_t word = + !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos]; + if ((word & 0xFF80) == 0) { + // will generate one UTF-8 bytes + if (check_output && size_t(end - utf8_output) < 1) { + return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos, + utf8_output - start); + } + *utf8_output++ = char(word); + pos++; + } else if ((word & 0xF800) == 0) { + // will generate two UTF-8 bytes + // we have 0b110XXXXX 0b10XXXXXX + if (check_output && size_t(end - utf8_output) < 2) { + return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos, + utf8_output - start); + } + *utf8_output++ = char((word >> 6) | 0b11000000); + *utf8_output++ = char((word & 0b111111) | 0b10000000); + pos++; + + } else if ((word & 0xF800) != 0xD800) { + // will generate three UTF-8 bytes + // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX + if (check_output && size_t(end - utf8_output) < 3) { + return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos, + utf8_output - start); + } + *utf8_output++ = char((word >> 12) | 0b11100000); + *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000); + *utf8_output++ = char((word & 0b111111) | 0b10000000); + pos++; + } else { + + if (check_output && size_t(end - utf8_output) < 4) { + return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos, + utf8_output - start); + } + // must be a surrogate pair + if (pos + 1 >= len) { + return full_result(error_code::SURROGATE, pos, utf8_output - start); + } + uint16_t diff = uint16_t(word - 0xD800); + if (diff > 0x3FF) { + return full_result(error_code::SURROGATE, pos, utf8_output - start); + } + uint16_t next_word = !match_system(big_endian) + ? u16_swap_bytes(data[pos + 1]) + : data[pos + 1]; + uint16_t diff2 = uint16_t(next_word - 0xDC00); + if (diff2 > 0x3FF) { + return full_result(error_code::SURROGATE, pos, utf8_output - start); + } + uint32_t value = (diff << 10) + diff2 + 0x10000; + // will generate four UTF-8 bytes + // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX + *utf8_output++ = char((value >> 18) | 0b11110000); + *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000); + *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000); + *utf8_output++ = char((value & 0b111111) | 0b10000000); + pos += 2; + } + } + return full_result(error_code::SUCCESS, pos, utf8_output - start); +} + +template +inline result simple_convert_with_errors(const char16_t *buf, size_t len, + char *utf8_output) { + return convert_with_errors(buf, len, utf8_output, 0); +} + +template +simdutf_constexpr23 size_t convert_with_replacement(const char16_t *data, + size_t len, + char *utf8_output) { + size_t pos = 0; + char *start = utf8_output; + while (pos < len) { +#if SIMDUTF_CPLUSPLUS23 + if !consteval +#endif + { + // try to convert the next block of 8 bytes + if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that + // they are ascii + uint64_t v; + ::memcpy(&v, data + pos, sizeof(uint64_t)); + if constexpr (!match_system(big_endian)) { + v = (v >> 8) | (v << (64 - 8)); + } + if ((v & 0xFF80FF80FF80FF80) == 0) { + size_t final_pos = pos + 4; + while (pos < final_pos) { + *utf8_output++ = !match_system(big_endian) + ? char(u16_swap_bytes(data[pos])) + : char(data[pos]); + pos++; + } + continue; + } + } + } + uint16_t word = + !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos]; + if ((word & 0xFF80) == 0) { + // will generate one UTF-8 bytes + *utf8_output++ = char(word); + pos++; + } else if ((word & 0xF800) == 0) { + // will generate two UTF-8 bytes + // we have 0b110XXXXX 0b10XXXXXX + *utf8_output++ = char((word >> 6) | 0b11000000); + *utf8_output++ = char((word & 0b111111) | 0b10000000); + pos++; + } else if ((word & 0xF800) != 0xD800) { + // will generate three UTF-8 bytes + // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX + *utf8_output++ = char((word >> 12) | 0b11100000); + *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000); + *utf8_output++ = char((word & 0b111111) | 0b10000000); + pos++; + } else { + // surrogate range + uint16_t diff = uint16_t(word - 0xD800); + if (diff <= 0x3FF && pos + 1 < len) { + // high surrogate, check for valid pair + uint16_t next_word = !match_system(big_endian) + ? u16_swap_bytes(data[pos + 1]) + : data[pos + 1]; + uint16_t diff2 = uint16_t(next_word - 0xDC00); + if (diff2 <= 0x3FF) { + // valid surrogate pair + uint32_t value = (diff << 10) + diff2 + 0x10000; + // will generate four UTF-8 bytes + *utf8_output++ = char((value >> 18) | 0b11110000); + *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000); + *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000); + *utf8_output++ = char((value & 0b111111) | 0b10000000); + pos += 2; + continue; + } + } + // unpaired surrogate: replace with U+FFFD (0xEF 0xBF 0xBD) + *utf8_output++ = char(0xef); + *utf8_output++ = char(0xbf); + *utf8_output++ = char(0xbd); + pos++; + } + } + return utf8_output - start; +} + +} // namespace utf16_to_utf8 +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/utf16_to_utf8/utf16_to_utf8.h */ +/* begin file include/simdutf/scalar/utf16_to_utf8/valid_utf16_to_utf8.h */ +#ifndef SIMDUTF_VALID_UTF16_TO_UTF8_H +#define SIMDUTF_VALID_UTF16_TO_UTF8_H + +namespace simdutf { +namespace scalar { +namespace { +namespace utf16_to_utf8 { + +template +#if SIMDUTF_CPLUSPLUS20 + requires(simdutf::detail::indexes_into_utf16 && + simdutf::detail::index_assignable_from_char) +#endif +simdutf_constexpr23 size_t convert_valid(InputPtr data, size_t len, + OutputPtr utf8_output) { + size_t pos = 0; + auto start = utf8_output; + while (pos < len) { +#if SIMDUTF_CPLUSPLUS23 + if !consteval +#endif + { + // try to convert the next block of 4 ASCII characters + if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that + // they are ascii + uint64_t v; + ::memcpy(&v, data + pos, sizeof(uint64_t)); + if constexpr (!match_system(big_endian)) { + v = (v >> 8) | (v << (64 - 8)); + } + if ((v & 0xFF80FF80FF80FF80) == 0) { + size_t final_pos = pos + 4; + while (pos < final_pos) { + *utf8_output++ = !match_system(big_endian) + ? char(u16_swap_bytes(data[pos])) + : char(data[pos]); + pos++; + } + continue; + } + } + } + + uint16_t word = + !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos]; + if ((word & 0xFF80) == 0) { + // will generate one UTF-8 bytes + *utf8_output++ = char(word); + pos++; + } else if ((word & 0xF800) == 0) { + // will generate two UTF-8 bytes + // we have 0b110XXXXX 0b10XXXXXX + *utf8_output++ = char((word >> 6) | 0b11000000); + *utf8_output++ = char((word & 0b111111) | 0b10000000); + pos++; + } else if ((word & 0xF800) != 0xD800) { + // will generate three UTF-8 bytes + // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX + *utf8_output++ = char((word >> 12) | 0b11100000); + *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000); + *utf8_output++ = char((word & 0b111111) | 0b10000000); + pos++; + } else { + // must be a surrogate pair + uint16_t diff = uint16_t(word - 0xD800); + if (pos + 1 >= len) { + return 0; + } // minimal bound checking + uint16_t next_word = !match_system(big_endian) + ? u16_swap_bytes(data[pos + 1]) + : data[pos + 1]; + uint16_t diff2 = uint16_t(next_word - 0xDC00); + uint32_t value = (diff << 10) + diff2 + 0x10000; + // will generate four UTF-8 bytes + // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX + *utf8_output++ = char((value >> 18) | 0b11110000); + *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000); + *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000); + *utf8_output++ = char((value & 0b111111) | 0b10000000); + pos += 2; + } + } + return utf8_output - start; +} + +} // namespace utf16_to_utf8 +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/utf16_to_utf8/valid_utf16_to_utf8.h */ +/* begin file include/simdutf/scalar/utf32.h */ +#ifndef SIMDUTF_UTF32_H +#define SIMDUTF_UTF32_H + +namespace simdutf { +namespace scalar { +namespace utf32 { + +template +#if SIMDUTF_CPLUSPLUS20 + requires simdutf::detail::indexes_into_uint32 +#endif +simdutf_warn_unused simdutf_constexpr23 bool validate(InputPtr data, + size_t len) noexcept { + uint64_t pos = 0; + for (; pos < len; pos++) { + uint32_t word = data[pos]; + if (word > 0x10FFFF || (word >= 0xD800 && word <= 0xDFFF)) { + return false; + } + } + return true; +} + +simdutf_warn_unused simdutf_really_inline bool validate(const char32_t *buf, + size_t len) noexcept { + return validate(reinterpret_cast(buf), len); +} + +template +#if SIMDUTF_CPLUSPLUS20 + requires simdutf::detail::indexes_into_uint32 +#endif +simdutf_warn_unused simdutf_constexpr23 result +validate_with_errors(InputPtr data, size_t len) noexcept { + size_t pos = 0; + for (; pos < len; pos++) { + uint32_t word = data[pos]; + if (word > 0x10FFFF) { + return result(error_code::TOO_LARGE, pos); + } + if (word >= 0xD800 && word <= 0xDFFF) { + return result(error_code::SURROGATE, pos); + } + } + return result(error_code::SUCCESS, pos); +} + +simdutf_warn_unused simdutf_really_inline result +validate_with_errors(const char32_t *buf, size_t len) noexcept { + return validate_with_errors(reinterpret_cast(buf), len); +} + +inline simdutf_constexpr23 size_t utf8_length_from_utf32(const char32_t *p, + size_t len) { + // We are not BOM aware. + size_t counter{0}; + for (size_t i = 0; i < len; i++) { + // credit: @ttsugriy for the vectorizable approach + counter++; // ASCII + counter += static_cast(p[i] > 0x7F); // two-byte + counter += static_cast(p[i] > 0x7FF); // three-byte + counter += static_cast(p[i] > 0xFFFF); // four-bytes + } + return counter; +} + +inline simdutf_warn_unused simdutf_constexpr23 size_t +utf16_length_from_utf32(const char32_t *p, size_t len) { + // We are not BOM aware. + size_t counter{0}; + for (size_t i = 0; i < len; i++) { + counter++; // non-surrogate word + counter += static_cast(p[i] > 0xFFFF); // surrogate pair + } + return counter; +} + +} // namespace utf32 +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/utf32.h */ +/* begin file include/simdutf/scalar/utf32_to_latin1/utf32_to_latin1.h */ +#ifndef SIMDUTF_UTF32_TO_LATIN1_H +#define SIMDUTF_UTF32_TO_LATIN1_H + +namespace simdutf { +namespace scalar { +namespace { +namespace utf32_to_latin1 { + +inline simdutf_constexpr23 size_t convert(const char32_t *data, size_t len, + char *latin1_output) { + char *start = latin1_output; + uint32_t utf32_char; + size_t pos = 0; + uint32_t too_large = 0; + + while (pos < len) { + utf32_char = (uint32_t)data[pos]; + too_large |= utf32_char; + *latin1_output++ = (char)(utf32_char & 0xFF); + pos++; + } + if ((too_large & 0xFFFFFF00) != 0) { + return 0; + } + return latin1_output - start; +} + +inline simdutf_constexpr23 result convert_with_errors(const char32_t *data, + size_t len, + char *latin1_output) { + char *start{latin1_output}; + size_t pos = 0; + while (pos < len) { +#if SIMDUTF_CPLUSPLUS23 + if !consteval +#endif + { + if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that + // they are Latin1 + uint64_t v; + ::memcpy(&v, data + pos, sizeof(uint64_t)); + if ((v & 0xFFFFFF00FFFFFF00) == 0) { + *latin1_output++ = char(data[pos]); + *latin1_output++ = char(data[pos + 1]); + pos += 2; + continue; + } + } + } + + uint32_t utf32_char = data[pos]; + if ((utf32_char & 0xFFFFFF00) == + 0) { // Check if the character can be represented in Latin-1 + *latin1_output++ = (char)(utf32_char & 0xFF); + pos++; + } else { + return result(error_code::TOO_LARGE, pos); + }; + } + return result(error_code::SUCCESS, latin1_output - start); +} + +} // namespace utf32_to_latin1 +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/utf32_to_latin1/utf32_to_latin1.h */ +/* begin file include/simdutf/scalar/utf32_to_latin1/valid_utf32_to_latin1.h */ +#ifndef SIMDUTF_VALID_UTF32_TO_LATIN1_H +#define SIMDUTF_VALID_UTF32_TO_LATIN1_H + +namespace simdutf { +namespace scalar { +namespace { +namespace utf32_to_latin1 { + +template +simdutf_constexpr23 size_t convert_valid(ReadPtr data, size_t len, + WritePtr latin1_output) { + static_assert( + std::is_same::type, uint32_t>::value, + "dereferencing the data pointer must result in a uint32_t"); + auto start = latin1_output; + uint32_t utf32_char; + size_t pos = 0; + + while (pos < len) { + utf32_char = data[pos]; + +#if SIMDUTF_CPLUSPLUS23 + // avoid using the 8 byte at a time optimization in constant evaluation + // mode. memcpy can't be used and replacing it with bitwise or gave worse + // codegen (when not during constant evaluation). + if !consteval { +#endif + if (pos + 2 <= len) { + // if it is safe to read 8 more bytes, check that they are Latin1 + uint64_t v; + std::memcpy(&v, data + pos, sizeof(uint64_t)); + if ((v & 0xFFFFFF00FFFFFF00) == 0) { + *latin1_output++ = char(data[pos]); + *latin1_output++ = char(data[pos + 1]); + pos += 2; + continue; + } else { + // output can not be represented in latin1 + return 0; + } + } +#if SIMDUTF_CPLUSPLUS23 + } // if ! consteval +#endif + if ((utf32_char & 0xFFFFFF00) == 0) { + *latin1_output++ = char(utf32_char); + } else { + // output can not be represented in latin1 + return 0; + } + pos++; + } + return latin1_output - start; +} + +simdutf_really_inline size_t convert_valid(const char32_t *buf, size_t len, + char *latin1_output) { + return convert_valid(reinterpret_cast(buf), len, + latin1_output); +} + +} // namespace utf32_to_latin1 +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/utf32_to_latin1/valid_utf32_to_latin1.h */ +/* begin file include/simdutf/scalar/utf32_to_utf16/utf32_to_utf16.h */ +#ifndef SIMDUTF_UTF32_TO_UTF16_H +#define SIMDUTF_UTF32_TO_UTF16_H + +namespace simdutf { +namespace scalar { +namespace { +namespace utf32_to_utf16 { + +template +simdutf_constexpr23 size_t convert(const char32_t *data, size_t len, + char16_t *utf16_output) { + size_t pos = 0; + char16_t *start{utf16_output}; + while (pos < len) { + uint32_t word = data[pos]; + if ((word & 0xFFFF0000) == 0) { + if (word >= 0xD800 && word <= 0xDFFF) { + return 0; + } + // will not generate a surrogate pair + *utf16_output++ = !match_system(big_endian) + ? char16_t(u16_swap_bytes(uint16_t(word))) + : char16_t(word); + } else { + // will generate a surrogate pair + if (word > 0x10FFFF) { + return 0; + } + word -= 0x10000; + uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10)); + uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF)); + if constexpr (!match_system(big_endian)) { + high_surrogate = u16_swap_bytes(high_surrogate); + low_surrogate = u16_swap_bytes(low_surrogate); + } + *utf16_output++ = char16_t(high_surrogate); + *utf16_output++ = char16_t(low_surrogate); + } + pos++; + } + return utf16_output - start; +} + +template +simdutf_constexpr23 result convert_with_errors(const char32_t *data, size_t len, + char16_t *utf16_output) { + size_t pos = 0; + char16_t *start{utf16_output}; + while (pos < len) { + uint32_t word = data[pos]; + if ((word & 0xFFFF0000) == 0) { + if (word >= 0xD800 && word <= 0xDFFF) { + return result(error_code::SURROGATE, pos); + } + // will not generate a surrogate pair + *utf16_output++ = !match_system(big_endian) + ? char16_t(u16_swap_bytes(uint16_t(word))) + : char16_t(word); + } else { + // will generate a surrogate pair + if (word > 0x10FFFF) { + return result(error_code::TOO_LARGE, pos); + } + word -= 0x10000; + uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10)); + uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF)); + if constexpr (!match_system(big_endian)) { + high_surrogate = u16_swap_bytes(high_surrogate); + low_surrogate = u16_swap_bytes(low_surrogate); + } + *utf16_output++ = char16_t(high_surrogate); + *utf16_output++ = char16_t(low_surrogate); + } + pos++; + } + return result(error_code::SUCCESS, utf16_output - start); +} + +} // namespace utf32_to_utf16 +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/utf32_to_utf16/utf32_to_utf16.h */ +/* begin file include/simdutf/scalar/utf32_to_utf16/valid_utf32_to_utf16.h */ +#ifndef SIMDUTF_VALID_UTF32_TO_UTF16_H +#define SIMDUTF_VALID_UTF32_TO_UTF16_H + +namespace simdutf { +namespace scalar { +namespace { +namespace utf32_to_utf16 { + +template +simdutf_constexpr23 size_t convert_valid(const char32_t *data, size_t len, + char16_t *utf16_output) { + size_t pos = 0; + char16_t *start{utf16_output}; + while (pos < len) { + uint32_t word = data[pos]; + if ((word & 0xFFFF0000) == 0) { + // will not generate a surrogate pair + *utf16_output++ = !match_system(big_endian) + ? char16_t(u16_swap_bytes(uint16_t(word))) + : char16_t(word); + pos++; + } else { + // will generate a surrogate pair + word -= 0x10000; + uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10)); + uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF)); + if constexpr (!match_system(big_endian)) { + high_surrogate = u16_swap_bytes(high_surrogate); + low_surrogate = u16_swap_bytes(low_surrogate); + } + *utf16_output++ = char16_t(high_surrogate); + *utf16_output++ = char16_t(low_surrogate); + pos++; + } + } + return utf16_output - start; +} + +} // namespace utf32_to_utf16 +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/utf32_to_utf16/valid_utf32_to_utf16.h */ +/* begin file include/simdutf/scalar/utf32_to_utf8/utf32_to_utf8.h */ +#ifndef SIMDUTF_UTF32_TO_UTF8_H +#define SIMDUTF_UTF32_TO_UTF8_H + +namespace simdutf { +namespace scalar { +namespace { +namespace utf32_to_utf8 { + +template +#if SIMDUTF_CPLUSPLUS20 + requires(simdutf::detail::indexes_into_utf32 && + simdutf::detail::index_assignable_from_char) +#endif +simdutf_constexpr23 size_t convert(InputPtr data, size_t len, + OutputPtr utf8_output) { + size_t pos = 0; + auto start = utf8_output; + while (pos < len) { +#if SIMDUTF_CPLUSPLUS23 + if !consteval +#endif + { // try to convert the next block of 2 ASCII characters + if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that + // they are ascii + uint64_t v; + ::memcpy(&v, data + pos, sizeof(uint64_t)); + if ((v & 0xFFFFFF80FFFFFF80) == 0) { + *utf8_output++ = char(data[pos]); + *utf8_output++ = char(data[pos + 1]); + pos += 2; + continue; + } + } + } + + uint32_t word = data[pos]; + if ((word & 0xFFFFFF80) == 0) { + // will generate one UTF-8 bytes + *utf8_output++ = char(word); + pos++; + } else if ((word & 0xFFFFF800) == 0) { + // will generate two UTF-8 bytes + // we have 0b110XXXXX 0b10XXXXXX + *utf8_output++ = char((word >> 6) | 0b11000000); + *utf8_output++ = char((word & 0b111111) | 0b10000000); + pos++; + } else if ((word & 0xFFFF0000) == 0) { + // will generate three UTF-8 bytes + // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX + if (word >= 0xD800 && word <= 0xDFFF) { + return 0; + } + *utf8_output++ = char((word >> 12) | 0b11100000); + *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000); + *utf8_output++ = char((word & 0b111111) | 0b10000000); + pos++; + } else { + // will generate four UTF-8 bytes + // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX + if (word > 0x10FFFF) { + return 0; + } + *utf8_output++ = char((word >> 18) | 0b11110000); + *utf8_output++ = char(((word >> 12) & 0b111111) | 0b10000000); + *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000); + *utf8_output++ = char((word & 0b111111) | 0b10000000); + pos++; + } + } + return utf8_output - start; +} + +template +#if SIMDUTF_CPLUSPLUS20 + requires(simdutf::detail::indexes_into_utf32 && + simdutf::detail::index_assignable_from_char) +#endif +simdutf_constexpr23 result convert_with_errors(InputPtr data, size_t len, + OutputPtr utf8_output) { + size_t pos = 0; + auto start = utf8_output; + while (pos < len) { +#if SIMDUTF_CPLUSPLUS23 + if !consteval +#endif + { // try to convert the next block of 2 ASCII characters + if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that + // they are ascii + uint64_t v; + ::memcpy(&v, data + pos, sizeof(uint64_t)); + if ((v & 0xFFFFFF80FFFFFF80) == 0) { + *utf8_output++ = char(data[pos]); + *utf8_output++ = char(data[pos + 1]); + pos += 2; + continue; + } + } + } + + uint32_t word = data[pos]; + if ((word & 0xFFFFFF80) == 0) { + // will generate one UTF-8 bytes + *utf8_output++ = char(word); + pos++; + } else if ((word & 0xFFFFF800) == 0) { + // will generate two UTF-8 bytes + // we have 0b110XXXXX 0b10XXXXXX + *utf8_output++ = char((word >> 6) | 0b11000000); + *utf8_output++ = char((word & 0b111111) | 0b10000000); + pos++; + } else if ((word & 0xFFFF0000) == 0) { + // will generate three UTF-8 bytes + // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX + if (word >= 0xD800 && word <= 0xDFFF) { + return result(error_code::SURROGATE, pos); + } + *utf8_output++ = char((word >> 12) | 0b11100000); + *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000); + *utf8_output++ = char((word & 0b111111) | 0b10000000); + pos++; + } else { + // will generate four UTF-8 bytes + // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX + if (word > 0x10FFFF) { + return result(error_code::TOO_LARGE, pos); + } + *utf8_output++ = char((word >> 18) | 0b11110000); + *utf8_output++ = char(((word >> 12) & 0b111111) | 0b10000000); + *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000); + *utf8_output++ = char((word & 0b111111) | 0b10000000); + pos++; + } + } + return result(error_code::SUCCESS, utf8_output - start); +} + +} // namespace utf32_to_utf8 +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/utf32_to_utf8/utf32_to_utf8.h */ +/* begin file include/simdutf/scalar/utf32_to_utf8/valid_utf32_to_utf8.h */ +#ifndef SIMDUTF_VALID_UTF32_TO_UTF8_H +#define SIMDUTF_VALID_UTF32_TO_UTF8_H + +namespace simdutf { +namespace scalar { +namespace { +namespace utf32_to_utf8 { + +template +#if SIMDUTF_CPLUSPLUS20 + requires(simdutf::detail::indexes_into_utf32 && + simdutf::detail::index_assignable_from_char) +#endif +simdutf_constexpr23 size_t convert_valid(InputPtr data, size_t len, + OutputPtr utf8_output) { + size_t pos = 0; + auto start = utf8_output; + while (pos < len) { +#if SIMDUTF_CPLUSPLUS23 + if !consteval +#endif + { // try to convert the next block of 2 ASCII characters + if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that + // they are ascii + uint64_t v; + ::memcpy(&v, data + pos, sizeof(uint64_t)); + if ((v & 0xFFFFFF80FFFFFF80) == 0) { + *utf8_output++ = char(data[pos]); + *utf8_output++ = char(data[pos + 1]); + pos += 2; + continue; + } + } + } + + uint32_t word = data[pos]; + if ((word & 0xFFFFFF80) == 0) { + // will generate one UTF-8 bytes + *utf8_output++ = char(word); + pos++; + } else if ((word & 0xFFFFF800) == 0) { + // will generate two UTF-8 bytes + // we have 0b110XXXXX 0b10XXXXXX + *utf8_output++ = char((word >> 6) | 0b11000000); + *utf8_output++ = char((word & 0b111111) | 0b10000000); + pos++; + } else if ((word & 0xFFFF0000) == 0) { + // will generate three UTF-8 bytes + // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX + *utf8_output++ = char((word >> 12) | 0b11100000); + *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000); + *utf8_output++ = char((word & 0b111111) | 0b10000000); + pos++; + } else { + // will generate four UTF-8 bytes + // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX + *utf8_output++ = char((word >> 18) | 0b11110000); + *utf8_output++ = char(((word >> 12) & 0b111111) | 0b10000000); + *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000); + *utf8_output++ = char((word & 0b111111) | 0b10000000); + pos++; + } + } + return utf8_output - start; +} + +} // namespace utf32_to_utf8 +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/utf32_to_utf8/valid_utf32_to_utf8.h */ +/* begin file include/simdutf/scalar/utf8.h */ +#ifndef SIMDUTF_UTF8_H +#define SIMDUTF_UTF8_H + +namespace simdutf { +namespace scalar { +namespace { +namespace utf8 { + +// credit: based on code from Google Fuchsia (Apache Licensed) +template +simdutf_constexpr23 simdutf_warn_unused bool validate(BytePtr data, + size_t len) noexcept { + static_assert( + std::is_same::type, uint8_t>::value, + "dereferencing the data pointer must result in a uint8_t"); + uint64_t pos = 0; + uint32_t code_point = 0; + while (pos < len) { + uint64_t next_pos; +#if SIMDUTF_CPLUSPLUS23 + if !consteval +#endif + { // check if the next 16 bytes are ascii. + next_pos = pos + 16; + if (next_pos <= len) { // if it is safe to read 16 more bytes, check + // that they are ascii + uint64_t v1{}; + std::memcpy(&v1, data + pos, sizeof(uint64_t)); + uint64_t v2{}; + std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); + uint64_t v{v1 | v2}; + if ((v & 0x8080808080808080) == 0) { + pos = next_pos; + continue; + } + } + } + + unsigned char byte = data[pos]; + + while (byte < 0b10000000) { + if (++pos == len) { + return true; + } + byte = data[pos]; + } + + if ((byte & 0b11100000) == 0b11000000) { + next_pos = pos + 2; + if (next_pos > len) { + return false; + } + if ((data[pos + 1] & 0b11000000) != 0b10000000) { + return false; + } + // range check + code_point = (byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111); + if ((code_point < 0x80) || (0x7ff < code_point)) { + return false; + } + } else if ((byte & 0b11110000) == 0b11100000) { + next_pos = pos + 3; + if (next_pos > len) { + return false; + } + if ((data[pos + 1] & 0b11000000) != 0b10000000) { + return false; + } + if ((data[pos + 2] & 0b11000000) != 0b10000000) { + return false; + } + // range check + code_point = (byte & 0b00001111) << 12 | + (data[pos + 1] & 0b00111111) << 6 | + (data[pos + 2] & 0b00111111); + if ((code_point < 0x800) || (0xffff < code_point) || + (0xd7ff < code_point && code_point < 0xe000)) { + return false; + } + } else if ((byte & 0b11111000) == 0b11110000) { // 0b11110000 + next_pos = pos + 4; + if (next_pos > len) { + return false; + } + if ((data[pos + 1] & 0b11000000) != 0b10000000) { + return false; + } + if ((data[pos + 2] & 0b11000000) != 0b10000000) { + return false; + } + if ((data[pos + 3] & 0b11000000) != 0b10000000) { + return false; + } + // range check + code_point = + (byte & 0b00000111) << 18 | (data[pos + 1] & 0b00111111) << 12 | + (data[pos + 2] & 0b00111111) << 6 | (data[pos + 3] & 0b00111111); + if (code_point <= 0xffff || 0x10ffff < code_point) { + return false; + } + } else { + // we may have a continuation + return false; + } + pos = next_pos; + } + return true; +} + +simdutf_really_inline simdutf_warn_unused bool validate(const char *buf, + size_t len) noexcept { + return validate(reinterpret_cast(buf), len); +} + +template +simdutf_constexpr23 simdutf_warn_unused result +validate_with_errors(BytePtr data, size_t len) noexcept { + static_assert( + std::is_same::type, uint8_t>::value, + "dereferencing the data pointer must result in a uint8_t"); + size_t pos = 0; + uint32_t code_point = 0; + while (pos < len) { + // check of the next 16 bytes are ascii. + size_t next_pos = pos + 16; + if (next_pos <= + len) { // if it is safe to read 16 more bytes, check that they are ascii + uint64_t v1; + std::memcpy(&v1, data + pos, sizeof(uint64_t)); + uint64_t v2; + std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); + uint64_t v{v1 | v2}; + if ((v & 0x8080808080808080) == 0) { + pos = next_pos; + continue; + } + } + unsigned char byte = data[pos]; + + while (byte < 0b10000000) { + if (++pos == len) { + return result(error_code::SUCCESS, len); + } + byte = data[pos]; + } + + if ((byte & 0b11100000) == 0b11000000) { + next_pos = pos + 2; + if (next_pos > len) { + return result(error_code::TOO_SHORT, pos); + } + if ((data[pos + 1] & 0b11000000) != 0b10000000) { + return result(error_code::TOO_SHORT, pos); + } + // range check + code_point = (byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111); + if ((code_point < 0x80) || (0x7ff < code_point)) { + return result(error_code::OVERLONG, pos); + } + } else if ((byte & 0b11110000) == 0b11100000) { + next_pos = pos + 3; + if (next_pos > len) { + return result(error_code::TOO_SHORT, pos); + } + if ((data[pos + 1] & 0b11000000) != 0b10000000) { + return result(error_code::TOO_SHORT, pos); + } + if ((data[pos + 2] & 0b11000000) != 0b10000000) { + return result(error_code::TOO_SHORT, pos); + } + // range check + code_point = (byte & 0b00001111) << 12 | + (data[pos + 1] & 0b00111111) << 6 | + (data[pos + 2] & 0b00111111); + if ((code_point < 0x800) || (0xffff < code_point)) { + return result(error_code::OVERLONG, pos); + } + if (0xd7ff < code_point && code_point < 0xe000) { + return result(error_code::SURROGATE, pos); + } + } else if ((byte & 0b11111000) == 0b11110000) { // 0b11110000 + next_pos = pos + 4; + if (next_pos > len) { + return result(error_code::TOO_SHORT, pos); + } + if ((data[pos + 1] & 0b11000000) != 0b10000000) { + return result(error_code::TOO_SHORT, pos); + } + if ((data[pos + 2] & 0b11000000) != 0b10000000) { + return result(error_code::TOO_SHORT, pos); + } + if ((data[pos + 3] & 0b11000000) != 0b10000000) { + return result(error_code::TOO_SHORT, pos); + } + // range check + code_point = + (byte & 0b00000111) << 18 | (data[pos + 1] & 0b00111111) << 12 | + (data[pos + 2] & 0b00111111) << 6 | (data[pos + 3] & 0b00111111); + if (code_point <= 0xffff) { + return result(error_code::OVERLONG, pos); + } + if (0x10ffff < code_point) { + return result(error_code::TOO_LARGE, pos); + } + } else { + // we either have too many continuation bytes or an invalid leading byte + if ((byte & 0b11000000) == 0b10000000) { + return result(error_code::TOO_LONG, pos); + } else { + return result(error_code::HEADER_BITS, pos); + } + } + pos = next_pos; + } + return result(error_code::SUCCESS, len); +} + +simdutf_really_inline simdutf_warn_unused result +validate_with_errors(const char *buf, size_t len) noexcept { + return validate_with_errors(reinterpret_cast(buf), len); +} + +// Finds the previous leading byte starting backward from buf and validates with +// errors from there Used to pinpoint the location of an error when an invalid +// chunk is detected We assume that the stream starts with a leading byte, and +// to check that it is the case, we ask that you pass a pointer to the start of +// the stream (start). +inline simdutf_warn_unused result rewind_and_validate_with_errors( + const char *start, const char *buf, size_t len) noexcept { + // First check that we start with a leading byte + if ((*start & 0b11000000) == 0b10000000) { + return result(error_code::TOO_LONG, 0); + } + size_t extra_len{0}; + // A leading byte cannot be further than 4 bytes away + for (int i = 0; i < 5; i++) { + unsigned char byte = *buf; + if ((byte & 0b11000000) != 0b10000000) { + break; + } else { + buf--; + extra_len++; + } + } + + result res = validate_with_errors(buf, len + extra_len); + res.count -= extra_len; + return res; +} + +template +#if SIMDUTF_CPLUSPLUS20 + requires simdutf::detail::indexes_into_byte_like +#endif +simdutf_constexpr23 size_t count_code_points(InputPtr data, size_t len) { + size_t counter{0}; + for (size_t i = 0; i < len; i++) { + // -65 is 0b10111111, anything larger in two-complement's should start a new + // code point. + if (int8_t(data[i]) > -65) { + counter++; + } + } + return counter; +} + +template +#if SIMDUTF_CPLUSPLUS20 + requires simdutf::detail::indexes_into_byte_like +#endif +simdutf_constexpr23 size_t utf16_length_from_utf8(InputPtr data, size_t len) { + size_t counter{0}; + for (size_t i = 0; i < len; i++) { + if (int8_t(data[i]) > -65) { + counter++; + } + if (uint8_t(data[i]) >= 240) { + counter++; + } + } + return counter; +} + +template +#if SIMDUTF_CPLUSPLUS20 + requires simdutf::detail::indexes_into_byte_like +#endif +simdutf_warn_unused simdutf_constexpr23 size_t +trim_partial_utf8(InputPtr input, size_t length) { + if (length < 3) { + switch (length) { + case 2: + if (uint8_t(input[length - 1]) >= 0xc0) { + return length - 1; + } // 2-, 3- and 4-byte characters with only 1 byte left + if (uint8_t(input[length - 2]) >= 0xe0) { + return length - 2; + } // 3- and 4-byte characters with only 2 bytes left + return length; + case 1: + if (uint8_t(input[length - 1]) >= 0xc0) { + return length - 1; + } // 2-, 3- and 4-byte characters with only 1 byte left + return length; + case 0: + return length; + } + } + if (uint8_t(input[length - 1]) >= 0xc0) { + return length - 1; + } // 2-, 3- and 4-byte characters with only 1 byte left + if (uint8_t(input[length - 2]) >= 0xe0) { + return length - 2; + } // 3- and 4-byte characters with only 1 byte left + if (uint8_t(input[length - 3]) >= 0xf0) { + return length - 3; + } // 4-byte characters with only 3 bytes left + return length; +} + +} // namespace utf8 +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/utf8.h */ +/* begin file include/simdutf/scalar/utf8_to_latin1/utf8_to_latin1.h */ +#ifndef SIMDUTF_UTF8_TO_LATIN1_H +#define SIMDUTF_UTF8_TO_LATIN1_H + +namespace simdutf { +namespace scalar { +namespace { +namespace utf8_to_latin1 { + +template +#if SIMDUTF_CPLUSPLUS20 + requires(simdutf::detail::indexes_into_byte_like && + simdutf::detail::indexes_into_byte_like) +#endif +simdutf_constexpr23 size_t convert(InputPtr data, size_t len, + OutputPtr latin_output) { + size_t pos = 0; + auto start = latin_output; + + while (pos < len) { +#if SIMDUTF_CPLUSPLUS23 + if !consteval +#endif + { + // try to convert the next block of 16 ASCII bytes + if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that + // they are ascii + uint64_t v1; + ::memcpy(&v1, data + pos, sizeof(uint64_t)); + uint64_t v2; + ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); + uint64_t v{v1 | v2}; // We are only interested in these bits: 1000 1000 + // 1000 1000 .... etc + if ((v & 0x8080808080808080) == + 0) { // if NONE of these are set, e.g. all of them are zero, then + // everything is ASCII + size_t final_pos = pos + 16; + while (pos < final_pos) { + *latin_output++ = char(data[pos]); + pos++; + } + continue; + } + } + } + + // suppose it is not an all ASCII byte sequence + uint8_t leading_byte = data[pos]; // leading byte + if (leading_byte < 0b10000000) { + // converting one ASCII byte !!! + *latin_output++ = char(leading_byte); + pos++; + } else if ((leading_byte & 0b11100000) == + 0b11000000) { // the first three bits indicate: + // We have a two-byte UTF-8 + if (pos + 1 >= len) { + return 0; + } // minimal bound checking + if ((data[pos + 1] & 0b11000000) != 0b10000000) { + return 0; + } // checks if the next byte is a valid continuation byte in UTF-8. A + // valid continuation byte starts with 10. + // range check - + uint32_t code_point = + (leading_byte & 0b00011111) << 6 | + (data[pos + 1] & + 0b00111111); // assembles the Unicode code point from the two bytes. + // It does this by discarding the leading 110 and 10 + // bits from the two bytes, shifting the remaining bits + // of the first byte, and then combining the results + // with a bitwise OR operation. + if (code_point < 0x80 || 0xFF < code_point) { + return 0; // We only care about the range 129-255 which is Non-ASCII + // latin1 characters. A code_point beneath 0x80 is invalid as + // it is already covered by bytes whose leading bit is zero. + } + *latin_output++ = char(code_point); + pos += 2; + } else { + return 0; + } + } + return latin_output - start; +} + +template +#if SIMDUTF_CPLUSPLUS20 + requires simdutf::detail::indexes_into_byte_like +#endif +simdutf_constexpr23 result convert_with_errors(InputPtr data, size_t len, + char *latin_output) { + size_t pos = 0; + char *start{latin_output}; + + while (pos < len) { +#if SIMDUTF_CPLUSPLUS23 + if !consteval +#endif + { + // try to convert the next block of 16 ASCII bytes + if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that + // they are ascii + uint64_t v1; + ::memcpy(&v1, data + pos, sizeof(uint64_t)); + uint64_t v2; + ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); + uint64_t v{v1 | v2}; // We are only interested in these bits: 1000 1000 + // 1000 1000...etc + if ((v & 0x8080808080808080) == + 0) { // if NONE of these are set, e.g. all of them are zero, then + // everything is ASCII + size_t final_pos = pos + 16; + while (pos < final_pos) { + *latin_output++ = char(data[pos]); + pos++; + } + continue; + } + } + } + // suppose it is not an all ASCII byte sequence + uint8_t leading_byte = data[pos]; // leading byte + if (leading_byte < 0b10000000) { + // converting one ASCII byte !!! + *latin_output++ = char(leading_byte); + pos++; + } else if ((leading_byte & 0b11100000) == + 0b11000000) { // the first three bits indicate: + // We have a two-byte UTF-8 + if (pos + 1 >= len) { + return result(error_code::TOO_SHORT, pos); + } // minimal bound checking + if ((data[pos + 1] & 0b11000000) != 0b10000000) { + return result(error_code::TOO_SHORT, pos); + } // checks if the next byte is a valid continuation byte in UTF-8. A + // valid continuation byte starts with 10. + // range check - + uint32_t code_point = + (leading_byte & 0b00011111) << 6 | + (data[pos + 1] & + 0b00111111); // assembles the Unicode code point from the two bytes. + // It does this by discarding the leading 110 and 10 + // bits from the two bytes, shifting the remaining bits + // of the first byte, and then combining the results + // with a bitwise OR operation. + if (code_point < 0x80) { + return result(error_code::OVERLONG, pos); + } + if (0xFF < code_point) { + return result(error_code::TOO_LARGE, pos); + } // We only care about the range 129-255 which is Non-ASCII latin1 + // characters + *latin_output++ = char(code_point); + pos += 2; + } else if ((leading_byte & 0b11110000) == 0b11100000) { + // We have a three-byte UTF-8 + return result(error_code::TOO_LARGE, pos); + } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000 + // we have a 4-byte UTF-8 word. + return result(error_code::TOO_LARGE, pos); + } else { + // we either have too many continuation bytes or an invalid leading byte + if ((leading_byte & 0b11000000) == 0b10000000) { + return result(error_code::TOO_LONG, pos); + } + + return result(error_code::HEADER_BITS, pos); + } + } + return result(error_code::SUCCESS, latin_output - start); +} + +inline result rewind_and_convert_with_errors(size_t prior_bytes, + const char *buf, size_t len, + char *latin1_output) { + size_t extra_len{0}; + // We potentially need to go back in time and find a leading byte. + // In theory '3' would be sufficient, but sometimes the error can go back + // quite far. + size_t how_far_back = prior_bytes; + // size_t how_far_back = 3; // 3 bytes in the past + current position + // if(how_far_back >= prior_bytes) { how_far_back = prior_bytes; } + bool found_leading_bytes{false}; + // important: it is i <= how_far_back and not 'i < how_far_back'. + for (size_t i = 0; i <= how_far_back; i++) { + unsigned char byte = buf[-static_cast(i)]; + found_leading_bytes = ((byte & 0b11000000) != 0b10000000); + if (found_leading_bytes) { + if (i > 0 && byte < 128) { + // If we had to go back and the leading byte is ascii + // then we can stop right away. + return result(error_code::TOO_LONG, 0 - i + 1); + } + buf -= i; + extra_len = i; + break; + } + } + // + // It is possible for this function to return a negative count in its result. + // C++ Standard Section 18.1 defines size_t is in which is described + // in C Standard as . C Standard Section 4.1.5 defines size_t as an + // unsigned integral type of the result of the sizeof operator + // + // An unsigned type will simply wrap round arithmetically (well defined). + // + if (!found_leading_bytes) { + // If how_far_back == 3, we may have four consecutive continuation bytes!!! + // [....] [continuation] [continuation] [continuation] | [buf is + // continuation] Or we possibly have a stream that does not start with a + // leading byte. + return result(error_code::TOO_LONG, 0 - how_far_back); + } + result res = convert_with_errors(buf, len + extra_len, latin1_output); + if (res.error) { + res.count -= extra_len; + } + return res; +} + +} // namespace utf8_to_latin1 +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/utf8_to_latin1/utf8_to_latin1.h */ +/* begin file include/simdutf/scalar/utf8_to_latin1/valid_utf8_to_latin1.h */ +#ifndef SIMDUTF_VALID_UTF8_TO_LATIN1_H +#define SIMDUTF_VALID_UTF8_TO_LATIN1_H + +namespace simdutf { +namespace scalar { +namespace { +namespace utf8_to_latin1 { + +template +#if SIMDUTF_CPLUSPLUS20 + requires simdutf::detail::indexes_into_byte_like +#endif +simdutf_constexpr23 size_t convert_valid(InputPtr data, size_t len, + char *latin_output) { + + size_t pos = 0; + char *start{latin_output}; + + while (pos < len) { +#if SIMDUTF_CPLUSPLUS23 + if !consteval +#endif + { + // try to convert the next block of 16 ASCII bytes + if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that + // they are ascii + uint64_t v1; + ::memcpy(&v1, data + pos, sizeof(uint64_t)); + uint64_t v2; + ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); + uint64_t v{v1 | + v2}; // We are only interested in these bits: 1000 1000 1000 + // 1000, so it makes sense to concatenate everything + if ((v & 0x8080808080808080) == + 0) { // if NONE of these are set, e.g. all of them are zero, then + // everything is ASCII + size_t final_pos = pos + 16; + while (pos < final_pos) { + *latin_output++ = uint8_t(data[pos]); + pos++; + } + continue; + } + } + } + + // suppose it is not an all ASCII byte sequence + auto leading_byte = uint8_t(data[pos]); // leading byte + if (leading_byte < 0b10000000) { + // converting one ASCII byte !!! + *latin_output++ = char(leading_byte); + pos++; + } else if ((leading_byte & 0b11100000) == + 0b11000000) { // the first three bits indicate: + // We have a two-byte UTF-8 + if (pos + 1 >= len) { + break; + } // minimal bound checking + if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) { + return 0; + } // checks if the next byte is a valid continuation byte in UTF-8. A + // valid continuation byte starts with 10. + // range check - + uint32_t code_point = + (leading_byte & 0b00011111) << 6 | + (uint8_t(data[pos + 1]) & + 0b00111111); // assembles the Unicode code point from the two bytes. + // It does this by discarding the leading 110 and 10 + // bits from the two bytes, shifting the remaining bits + // of the first byte, and then combining the results + // with a bitwise OR operation. + *latin_output++ = char(code_point); + pos += 2; + } else { + // we may have a continuation but we do not do error checking + return 0; + } + } + return latin_output - start; +} + +} // namespace utf8_to_latin1 +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/utf8_to_latin1/valid_utf8_to_latin1.h */ +/* begin file include/simdutf/scalar/utf8_to_utf16/utf8_to_utf16.h */ +#ifndef SIMDUTF_UTF8_TO_UTF16_H +#define SIMDUTF_UTF8_TO_UTF16_H + +namespace simdutf { +namespace scalar { +namespace { +namespace utf8_to_utf16 { + +template +#if SIMDUTF_CPLUSPLUS20 + requires simdutf::detail::indexes_into_byte_like +#endif +simdutf_constexpr23 size_t convert(InputPtr data, size_t len, + char16_t *utf16_output) { + size_t pos = 0; + char16_t *start{utf16_output}; + while (pos < len) { +#if SIMDUTF_CPLUSPLUS23 + if !consteval +#endif + // try to convert the next block of 16 ASCII bytes + { + if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that + // they are ascii + uint64_t v1; + ::memcpy(&v1, data + pos, sizeof(uint64_t)); + uint64_t v2; + ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); + uint64_t v{v1 | v2}; + if ((v & 0x8080808080808080) == 0) { + size_t final_pos = pos + 16; + while (pos < final_pos) { + *utf16_output++ = !match_system(big_endian) + ? char16_t(u16_swap_bytes(data[pos])) + : char16_t(data[pos]); + pos++; + } + continue; + } + } + } + + uint8_t leading_byte = data[pos]; // leading byte + if (leading_byte < 0b10000000) { + // converting one ASCII byte !!! + *utf16_output++ = !match_system(big_endian) + ? char16_t(u16_swap_bytes(leading_byte)) + : char16_t(leading_byte); + pos++; + } else if ((leading_byte & 0b11100000) == 0b11000000) { + // We have a two-byte UTF-8, it should become + // a single UTF-16 word. + if (pos + 1 >= len) { + return 0; + } // minimal bound checking + if ((data[pos + 1] & 0b11000000) != 0b10000000) { + return 0; + } + // range check + uint32_t code_point = + (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111); + if (code_point < 0x80 || 0x7ff < code_point) { + return 0; + } + if constexpr (!match_system(big_endian)) { + code_point = uint32_t(u16_swap_bytes(uint16_t(code_point))); + } + *utf16_output++ = char16_t(code_point); + pos += 2; + } else if ((leading_byte & 0b11110000) == 0b11100000) { + // We have a three-byte UTF-8, it should become + // a single UTF-16 word. + if (pos + 2 >= len) { + return 0; + } // minimal bound checking + + if ((data[pos + 1] & 0b11000000) != 0b10000000) { + return 0; + } + if ((data[pos + 2] & 0b11000000) != 0b10000000) { + return 0; + } + // range check + uint32_t code_point = (leading_byte & 0b00001111) << 12 | + (data[pos + 1] & 0b00111111) << 6 | + (data[pos + 2] & 0b00111111); + if (code_point < 0x800 || 0xffff < code_point || + (0xd7ff < code_point && code_point < 0xe000)) { + return 0; + } + if constexpr (!match_system(big_endian)) { + code_point = uint32_t(u16_swap_bytes(uint16_t(code_point))); + } + *utf16_output++ = char16_t(code_point); + pos += 3; + } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000 + // we have a 4-byte UTF-8 word. + if (pos + 3 >= len) { + return 0; + } // minimal bound checking + if ((data[pos + 1] & 0b11000000) != 0b10000000) { + return 0; + } + if ((data[pos + 2] & 0b11000000) != 0b10000000) { + return 0; + } + if ((data[pos + 3] & 0b11000000) != 0b10000000) { + return 0; + } + + // range check + uint32_t code_point = (leading_byte & 0b00000111) << 18 | + (data[pos + 1] & 0b00111111) << 12 | + (data[pos + 2] & 0b00111111) << 6 | + (data[pos + 3] & 0b00111111); + if (code_point <= 0xffff || 0x10ffff < code_point) { + return 0; + } + code_point -= 0x10000; + uint16_t high_surrogate = uint16_t(0xD800 + (code_point >> 10)); + uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF)); + if constexpr (!match_system(big_endian)) { + high_surrogate = u16_swap_bytes(high_surrogate); + low_surrogate = u16_swap_bytes(low_surrogate); + } + *utf16_output++ = char16_t(high_surrogate); + *utf16_output++ = char16_t(low_surrogate); + pos += 4; + } else { + return 0; + } + } + return utf16_output - start; +} + +template +#if SIMDUTF_CPLUSPLUS20 + requires simdutf::detail::indexes_into_byte_like +#endif +simdutf_constexpr23 result convert_with_errors(InputPtr data, size_t len, + char16_t *utf16_output) { + size_t pos = 0; + char16_t *start{utf16_output}; + while (pos < len) { +#if SIMDUTF_CPLUSPLUS23 + if !consteval +#endif + { + // try to convert the next block of 16 ASCII bytes + if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that + // they are ascii + uint64_t v1; + ::memcpy(&v1, data + pos, sizeof(uint64_t)); + uint64_t v2; + ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); + uint64_t v{v1 | v2}; + if ((v & 0x8080808080808080) == 0) { + size_t final_pos = pos + 16; + while (pos < final_pos) { + const char16_t byte = uint8_t(data[pos]); + *utf16_output++ = + !match_system(big_endian) ? u16_swap_bytes(byte) : byte; + pos++; + } + continue; + } + } + } + + auto leading_byte = uint8_t(data[pos]); // leading byte + if (leading_byte < 0b10000000) { + // converting one ASCII byte !!! + *utf16_output++ = !match_system(big_endian) + ? char16_t(u16_swap_bytes(leading_byte)) + : char16_t(leading_byte); + pos++; + } else if ((leading_byte & 0b11100000) == 0b11000000) { + // We have a two-byte UTF-8, it should become + // a single UTF-16 word. + if (pos + 1 >= len) { + return result(error_code::TOO_SHORT, pos); + } // minimal bound checking + if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) { + return result(error_code::TOO_SHORT, pos); + } + // range check + uint32_t code_point = (leading_byte & 0b00011111) << 6 | + (uint8_t(data[pos + 1]) & 0b00111111); + if (code_point < 0x80 || 0x7ff < code_point) { + return result(error_code::OVERLONG, pos); + } + if constexpr (!match_system(big_endian)) { + code_point = uint32_t(u16_swap_bytes(uint16_t(code_point))); + } + *utf16_output++ = char16_t(code_point); + pos += 2; + } else if ((leading_byte & 0b11110000) == 0b11100000) { + // We have a three-byte UTF-8, it should become + // a single UTF-16 word. + if (pos + 2 >= len) { + return result(error_code::TOO_SHORT, pos); + } // minimal bound checking + + if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) { + return result(error_code::TOO_SHORT, pos); + } + if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) { + return result(error_code::TOO_SHORT, pos); + } + // range check + uint32_t code_point = (leading_byte & 0b00001111) << 12 | + (uint8_t(data[pos + 1]) & 0b00111111) << 6 | + (uint8_t(data[pos + 2]) & 0b00111111); + if ((code_point < 0x800) || (0xffff < code_point)) { + return result(error_code::OVERLONG, pos); + } + if (0xd7ff < code_point && code_point < 0xe000) { + return result(error_code::SURROGATE, pos); + } + if constexpr (!match_system(big_endian)) { + code_point = uint32_t(u16_swap_bytes(uint16_t(code_point))); + } + *utf16_output++ = char16_t(code_point); + pos += 3; + } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000 + // we have a 4-byte UTF-8 word. + if (pos + 3 >= len) { + return result(error_code::TOO_SHORT, pos); + } // minimal bound checking + if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) { + return result(error_code::TOO_SHORT, pos); + } + if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) { + return result(error_code::TOO_SHORT, pos); + } + if ((uint8_t(data[pos + 3]) & 0b11000000) != 0b10000000) { + return result(error_code::TOO_SHORT, pos); + } + + // range check + uint32_t code_point = (leading_byte & 0b00000111) << 18 | + (uint8_t(data[pos + 1]) & 0b00111111) << 12 | + (uint8_t(data[pos + 2]) & 0b00111111) << 6 | + (uint8_t(data[pos + 3]) & 0b00111111); + if (code_point <= 0xffff) { + return result(error_code::OVERLONG, pos); + } + if (0x10ffff < code_point) { + return result(error_code::TOO_LARGE, pos); + } + code_point -= 0x10000; + uint16_t high_surrogate = uint16_t(0xD800 + (code_point >> 10)); + uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF)); + if constexpr (!match_system(big_endian)) { + high_surrogate = u16_swap_bytes(high_surrogate); + low_surrogate = u16_swap_bytes(low_surrogate); + } + *utf16_output++ = char16_t(high_surrogate); + *utf16_output++ = char16_t(low_surrogate); + pos += 4; + } else { + // we either have too many continuation bytes or an invalid leading byte + if ((leading_byte & 0b11000000) == 0b10000000) { + return result(error_code::TOO_LONG, pos); + } else { + return result(error_code::HEADER_BITS, pos); + } + } + } + return result(error_code::SUCCESS, utf16_output - start); +} + +/** + * When rewind_and_convert_with_errors is called, we are pointing at 'buf' and + * we have up to len input bytes left, and we encountered some error. It is + * possible that the error is at 'buf' exactly, but it could also be in the + * previous bytes (up to 3 bytes back). + * + * prior_bytes indicates how many bytes, prior to 'buf' may belong to the + * current memory section and can be safely accessed. We prior_bytes to access + * safely up to three bytes before 'buf'. + * + * The caller is responsible to ensure that len > 0. + * + * If the error is believed to have occurred prior to 'buf', the count value + * contain in the result will be SIZE_T - 1, SIZE_T - 2, or SIZE_T - 3. + */ +template +inline result rewind_and_convert_with_errors(size_t prior_bytes, + const char *buf, size_t len, + char16_t *utf16_output) { + size_t extra_len{0}; + // We potentially need to go back in time and find a leading byte. + // In theory '3' would be sufficient, but sometimes the error can go back + // quite far. + size_t how_far_back = prior_bytes; + // size_t how_far_back = 3; // 3 bytes in the past + current position + // if(how_far_back >= prior_bytes) { how_far_back = prior_bytes; } + bool found_leading_bytes{false}; + // important: it is i <= how_far_back and not 'i < how_far_back'. + for (size_t i = 0; i <= how_far_back; i++) { + unsigned char byte = buf[-static_cast(i)]; + found_leading_bytes = ((byte & 0b11000000) != 0b10000000); + if (found_leading_bytes) { + if (i > 0 && byte < 128) { + // If we had to go back and the leading byte is ascii + // then we can stop right away. + return result(error_code::TOO_LONG, 0 - i + 1); + } + buf -= i; + extra_len = i; + break; + } + } + // + // It is possible for this function to return a negative count in its result. + // C++ Standard Section 18.1 defines size_t is in which is described + // in C Standard as . C Standard Section 4.1.5 defines size_t as an + // unsigned integral type of the result of the sizeof operator + // + // An unsigned type will simply wrap round arithmetically (well defined). + // + if (!found_leading_bytes) { + // If how_far_back == 3, we may have four consecutive continuation bytes!!! + // [....] [continuation] [continuation] [continuation] | [buf is + // continuation] Or we possibly have a stream that does not start with a + // leading byte. + return result(error_code::TOO_LONG, 0 - how_far_back); + } + result res = convert_with_errors(buf, len + extra_len, utf16_output); + if (res.error) { + res.count -= extra_len; + } + return res; +} + +} // namespace utf8_to_utf16 +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/utf8_to_utf16/utf8_to_utf16.h */ +/* begin file include/simdutf/scalar/utf8_to_utf16/valid_utf8_to_utf16.h */ +#ifndef SIMDUTF_VALID_UTF8_TO_UTF16_H +#define SIMDUTF_VALID_UTF8_TO_UTF16_H + +namespace simdutf { +namespace scalar { +namespace { +namespace utf8_to_utf16 { + +template +#if SIMDUTF_CPLUSPLUS20 + requires simdutf::detail::indexes_into_byte_like +#endif +simdutf_constexpr23 size_t convert_valid(InputPtr data, size_t len, + char16_t *utf16_output) { + size_t pos = 0; + char16_t *start{utf16_output}; + while (pos < len) { +#if SIMDUTF_CPLUSPLUS23 + if !consteval +#endif + { // try to convert the next block of 8 ASCII bytes + if (pos + 8 <= len) { // if it is safe to read 8 more bytes, check that + // they are ascii + uint64_t v; + ::memcpy(&v, data + pos, sizeof(uint64_t)); + if ((v & 0x8080808080808080) == 0) { + size_t final_pos = pos + 8; + while (pos < final_pos) { + const char16_t byte = uint8_t(data[pos]); + *utf16_output++ = + !match_system(big_endian) ? u16_swap_bytes(byte) : byte; + pos++; + } + continue; + } + } + } + + auto leading_byte = uint8_t(data[pos]); // leading byte + if (leading_byte < 0b10000000) { + // converting one ASCII byte !!! + *utf16_output++ = !match_system(big_endian) + ? char16_t(u16_swap_bytes(leading_byte)) + : char16_t(leading_byte); + pos++; + } else if ((leading_byte & 0b11100000) == 0b11000000) { + // We have a two-byte UTF-8, it should become + // a single UTF-16 word. + if (pos + 1 >= len) { + break; + } // minimal bound checking + uint16_t code_point = uint16_t(((leading_byte & 0b00011111) << 6) | + (uint8_t(data[pos + 1]) & 0b00111111)); + if constexpr (!match_system(big_endian)) { + code_point = u16_swap_bytes(uint16_t(code_point)); + } + *utf16_output++ = char16_t(code_point); + pos += 2; + } else if ((leading_byte & 0b11110000) == 0b11100000) { + // We have a three-byte UTF-8, it should become + // a single UTF-16 word. + if (pos + 2 >= len) { + break; + } // minimal bound checking + uint16_t code_point = + uint16_t(((leading_byte & 0b00001111) << 12) | + ((uint8_t(data[pos + 1]) & 0b00111111) << 6) | + (uint8_t(data[pos + 2]) & 0b00111111)); + if constexpr (!match_system(big_endian)) { + code_point = u16_swap_bytes(uint16_t(code_point)); + } + *utf16_output++ = char16_t(code_point); + pos += 3; + } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000 + // we have a 4-byte UTF-8 word. + if (pos + 3 >= len) { + break; + } // minimal bound checking + uint32_t code_point = ((leading_byte & 0b00000111) << 18) | + ((uint8_t(data[pos + 1]) & 0b00111111) << 12) | + ((uint8_t(data[pos + 2]) & 0b00111111) << 6) | + (uint8_t(data[pos + 3]) & 0b00111111); + code_point -= 0x10000; + uint16_t high_surrogate = uint16_t(0xD800 + (code_point >> 10)); + uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF)); + if constexpr (!match_system(big_endian)) { + high_surrogate = u16_swap_bytes(high_surrogate); + low_surrogate = u16_swap_bytes(low_surrogate); + } + *utf16_output++ = char16_t(high_surrogate); + *utf16_output++ = char16_t(low_surrogate); + pos += 4; + } else { + // we may have a continuation but we do not do error checking + return 0; + } + } + return utf16_output - start; +} + +} // namespace utf8_to_utf16 +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/utf8_to_utf16/valid_utf8_to_utf16.h */ +/* begin file include/simdutf/scalar/utf8_to_utf32/utf8_to_utf32.h */ +#ifndef SIMDUTF_UTF8_TO_UTF32_H +#define SIMDUTF_UTF8_TO_UTF32_H + +namespace simdutf { +namespace scalar { +namespace { +namespace utf8_to_utf32 { + +template +#if SIMDUTF_CPLUSPLUS20 + requires simdutf::detail::indexes_into_byte_like +#endif +simdutf_constexpr23 size_t convert(InputPtr data, size_t len, + char32_t *utf32_output) { + size_t pos = 0; + char32_t *start{utf32_output}; + while (pos < len) { +#if SIMDUTF_CPLUSPLUS23 + if !consteval +#endif + { + // try to convert the next block of 16 ASCII bytes + if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that + // they are ascii + uint64_t v1; + ::memcpy(&v1, data + pos, sizeof(uint64_t)); + uint64_t v2; + ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); + uint64_t v{v1 | v2}; + if ((v & 0x8080808080808080) == 0) { + size_t final_pos = pos + 16; + while (pos < final_pos) { + *utf32_output++ = uint8_t(data[pos]); + pos++; + } + continue; + } + } + } + auto leading_byte = uint8_t(data[pos]); // leading byte + if (leading_byte < 0b10000000) { + // converting one ASCII byte !!! + *utf32_output++ = char32_t(leading_byte); + pos++; + } else if ((leading_byte & 0b11100000) == 0b11000000) { + // We have a two-byte UTF-8 + if (pos + 1 >= len) { + return 0; + } // minimal bound checking + if ((data[pos + 1] & 0b11000000) != 0b10000000) { + return 0; + } + // range check + uint32_t code_point = (leading_byte & 0b00011111) << 6 | + (uint8_t(data[pos + 1]) & 0b00111111); + if (code_point < 0x80 || 0x7ff < code_point) { + return 0; + } + *utf32_output++ = char32_t(code_point); + pos += 2; + } else if ((leading_byte & 0b11110000) == 0b11100000) { + // We have a three-byte UTF-8 + if (pos + 2 >= len) { + return 0; + } // minimal bound checking + + if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) { + return 0; + } + if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) { + return 0; + } + // range check + uint32_t code_point = (leading_byte & 0b00001111) << 12 | + (uint8_t(data[pos + 1]) & 0b00111111) << 6 | + (uint8_t(data[pos + 2]) & 0b00111111); + if (code_point < 0x800 || 0xffff < code_point || + (0xd7ff < code_point && code_point < 0xe000)) { + return 0; + } + *utf32_output++ = char32_t(code_point); + pos += 3; + } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000 + // we have a 4-byte UTF-8 word. + if (pos + 3 >= len) { + return 0; + } // minimal bound checking + if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) { + return 0; + } + if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) { + return 0; + } + if ((uint8_t(data[pos + 3]) & 0b11000000) != 0b10000000) { + return 0; + } + + // range check + uint32_t code_point = (leading_byte & 0b00000111) << 18 | + (uint8_t(data[pos + 1]) & 0b00111111) << 12 | + (uint8_t(data[pos + 2]) & 0b00111111) << 6 | + (uint8_t(data[pos + 3]) & 0b00111111); + if (code_point <= 0xffff || 0x10ffff < code_point) { + return 0; + } + *utf32_output++ = char32_t(code_point); + pos += 4; + } else { + return 0; + } + } + return utf32_output - start; +} + +template +#if SIMDUTF_CPLUSPLUS20 + requires simdutf::detail::indexes_into_byte_like +#endif +simdutf_constexpr23 result convert_with_errors(InputPtr data, size_t len, + char32_t *utf32_output) { + size_t pos = 0; + char32_t *start{utf32_output}; + while (pos < len) { +#if SIMDUTF_CPLUSPLUS23 + if !consteval +#endif + { + // try to convert the next block of 16 ASCII bytes + if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that + // they are ascii + uint64_t v1; + ::memcpy(&v1, data + pos, sizeof(uint64_t)); + uint64_t v2; + ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t)); + uint64_t v{v1 | v2}; + if ((v & 0x8080808080808080) == 0) { + size_t final_pos = pos + 16; + while (pos < final_pos) { + *utf32_output++ = uint8_t(data[pos]); + pos++; + } + continue; + } + } + } + auto leading_byte = uint8_t(data[pos]); // leading byte + if (leading_byte < 0b10000000) { + // converting one ASCII byte !!! + *utf32_output++ = char32_t(leading_byte); + pos++; + } else if ((leading_byte & 0b11100000) == 0b11000000) { + // We have a two-byte UTF-8 + if (pos + 1 >= len) { + return result(error_code::TOO_SHORT, pos); + } // minimal bound checking + if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) { + return result(error_code::TOO_SHORT, pos); + } + // range check + uint32_t code_point = (leading_byte & 0b00011111) << 6 | + (uint8_t(data[pos + 1]) & 0b00111111); + if (code_point < 0x80 || 0x7ff < code_point) { + return result(error_code::OVERLONG, pos); + } + *utf32_output++ = char32_t(code_point); + pos += 2; + } else if ((leading_byte & 0b11110000) == 0b11100000) { + // We have a three-byte UTF-8 + if (pos + 2 >= len) { + return result(error_code::TOO_SHORT, pos); + } // minimal bound checking + + if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) { + return result(error_code::TOO_SHORT, pos); + } + if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) { + return result(error_code::TOO_SHORT, pos); + } + // range check + uint32_t code_point = (leading_byte & 0b00001111) << 12 | + (uint8_t(data[pos + 1]) & 0b00111111) << 6 | + (uint8_t(data[pos + 2]) & 0b00111111); + if (code_point < 0x800 || 0xffff < code_point) { + return result(error_code::OVERLONG, pos); + } + if (0xd7ff < code_point && code_point < 0xe000) { + return result(error_code::SURROGATE, pos); + } + *utf32_output++ = char32_t(code_point); + pos += 3; + } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000 + // we have a 4-byte UTF-8 word. + if (pos + 3 >= len) { + return result(error_code::TOO_SHORT, pos); + } // minimal bound checking + if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) { + return result(error_code::TOO_SHORT, pos); + } + if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) { + return result(error_code::TOO_SHORT, pos); + } + if ((uint8_t(data[pos + 3]) & 0b11000000) != 0b10000000) { + return result(error_code::TOO_SHORT, pos); + } + + // range check + uint32_t code_point = (leading_byte & 0b00000111) << 18 | + (uint8_t(data[pos + 1]) & 0b00111111) << 12 | + (uint8_t(data[pos + 2]) & 0b00111111) << 6 | + (uint8_t(data[pos + 3]) & 0b00111111); + if (code_point <= 0xffff) { + return result(error_code::OVERLONG, pos); + } + if (0x10ffff < code_point) { + return result(error_code::TOO_LARGE, pos); + } + *utf32_output++ = char32_t(code_point); + pos += 4; + } else { + // we either have too many continuation bytes or an invalid leading byte + if ((leading_byte & 0b11000000) == 0b10000000) { + return result(error_code::TOO_LONG, pos); + } else { + return result(error_code::HEADER_BITS, pos); + } + } + } + return result(error_code::SUCCESS, utf32_output - start); +} + +/** + * When rewind_and_convert_with_errors is called, we are pointing at 'buf' and + * we have up to len input bytes left, and we encountered some error. It is + * possible that the error is at 'buf' exactly, but it could also be in the + * previous bytes location (up to 3 bytes back). + * + * prior_bytes indicates how many bytes, prior to 'buf' may belong to the + * current memory section and can be safely accessed. We prior_bytes to access + * safely up to three bytes before 'buf'. + * + * The caller is responsible to ensure that len > 0. + * + * If the error is believed to have occurred prior to 'buf', the count value + * contain in the result will be SIZE_T - 1, SIZE_T - 2, or SIZE_T - 3. + */ +inline result rewind_and_convert_with_errors(size_t prior_bytes, + const char *buf, size_t len, + char32_t *utf32_output) { + size_t extra_len{0}; + // We potentially need to go back in time and find a leading byte. + size_t how_far_back = 3; // 3 bytes in the past + current position + if (how_far_back > prior_bytes) { + how_far_back = prior_bytes; + } + bool found_leading_bytes{false}; + // important: it is i <= how_far_back and not 'i < how_far_back'. + for (size_t i = 0; i <= how_far_back; i++) { + unsigned char byte = buf[-static_cast(i)]; + found_leading_bytes = ((byte & 0b11000000) != 0b10000000); + if (found_leading_bytes) { + if (i > 0 && byte < 128) { + // If we had to go back and the leading byte is ascii + // then we can stop right away. + return result(error_code::TOO_LONG, 0 - i + 1); + } + buf -= i; + extra_len = i; + break; + } + } + // + // It is possible for this function to return a negative count in its result. + // C++ Standard Section 18.1 defines size_t is in which is described + // in C Standard as . C Standard Section 4.1.5 defines size_t as an + // unsigned integral type of the result of the sizeof operator + // + // An unsigned type will simply wrap round arithmetically (well defined). + // + if (!found_leading_bytes) { + // If how_far_back == 3, we may have four consecutive continuation bytes!!! + // [....] [continuation] [continuation] [continuation] | [buf is + // continuation] Or we possibly have a stream that does not start with a + // leading byte. + return result(error_code::TOO_LONG, 0 - how_far_back); + } + + result res = convert_with_errors(buf, len + extra_len, utf32_output); + if (res.error) { + res.count -= extra_len; + } + return res; +} + +} // namespace utf8_to_utf32 +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/utf8_to_utf32/utf8_to_utf32.h */ +/* begin file include/simdutf/scalar/utf8_to_utf32/valid_utf8_to_utf32.h */ +#ifndef SIMDUTF_VALID_UTF8_TO_UTF32_H +#define SIMDUTF_VALID_UTF8_TO_UTF32_H + +namespace simdutf { +namespace scalar { +namespace { +namespace utf8_to_utf32 { + +template +#if SIMDUTF_CPLUSPLUS20 + requires simdutf::detail::indexes_into_byte_like +#endif +simdutf_constexpr23 size_t convert_valid(InputPtr data, size_t len, + char32_t *utf32_output) { + size_t pos = 0; + char32_t *start{utf32_output}; + while (pos < len) { +#if SIMDUTF_CPLUSPLUS23 + if !consteval +#endif + { + // try to convert the next block of 8 ASCII bytes + if (pos + 8 <= len) { // if it is safe to read 8 more bytes, check that + // they are ascii + uint64_t v; + ::memcpy(&v, data + pos, sizeof(uint64_t)); + if ((v & 0x8080808080808080) == 0) { + size_t final_pos = pos + 8; + while (pos < final_pos) { + *utf32_output++ = uint8_t(data[pos]); + pos++; + } + continue; + } + } + } + auto leading_byte = uint8_t(data[pos]); // leading byte + if (leading_byte < 0b10000000) { + // converting one ASCII byte !!! + *utf32_output++ = char32_t(leading_byte); + pos++; + } else if ((leading_byte & 0b11100000) == 0b11000000) { + // We have a two-byte UTF-8 + if (pos + 1 >= len) { + break; + } // minimal bound checking + *utf32_output++ = char32_t(((leading_byte & 0b00011111) << 6) | + (uint8_t(data[pos + 1]) & 0b00111111)); + pos += 2; + } else if ((leading_byte & 0b11110000) == 0b11100000) { + // We have a three-byte UTF-8 + if (pos + 2 >= len) { + break; + } // minimal bound checking + *utf32_output++ = char32_t(((leading_byte & 0b00001111) << 12) | + ((uint8_t(data[pos + 1]) & 0b00111111) << 6) | + (uint8_t(data[pos + 2]) & 0b00111111)); + pos += 3; + } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000 + // we have a 4-byte UTF-8 word. + if (pos + 3 >= len) { + break; + } // minimal bound checking + uint32_t code_word = ((leading_byte & 0b00000111) << 18) | + ((uint8_t(data[pos + 1]) & 0b00111111) << 12) | + ((uint8_t(data[pos + 2]) & 0b00111111) << 6) | + (uint8_t(data[pos + 3]) & 0b00111111); + *utf32_output++ = char32_t(code_word); + pos += 4; + } else { + // we may have a continuation but we do not do error checking + return 0; + } + } + return utf32_output - start; +} + +} // namespace utf8_to_utf32 +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/utf8_to_utf32/valid_utf8_to_utf32.h */ + +namespace simdutf { + +constexpr size_t default_line_length = + 76; ///< default line length for base64 encoding with lines + + #ifndef SIMDUTF_NEED_TRAILING_ZEROES + #define SIMDUTF_NEED_TRAILING_ZEROES 1 + #endif + +// base64_options are used to specify the base64 encoding options. +// ASCII spaces are ' ', '\t', '\n', '\r', '\f' +// garbage characters are characters that are not part of the base64 alphabet +// nor ASCII spaces. +constexpr uint64_t base64_reverse_padding = + 2; /* modifier for base64_default and base64_url */ +enum base64_options : uint64_t { + base64_default = 0, /* standard base64 format (with padding) */ + base64_url = 1, /* base64url format (no padding) */ + base64_default_no_padding = + base64_default | + base64_reverse_padding, /* standard base64 format without padding */ + base64_url_with_padding = + base64_url | base64_reverse_padding, /* base64url with padding */ + base64_default_accept_garbage = + 4, /* standard base64 format accepting garbage characters, the input stops + with the first '=' if any */ + base64_url_accept_garbage = + 5, /* base64url format accepting garbage characters, the input stops with + the first '=' if any */ + base64_default_or_url = + 8, /* standard/base64url hybrid format (only meaningful for decoding!) */ + base64_default_or_url_accept_garbage = + 12, /* standard/base64url hybrid format accepting garbage characters + (only meaningful for decoding!), the input stops with the first '=' + if any */ +}; + +// last_chunk_handling_options are used to specify the handling of the last +// chunk in base64 decoding. +// https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64 +enum last_chunk_handling_options : uint64_t { + loose = 0, /* standard base64 format, decode partial final chunk */ + strict = 1, /* error when the last chunk is partial, 2 or 3 chars, and + unpadded, or non-zero bit padding */ + stop_before_partial = + 2, /* if the last chunk is partial, ignore it (no error) */ + only_full_chunks = + 3 /* only decode full blocks (4 base64 characters, no padding) */ +}; + +inline simdutf_constexpr23 bool +is_partial(last_chunk_handling_options options) { + return (options == stop_before_partial) || (options == only_full_chunks); +} + +namespace detail { +simdutf_warn_unused const char *find(const char *start, const char *end, + char character) noexcept; +simdutf_warn_unused const char16_t * +find(const char16_t *start, const char16_t *end, char16_t character) noexcept; +} // namespace detail + +/** + * Find the first occurrence of a character in a string. If the character is + * not found, return a pointer to the end of the string. + * @param start the start of the string + * @param end the end of the string + * @param character the character to find + * @return a pointer to the first occurrence of the character in the string, + * or a pointer to the end of the string if the character is not found. + * + */ +simdutf_warn_unused simdutf_really_inline simdutf_constexpr23 const char * +find(const char *start, const char *end, char character) noexcept { + #if SIMDUTF_CPLUSPLUS23 + if consteval { + for (; start != end; ++start) + if (*start == character) + return start; + return end; + } else + #endif + { + return detail::find(start, end, character); + } +} +simdutf_warn_unused simdutf_really_inline simdutf_constexpr23 const char16_t * +find(const char16_t *start, const char16_t *end, char16_t character) noexcept { + // implementation note: this is repeated instead of a template, to ensure + // the api is still a function and compiles without concepts + #if SIMDUTF_CPLUSPLUS23 + if consteval { + for (; start != end; ++start) + if (*start == character) + return start; + return end; + } else + #endif + { + return detail::find(start, end, character); + } +} +} + // We include base64_tables once. +/* begin file include/simdutf/base64_tables.h */ +#ifndef SIMDUTF_BASE64_TABLES_H +#define SIMDUTF_BASE64_TABLES_H +#include + +namespace simdutf { +namespace { +namespace tables { +namespace base64 { +namespace base64_default { + +constexpr char e0[256] = { + 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'C', 'C', 'C', 'C', 'D', 'D', 'D', + 'D', 'E', 'E', 'E', 'E', 'F', 'F', 'F', 'F', 'G', 'G', 'G', 'G', 'H', 'H', + 'H', 'H', 'I', 'I', 'I', 'I', 'J', 'J', 'J', 'J', 'K', 'K', 'K', 'K', 'L', + 'L', 'L', 'L', 'M', 'M', 'M', 'M', 'N', 'N', 'N', 'N', 'O', 'O', 'O', 'O', + 'P', 'P', 'P', 'P', 'Q', 'Q', 'Q', 'Q', 'R', 'R', 'R', 'R', 'S', 'S', 'S', + 'S', 'T', 'T', 'T', 'T', 'U', 'U', 'U', 'U', 'V', 'V', 'V', 'V', 'W', 'W', + 'W', 'W', 'X', 'X', 'X', 'X', 'Y', 'Y', 'Y', 'Y', 'Z', 'Z', 'Z', 'Z', 'a', + 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'c', 'd', 'd', 'd', 'd', + 'e', 'e', 'e', 'e', 'f', 'f', 'f', 'f', 'g', 'g', 'g', 'g', 'h', 'h', 'h', + 'h', 'i', 'i', 'i', 'i', 'j', 'j', 'j', 'j', 'k', 'k', 'k', 'k', 'l', 'l', + 'l', 'l', 'm', 'm', 'm', 'm', 'n', 'n', 'n', 'n', 'o', 'o', 'o', 'o', 'p', + 'p', 'p', 'p', 'q', 'q', 'q', 'q', 'r', 'r', 'r', 'r', 's', 's', 's', 's', + 't', 't', 't', 't', 'u', 'u', 'u', 'u', 'v', 'v', 'v', 'v', 'w', 'w', 'w', + 'w', 'x', 'x', 'x', 'x', 'y', 'y', 'y', 'y', 'z', 'z', 'z', 'z', '0', '0', + '0', '0', '1', '1', '1', '1', '2', '2', '2', '2', '3', '3', '3', '3', '4', + '4', '4', '4', '5', '5', '5', '5', '6', '6', '6', '6', '7', '7', '7', '7', + '8', '8', '8', '8', '9', '9', '9', '9', '+', '+', '+', '+', '/', '/', '/', + '/'}; + +constexpr char e1[256] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', + 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', + 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', + 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', 'C', + 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', + '/'}; + +constexpr char e2[256] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', + 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', + 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', + 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', 'C', + 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', + '/'}; + +constexpr uint32_t d0[256] = { + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x000000f8, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000fc, + 0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4, + 0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, + 0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018, + 0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030, + 0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048, + 0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060, + 0x00000064, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078, + 0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090, + 0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8, + 0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0, + 0x000000c4, 0x000000c8, 0x000000cc, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; + +constexpr uint32_t d1[256] = { + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x0000e003, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000f003, + 0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003, + 0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, + 0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000, + 0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000, + 0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001, + 0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001, + 0x00009001, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001, + 0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002, + 0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002, + 0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003, + 0x00001003, 0x00002003, 0x00003003, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; + +constexpr uint32_t d2[256] = { + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x00800f00, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00c00f00, + 0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00, + 0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, + 0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100, + 0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300, + 0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400, + 0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600, + 0x00400600, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700, + 0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900, + 0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00, + 0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00, + 0x00400c00, 0x00800c00, 0x00c00c00, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; + +constexpr uint32_t d3[256] = { + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x003e0000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003f0000, + 0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000, + 0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, + 0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000, + 0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000, + 0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000, + 0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000, + 0x00190000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000, + 0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000, + 0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000, + 0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000, + 0x00310000, 0x00320000, 0x00330000, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; +} // namespace base64_default + +namespace base64_url { + +constexpr char e0[256] = { + 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'C', 'C', 'C', 'C', 'D', 'D', 'D', + 'D', 'E', 'E', 'E', 'E', 'F', 'F', 'F', 'F', 'G', 'G', 'G', 'G', 'H', 'H', + 'H', 'H', 'I', 'I', 'I', 'I', 'J', 'J', 'J', 'J', 'K', 'K', 'K', 'K', 'L', + 'L', 'L', 'L', 'M', 'M', 'M', 'M', 'N', 'N', 'N', 'N', 'O', 'O', 'O', 'O', + 'P', 'P', 'P', 'P', 'Q', 'Q', 'Q', 'Q', 'R', 'R', 'R', 'R', 'S', 'S', 'S', + 'S', 'T', 'T', 'T', 'T', 'U', 'U', 'U', 'U', 'V', 'V', 'V', 'V', 'W', 'W', + 'W', 'W', 'X', 'X', 'X', 'X', 'Y', 'Y', 'Y', 'Y', 'Z', 'Z', 'Z', 'Z', 'a', + 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'c', 'd', 'd', 'd', 'd', + 'e', 'e', 'e', 'e', 'f', 'f', 'f', 'f', 'g', 'g', 'g', 'g', 'h', 'h', 'h', + 'h', 'i', 'i', 'i', 'i', 'j', 'j', 'j', 'j', 'k', 'k', 'k', 'k', 'l', 'l', + 'l', 'l', 'm', 'm', 'm', 'm', 'n', 'n', 'n', 'n', 'o', 'o', 'o', 'o', 'p', + 'p', 'p', 'p', 'q', 'q', 'q', 'q', 'r', 'r', 'r', 'r', 's', 's', 's', 's', + 't', 't', 't', 't', 'u', 'u', 'u', 'u', 'v', 'v', 'v', 'v', 'w', 'w', 'w', + 'w', 'x', 'x', 'x', 'x', 'y', 'y', 'y', 'y', 'z', 'z', 'z', 'z', '0', '0', + '0', '0', '1', '1', '1', '1', '2', '2', '2', '2', '3', '3', '3', '3', '4', + '4', '4', '4', '5', '5', '5', '5', '6', '6', '6', '6', '7', '7', '7', '7', + '8', '8', '8', '8', '9', '9', '9', '9', '-', '-', '-', '-', '_', '_', '_', + '_'}; + +constexpr char e1[256] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', + 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '-', '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', + 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '-', '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', + 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', 'A', 'B', 'C', + 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', + '_'}; + +constexpr char e2[256] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', + 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '-', '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', + 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '-', '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', + 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', 'A', 'B', 'C', + 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', + '_'}; + +constexpr uint32_t d0[256] = { + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000f8, 0x01ffffff, 0x01ffffff, + 0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4, + 0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, + 0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018, + 0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030, + 0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048, + 0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060, + 0x00000064, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000fc, + 0x01ffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078, + 0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090, + 0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8, + 0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0, + 0x000000c4, 0x000000c8, 0x000000cc, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; +constexpr uint32_t d1[256] = { + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000e003, 0x01ffffff, 0x01ffffff, + 0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003, + 0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, + 0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000, + 0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000, + 0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001, + 0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001, + 0x00009001, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000f003, + 0x01ffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001, + 0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002, + 0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002, + 0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003, + 0x00001003, 0x00002003, 0x00003003, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; +constexpr uint32_t d2[256] = { + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00800f00, 0x01ffffff, 0x01ffffff, + 0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00, + 0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, + 0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100, + 0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300, + 0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400, + 0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600, + 0x00400600, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00c00f00, + 0x01ffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700, + 0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900, + 0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00, + 0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00, + 0x00400c00, 0x00800c00, 0x00c00c00, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; +constexpr uint32_t d3[256] = { + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003e0000, 0x01ffffff, 0x01ffffff, + 0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000, + 0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, + 0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000, + 0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000, + 0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000, + 0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000, + 0x00190000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003f0000, + 0x01ffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000, + 0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000, + 0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000, + 0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000, + 0x00310000, 0x00320000, 0x00330000, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; +} // namespace base64_url + +namespace base64_default_or_url { +constexpr uint32_t d0[256] = { + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x000000f8, 0x01ffffff, 0x000000f8, 0x01ffffff, 0x000000fc, + 0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4, + 0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, + 0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018, + 0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030, + 0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048, + 0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060, + 0x00000064, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000fc, + 0x01ffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078, + 0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090, + 0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8, + 0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0, + 0x000000c4, 0x000000c8, 0x000000cc, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; +constexpr uint32_t d1[256] = { + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x0000e003, 0x01ffffff, 0x0000e003, 0x01ffffff, 0x0000f003, + 0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003, + 0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, + 0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000, + 0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000, + 0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001, + 0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001, + 0x00009001, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000f003, + 0x01ffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001, + 0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002, + 0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002, + 0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003, + 0x00001003, 0x00002003, 0x00003003, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; +constexpr uint32_t d2[256] = { + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x00800f00, 0x01ffffff, 0x00800f00, 0x01ffffff, 0x00c00f00, + 0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00, + 0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, + 0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100, + 0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300, + 0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400, + 0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600, + 0x00400600, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00c00f00, + 0x01ffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700, + 0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900, + 0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00, + 0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00, + 0x00400c00, 0x00800c00, 0x00c00c00, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; +constexpr uint32_t d3[256] = { + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x003e0000, 0x01ffffff, 0x003e0000, 0x01ffffff, 0x003f0000, + 0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000, + 0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000, + 0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000, + 0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000, + 0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000, + 0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000, + 0x00190000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003f0000, + 0x01ffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000, + 0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000, + 0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000, + 0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000, + 0x00310000, 0x00320000, 0x00330000, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, + 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}; +} // namespace base64_default_or_url +constexpr uint64_t thintable_epi8[256] = { + 0x0706050403020100, 0x0007060504030201, 0x0007060504030200, + 0x0000070605040302, 0x0007060504030100, 0x0000070605040301, + 0x0000070605040300, 0x0000000706050403, 0x0007060504020100, + 0x0000070605040201, 0x0000070605040200, 0x0000000706050402, + 0x0000070605040100, 0x0000000706050401, 0x0000000706050400, + 0x0000000007060504, 0x0007060503020100, 0x0000070605030201, + 0x0000070605030200, 0x0000000706050302, 0x0000070605030100, + 0x0000000706050301, 0x0000000706050300, 0x0000000007060503, + 0x0000070605020100, 0x0000000706050201, 0x0000000706050200, + 0x0000000007060502, 0x0000000706050100, 0x0000000007060501, + 0x0000000007060500, 0x0000000000070605, 0x0007060403020100, + 0x0000070604030201, 0x0000070604030200, 0x0000000706040302, + 0x0000070604030100, 0x0000000706040301, 0x0000000706040300, + 0x0000000007060403, 0x0000070604020100, 0x0000000706040201, + 0x0000000706040200, 0x0000000007060402, 0x0000000706040100, + 0x0000000007060401, 0x0000000007060400, 0x0000000000070604, + 0x0000070603020100, 0x0000000706030201, 0x0000000706030200, + 0x0000000007060302, 0x0000000706030100, 0x0000000007060301, + 0x0000000007060300, 0x0000000000070603, 0x0000000706020100, + 0x0000000007060201, 0x0000000007060200, 0x0000000000070602, + 0x0000000007060100, 0x0000000000070601, 0x0000000000070600, + 0x0000000000000706, 0x0007050403020100, 0x0000070504030201, + 0x0000070504030200, 0x0000000705040302, 0x0000070504030100, + 0x0000000705040301, 0x0000000705040300, 0x0000000007050403, + 0x0000070504020100, 0x0000000705040201, 0x0000000705040200, + 0x0000000007050402, 0x0000000705040100, 0x0000000007050401, + 0x0000000007050400, 0x0000000000070504, 0x0000070503020100, + 0x0000000705030201, 0x0000000705030200, 0x0000000007050302, + 0x0000000705030100, 0x0000000007050301, 0x0000000007050300, + 0x0000000000070503, 0x0000000705020100, 0x0000000007050201, + 0x0000000007050200, 0x0000000000070502, 0x0000000007050100, + 0x0000000000070501, 0x0000000000070500, 0x0000000000000705, + 0x0000070403020100, 0x0000000704030201, 0x0000000704030200, + 0x0000000007040302, 0x0000000704030100, 0x0000000007040301, + 0x0000000007040300, 0x0000000000070403, 0x0000000704020100, + 0x0000000007040201, 0x0000000007040200, 0x0000000000070402, + 0x0000000007040100, 0x0000000000070401, 0x0000000000070400, + 0x0000000000000704, 0x0000000703020100, 0x0000000007030201, + 0x0000000007030200, 0x0000000000070302, 0x0000000007030100, + 0x0000000000070301, 0x0000000000070300, 0x0000000000000703, + 0x0000000007020100, 0x0000000000070201, 0x0000000000070200, + 0x0000000000000702, 0x0000000000070100, 0x0000000000000701, + 0x0000000000000700, 0x0000000000000007, 0x0006050403020100, + 0x0000060504030201, 0x0000060504030200, 0x0000000605040302, + 0x0000060504030100, 0x0000000605040301, 0x0000000605040300, + 0x0000000006050403, 0x0000060504020100, 0x0000000605040201, + 0x0000000605040200, 0x0000000006050402, 0x0000000605040100, + 0x0000000006050401, 0x0000000006050400, 0x0000000000060504, + 0x0000060503020100, 0x0000000605030201, 0x0000000605030200, + 0x0000000006050302, 0x0000000605030100, 0x0000000006050301, + 0x0000000006050300, 0x0000000000060503, 0x0000000605020100, + 0x0000000006050201, 0x0000000006050200, 0x0000000000060502, + 0x0000000006050100, 0x0000000000060501, 0x0000000000060500, + 0x0000000000000605, 0x0000060403020100, 0x0000000604030201, + 0x0000000604030200, 0x0000000006040302, 0x0000000604030100, + 0x0000000006040301, 0x0000000006040300, 0x0000000000060403, + 0x0000000604020100, 0x0000000006040201, 0x0000000006040200, + 0x0000000000060402, 0x0000000006040100, 0x0000000000060401, + 0x0000000000060400, 0x0000000000000604, 0x0000000603020100, + 0x0000000006030201, 0x0000000006030200, 0x0000000000060302, + 0x0000000006030100, 0x0000000000060301, 0x0000000000060300, + 0x0000000000000603, 0x0000000006020100, 0x0000000000060201, + 0x0000000000060200, 0x0000000000000602, 0x0000000000060100, + 0x0000000000000601, 0x0000000000000600, 0x0000000000000006, + 0x0000050403020100, 0x0000000504030201, 0x0000000504030200, + 0x0000000005040302, 0x0000000504030100, 0x0000000005040301, + 0x0000000005040300, 0x0000000000050403, 0x0000000504020100, + 0x0000000005040201, 0x0000000005040200, 0x0000000000050402, + 0x0000000005040100, 0x0000000000050401, 0x0000000000050400, + 0x0000000000000504, 0x0000000503020100, 0x0000000005030201, + 0x0000000005030200, 0x0000000000050302, 0x0000000005030100, + 0x0000000000050301, 0x0000000000050300, 0x0000000000000503, + 0x0000000005020100, 0x0000000000050201, 0x0000000000050200, + 0x0000000000000502, 0x0000000000050100, 0x0000000000000501, + 0x0000000000000500, 0x0000000000000005, 0x0000000403020100, + 0x0000000004030201, 0x0000000004030200, 0x0000000000040302, + 0x0000000004030100, 0x0000000000040301, 0x0000000000040300, + 0x0000000000000403, 0x0000000004020100, 0x0000000000040201, + 0x0000000000040200, 0x0000000000000402, 0x0000000000040100, + 0x0000000000000401, 0x0000000000000400, 0x0000000000000004, + 0x0000000003020100, 0x0000000000030201, 0x0000000000030200, + 0x0000000000000302, 0x0000000000030100, 0x0000000000000301, + 0x0000000000000300, 0x0000000000000003, 0x0000000000020100, + 0x0000000000000201, 0x0000000000000200, 0x0000000000000002, + 0x0000000000000100, 0x0000000000000001, 0x0000000000000000, + 0x0000000000000000, +}; + +constexpr uint8_t pshufb_combine_table[272] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x01, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +constexpr unsigned char BitsSetTable256mul2[256] = { + 0, 2, 2, 4, 2, 4, 4, 6, 2, 4, 4, 6, 4, 6, 6, 8, 2, 4, 4, + 6, 4, 6, 6, 8, 4, 6, 6, 8, 6, 8, 8, 10, 2, 4, 4, 6, 4, 6, + 6, 8, 4, 6, 6, 8, 6, 8, 8, 10, 4, 6, 6, 8, 6, 8, 8, 10, 6, + 8, 8, 10, 8, 10, 10, 12, 2, 4, 4, 6, 4, 6, 6, 8, 4, 6, 6, 8, + 6, 8, 8, 10, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 10, 8, 10, 10, + 12, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 10, 8, 10, 10, 12, 6, 8, + 8, 10, 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 12, 14, 2, 4, 4, 6, 4, + 6, 6, 8, 4, 6, 6, 8, 6, 8, 8, 10, 4, 6, 6, 8, 6, 8, 8, 10, + 6, 8, 8, 10, 8, 10, 10, 12, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, + 10, 8, 10, 10, 12, 6, 8, 8, 10, 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, + 12, 14, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 10, 8, 10, 10, 12, 6, + 8, 8, 10, 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 12, 14, 6, 8, 8, 10, + 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 12, 14, 8, 10, 10, 12, 10, 12, 12, + 14, 10, 12, 12, 14, 12, 14, 14, 16}; + +constexpr uint8_t to_base64_value[] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 64, 64, 255, 64, 64, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 64, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, + 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, + 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255}; + +constexpr uint8_t to_base64_url_value[] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 64, 64, 255, 64, 64, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 64, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 62, 255, 255, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, + 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 255, 255, 255, 255, 63, 255, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255}; + +constexpr uint8_t to_base64_default_or_url_value[] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 64, 64, 255, 64, 64, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 64, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, + 62, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, + 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 255, 255, 255, 255, 63, 255, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255}; + +static_assert(sizeof(to_base64_value) == 256, + "to_base64_value must have 256 elements"); +static_assert(sizeof(to_base64_url_value) == 256, + "to_base64_url_value must have 256 elements"); +static_assert(to_base64_value[uint8_t(' ')] == 64, + "space must be == 64 in to_base64_value"); +static_assert(to_base64_url_value[uint8_t(' ')] == 64, + "space must be == 64 in to_base64_url_value"); +static_assert(to_base64_value[uint8_t('\t')] == 64, + "tab must be == 64 in to_base64_value"); +static_assert(to_base64_url_value[uint8_t('\t')] == 64, + "tab must be == 64 in to_base64_url_value"); +static_assert(to_base64_value[uint8_t('\r')] == 64, + "cr must be == 64 in to_base64_value"); +static_assert(to_base64_url_value[uint8_t('\r')] == 64, + "cr must be == 64 in to_base64_url_value"); +static_assert(to_base64_value[uint8_t('\n')] == 64, + "lf must be == 64 in to_base64_value"); +static_assert(to_base64_url_value[uint8_t('\n')] == 64, + "lf must be == 64 in to_base64_url_value"); +static_assert(to_base64_value[uint8_t('\f')] == 64, + "ff must be == 64 in to_base64_value"); +static_assert(to_base64_url_value[uint8_t('\f')] == 64, + "ff must be == 64 in to_base64_url_value"); +static_assert(to_base64_value[uint8_t('+')] == 62, + "+ must be == 62 in to_base64_value"); +static_assert(to_base64_url_value[uint8_t('-')] == 62, + "- must be == 62 in to_base64_url_value"); +static_assert(to_base64_value[uint8_t('/')] == 63, + "/ must be == 63 in to_base64_value"); +static_assert(to_base64_url_value[uint8_t('_')] == 63, + "_ must be == 63 in to_base64_url_value"); +} // namespace base64 +} // namespace tables +} // unnamed namespace +} // namespace simdutf + +#endif // SIMDUTF_BASE64_TABLES_H +/* end file include/simdutf/base64_tables.h */ +/* begin file include/simdutf/scalar/base64.h */ +#ifndef SIMDUTF_BASE64_H +#define SIMDUTF_BASE64_H + +#include +#include +#include +#include + +namespace simdutf { +namespace scalar { +namespace { +namespace base64 { + +// This function is not expected to be fast. Do not use in long loops. +// In most instances you should be using is_ignorable. +template bool is_ascii_white_space(char_type c) { + return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f'; +} + +template simdutf_constexpr23 bool is_eight_byte(char_type c) { + if constexpr (sizeof(char_type) == 1) { + return true; + } + return uint8_t(c) == c; +} + +template +simdutf_constexpr23 bool is_ignorable(char_type c, + simdutf::base64_options options) { + const uint8_t *to_base64 = + (options & base64_default_or_url) + ? tables::base64::to_base64_default_or_url_value + : ((options & base64_url) ? tables::base64::to_base64_url_value + : tables::base64::to_base64_value); + const bool ignore_garbage = + (options == base64_options::base64_url_accept_garbage) || + (options == base64_options::base64_default_accept_garbage) || + (options == base64_options::base64_default_or_url_accept_garbage); + uint8_t code = to_base64[uint8_t(c)]; + if (is_eight_byte(c) && code <= 63) { + return false; + } + if (is_eight_byte(c) && code == 64) { + return true; + } + return ignore_garbage; +} +template +simdutf_constexpr23 bool is_base64(char_type c, + simdutf::base64_options options) { + const uint8_t *to_base64 = + (options & base64_default_or_url) + ? tables::base64::to_base64_default_or_url_value + : ((options & base64_url) ? tables::base64::to_base64_url_value + : tables::base64::to_base64_value); + uint8_t code = to_base64[uint8_t(c)]; + if (is_eight_byte(c) && code <= 63) { + return true; + } + return false; +} + +template +simdutf_constexpr23 bool is_base64_or_padding(char_type c, + simdutf::base64_options options) { + const uint8_t *to_base64 = + (options & base64_default_or_url) + ? tables::base64::to_base64_default_or_url_value + : ((options & base64_url) ? tables::base64::to_base64_url_value + : tables::base64::to_base64_value); + if (c == '=') { + return true; + } + uint8_t code = to_base64[uint8_t(c)]; + if (is_eight_byte(c) && code <= 63) { + return true; + } + return false; +} + +template +bool is_ignorable_or_padding(char_type c, simdutf::base64_options options) { + return is_ignorable(c, options) || c == '='; +} + +struct reduced_input { + size_t equalsigns; // number of padding characters '=', typically 0, 1, 2. + size_t equallocation; // location of the first padding character if any + size_t srclen; // length of the input buffer before padding + size_t full_input_length; // length of the input buffer with padding but + // without ignorable characters +}; + +// find the end of the base64 input buffer +// It returns the number of padding characters, the location of the first +// padding character if any, the length of the input buffer before padding +// and the length of the input buffer with padding. The input buffer is not +// modified. The function assumes that there are at most two padding characters. +template +simdutf_constexpr23 reduced_input find_end(const char_type *src, size_t srclen, + simdutf::base64_options options) { + const uint8_t *to_base64 = + (options & base64_default_or_url) + ? tables::base64::to_base64_default_or_url_value + : ((options & base64_url) ? tables::base64::to_base64_url_value + : tables::base64::to_base64_value); + const bool ignore_garbage = + (options == base64_options::base64_url_accept_garbage) || + (options == base64_options::base64_default_accept_garbage) || + (options == base64_options::base64_default_or_url_accept_garbage); + + size_t equalsigns = 0; + // We intentionally include trailing spaces in the full input length. + // See https://github.com/simdutf/simdutf/issues/824 + size_t full_input_length = srclen; + // skip trailing spaces + while (!ignore_garbage && srclen > 0 && + scalar::base64::is_eight_byte(src[srclen - 1]) && + to_base64[uint8_t(src[srclen - 1])] == 64) { + srclen--; + } + size_t equallocation = + srclen; // location of the first padding character if any + if (ignore_garbage) { + // Technically, we don't need to find the first padding character, we can + // just change our algorithms, but it adds substantial complexity. + auto it = simdutf::find(src, src + srclen, '='); + if (it != src + srclen) { + equallocation = it - src; + equalsigns = 1; + srclen = equallocation; + full_input_length = equallocation + 1; + } + return {equalsigns, equallocation, srclen, full_input_length}; + } + if (!ignore_garbage && srclen > 0 && src[srclen - 1] == '=') { + // This is the last '=' sign. + equallocation = srclen - 1; + srclen--; + equalsigns = 1; + // skip trailing spaces + while (srclen > 0 && scalar::base64::is_eight_byte(src[srclen - 1]) && + to_base64[uint8_t(src[srclen - 1])] == 64) { + srclen--; + } + if (srclen > 0 && src[srclen - 1] == '=') { + // This is the second '=' sign. + equallocation = srclen - 1; + srclen--; + equalsigns = 2; + } + } + return {equalsigns, equallocation, srclen, full_input_length}; +} + +// Returns true upon success. The destination buffer must be large enough. +// This functions assumes that the padding (=) has been removed. +// if check_capacity is true, it will check that the destination buffer is +// large enough. If it is not, it will return OUTPUT_BUFFER_TOO_SMALL. +template +simdutf_constexpr23 full_result base64_tail_decode_impl( + char *dst, size_t outlen, const char_type *src, size_t length, + size_t padding_characters, // number of padding characters + // '=', typically 0, 1, 2. + base64_options options, last_chunk_handling_options last_chunk_options) { + char *dstend = dst + outlen; + (void)dstend; + // This looks like 10 branches, but we expect the compiler to resolve this to + // two branches (easily predicted): + const uint8_t *to_base64 = + (options & base64_default_or_url) + ? tables::base64::to_base64_default_or_url_value + : ((options & base64_url) ? tables::base64::to_base64_url_value + : tables::base64::to_base64_value); + const uint32_t *d0 = + (options & base64_default_or_url) + ? tables::base64::base64_default_or_url::d0 + : ((options & base64_url) ? tables::base64::base64_url::d0 + : tables::base64::base64_default::d0); + const uint32_t *d1 = + (options & base64_default_or_url) + ? tables::base64::base64_default_or_url::d1 + : ((options & base64_url) ? tables::base64::base64_url::d1 + : tables::base64::base64_default::d1); + const uint32_t *d2 = + (options & base64_default_or_url) + ? tables::base64::base64_default_or_url::d2 + : ((options & base64_url) ? tables::base64::base64_url::d2 + : tables::base64::base64_default::d2); + const uint32_t *d3 = + (options & base64_default_or_url) + ? tables::base64::base64_default_or_url::d3 + : ((options & base64_url) ? tables::base64::base64_url::d3 + : tables::base64::base64_default::d3); + const bool ignore_garbage = + (options == base64_options::base64_url_accept_garbage) || + (options == base64_options::base64_default_accept_garbage) || + (options == base64_options::base64_default_or_url_accept_garbage); + + const char_type *srcend = src + length; + const char_type *srcinit = src; + const char *dstinit = dst; + + uint32_t x; + size_t idx; + uint8_t buffer[4]; + while (true) { + while (srcend - src >= 4 && is_eight_byte(src[0]) && + is_eight_byte(src[1]) && is_eight_byte(src[2]) && + is_eight_byte(src[3]) && + (x = d0[uint8_t(src[0])] | d1[uint8_t(src[1])] | + d2[uint8_t(src[2])] | d3[uint8_t(src[3])]) < 0x01FFFFFF) { + if (check_capacity && dstend - dst < 3) { + return {OUTPUT_BUFFER_TOO_SMALL, size_t(src - srcinit), + size_t(dst - dstinit)}; + } + *dst++ = static_cast(x & 0xFF); + *dst++ = static_cast((x >> 8) & 0xFF); + *dst++ = static_cast((x >> 16) & 0xFF); + src += 4; + } + const char_type *srccur = src; + idx = 0; + // we need at least four characters. +#ifdef __clang__ + // If possible, we read four characters at a time. (It is an optimization.) + if (ignore_garbage && src + 4 <= srcend) { + char_type c0 = src[0]; + char_type c1 = src[1]; + char_type c2 = src[2]; + char_type c3 = src[3]; + + uint8_t code0 = to_base64[uint8_t(c0)]; + uint8_t code1 = to_base64[uint8_t(c1)]; + uint8_t code2 = to_base64[uint8_t(c2)]; + uint8_t code3 = to_base64[uint8_t(c3)]; + + buffer[idx] = code0; + idx += (is_eight_byte(c0) && code0 <= 63); + buffer[idx] = code1; + idx += (is_eight_byte(c1) && code1 <= 63); + buffer[idx] = code2; + idx += (is_eight_byte(c2) && code2 <= 63); + buffer[idx] = code3; + idx += (is_eight_byte(c3) && code3 <= 63); + src += 4; + } +#endif + while ((idx < 4) && (src < srcend)) { + char_type c = *src; + + uint8_t code = to_base64[uint8_t(c)]; + buffer[idx] = uint8_t(code); + if (is_eight_byte(c) && code <= 63) { + idx++; + } else if (!ignore_garbage && + (code > 64 || !scalar::base64::is_eight_byte(c))) { + return {INVALID_BASE64_CHARACTER, size_t(src - srcinit), + size_t(dst - dstinit)}; + } else { + // We have a space or a newline or garbage. We ignore it. + } + src++; + } + if (idx != 4) { + simdutf_log_assert(idx < 4, "idx should be less than 4"); + // We never should have that the number of base64 characters + the + // number of padding characters is more than 4. + if (!ignore_garbage && (idx + padding_characters > 4)) { + return {INVALID_BASE64_CHARACTER, size_t(src - srcinit), + size_t(dst - dstinit), true}; + } + + // The idea here is that in loose mode, + // if there is padding at all, it must be used + // to form 4-wise chunk. However, in loose mode, + // we do accept no padding at all. + if (!ignore_garbage && + last_chunk_options == last_chunk_handling_options::loose && + (idx >= 2) && padding_characters > 0 && + ((idx + padding_characters) & 3) != 0) { + return {INVALID_BASE64_CHARACTER, size_t(src - srcinit), + size_t(dst - dstinit), true}; + } else + + // The idea here is that in strict mode, we do not want to accept + // incomplete base64 chunks. So if the chunk was otherwise valid, we + // return BASE64_INPUT_REMAINDER. + if (!ignore_garbage && + last_chunk_options == last_chunk_handling_options::strict && + (idx >= 2) && ((idx + padding_characters) & 3) != 0) { + // The partial chunk was at src - idx + return {BASE64_INPUT_REMAINDER, size_t(src - srcinit), + size_t(dst - dstinit), true}; + } else + // If there is a partial chunk with insufficient padding, with + // stop_before_partial, we need to just ignore it. In "only full" + // mode, skip the minute there are padding characters. + if ((last_chunk_options == + last_chunk_handling_options::stop_before_partial && + (padding_characters + idx < 4) && (idx != 0) && + (idx >= 2 || padding_characters == 0)) || + (last_chunk_options == + last_chunk_handling_options::only_full_chunks && + (idx >= 2 || padding_characters == 0))) { + // partial means that we are *not* going to consume the read + // characters. We need to rewind the src pointer. + src = srccur; + return {SUCCESS, size_t(src - srcinit), size_t(dst - dstinit)}; + } else { + if (idx == 2) { + uint32_t triple = (uint32_t(buffer[0]) << 3 * 6) + + (uint32_t(buffer[1]) << 2 * 6); + if (!ignore_garbage && + (last_chunk_options == last_chunk_handling_options::strict) && + (triple & 0xffff)) { + return {BASE64_EXTRA_BITS, size_t(src - srcinit), + size_t(dst - dstinit)}; + } + if (check_capacity && dstend - dst < 1) { + return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit), + size_t(dst - dstinit)}; + } + *dst++ = static_cast((triple >> 16) & 0xFF); + } else if (idx == 3) { + uint32_t triple = (uint32_t(buffer[0]) << 3 * 6) + + (uint32_t(buffer[1]) << 2 * 6) + + (uint32_t(buffer[2]) << 1 * 6); + if (!ignore_garbage && + (last_chunk_options == last_chunk_handling_options::strict) && + (triple & 0xff)) { + return {BASE64_EXTRA_BITS, size_t(src - srcinit), + size_t(dst - dstinit)}; + } + if (check_capacity && dstend - dst < 2) { + return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit), + size_t(dst - dstinit)}; + } + *dst++ = static_cast((triple >> 16) & 0xFF); + *dst++ = static_cast((triple >> 8) & 0xFF); + } else if (!ignore_garbage && idx == 1 && + (!is_partial(last_chunk_options) || + (is_partial(last_chunk_options) && + padding_characters > 0))) { + return {BASE64_INPUT_REMAINDER, size_t(src - srcinit), + size_t(dst - dstinit)}; + } else if (!ignore_garbage && idx == 0 && padding_characters > 0) { + return {INVALID_BASE64_CHARACTER, size_t(src - srcinit), + size_t(dst - dstinit), true}; + } + return {SUCCESS, size_t(src - srcinit), size_t(dst - dstinit)}; + } + } + if (check_capacity && dstend - dst < 3) { + return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit), + size_t(dst - dstinit)}; + } + uint32_t triple = + (uint32_t(buffer[0]) << 3 * 6) + (uint32_t(buffer[1]) << 2 * 6) + + (uint32_t(buffer[2]) << 1 * 6) + (uint32_t(buffer[3]) << 0 * 6); + *dst++ = static_cast((triple >> 16) & 0xFF); + *dst++ = static_cast((triple >> 8) & 0xFF); + *dst++ = static_cast(triple & 0xFF); + } +} + +template +simdutf_constexpr23 full_result base64_tail_decode( + char *dst, const char_type *src, size_t length, + size_t padding_characters, // number of padding characters + // '=', typically 0, 1, 2. + base64_options options, last_chunk_handling_options last_chunk_options) { + return base64_tail_decode_impl(dst, 0, src, length, padding_characters, + options, last_chunk_options); +} + +// like base64_tail_decode, but it will not write past the end of the output +// buffer. The outlen parameter is modified to reflect the number of bytes +// written. This functions assumes that the padding (=) has been removed. +// +template +simdutf_constexpr23 full_result base64_tail_decode_safe( + char *dst, size_t outlen, const char_type *src, size_t length, + size_t padding_characters, // number of padding characters + // '=', typically 0, 1, 2. + base64_options options, last_chunk_handling_options last_chunk_options) { + return base64_tail_decode_impl(dst, outlen, src, length, + padding_characters, options, + last_chunk_options); +} + +inline simdutf_constexpr23 full_result +patch_tail_result(full_result r, size_t previous_input, size_t previous_output, + size_t equallocation, size_t full_input_length, + last_chunk_handling_options last_chunk_options) { + r.input_count += previous_input; + r.output_count += previous_output; + if (r.padding_error) { + r.input_count = equallocation; + } + + if (r.error == error_code::SUCCESS) { + if (!is_partial(last_chunk_options)) { + // A success when we are not in stop_before_partial mode. + // means that we have consumed the whole input buffer. + r.input_count = full_input_length; + } else if (r.output_count % 3 != 0) { + r.input_count = full_input_length; + } + } + return r; +} + +// Returns the number of bytes written. The destination buffer must be large +// enough. It will add padding (=) if needed. +template +simdutf_constexpr23 size_t tail_encode_base64_impl( + char *dst, const char *src, size_t srclen, base64_options options, + size_t line_length = simdutf::default_line_length, size_t line_offset = 0) { + if constexpr (use_lines) { + // sanitize line_length and starting_line_offset. + // line_length must be greater than 3. + if (line_length < 4) { + line_length = 4; + } + simdutf_log_assert(line_offset <= line_length, + "line_offset should be less than line_length"); + } + // By default, we use padding if we are not using the URL variant. + // This is check with ((options & base64_url) == 0) which returns true if we + // are not using the URL variant. However, we also allow 'inversion' of the + // convention with the base64_reverse_padding option. If the + // base64_reverse_padding option is set, we use padding if we are using the + // URL variant, and we omit it if we are not using the URL variant. This is + // checked with + // ((options & base64_reverse_padding) == base64_reverse_padding). + bool use_padding = + ((options & base64_url) == 0) ^ + ((options & base64_reverse_padding) == base64_reverse_padding); + // This looks like 3 branches, but we expect the compiler to resolve this to + // a single branch: + const char *e0 = (options & base64_url) ? tables::base64::base64_url::e0 + : tables::base64::base64_default::e0; + const char *e1 = (options & base64_url) ? tables::base64::base64_url::e1 + : tables::base64::base64_default::e1; + const char *e2 = (options & base64_url) ? tables::base64::base64_url::e2 + : tables::base64::base64_default::e2; + char *out = dst; + size_t i = 0; + uint8_t t1, t2, t3; + for (; i + 2 < srclen; i += 3) { + t1 = uint8_t(src[i]); + t2 = uint8_t(src[i + 1]); + t3 = uint8_t(src[i + 2]); + if constexpr (use_lines) { + if (line_offset + 3 >= line_length) { + if (line_offset == line_length) { + *out++ = '\n'; + *out++ = e0[t1]; + *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)]; + *out++ = e2[t3]; + line_offset = 4; + } else if (line_offset + 1 == line_length) { + *out++ = e0[t1]; + *out++ = '\n'; + *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)]; + *out++ = e2[t3]; + line_offset = 3; + } else if (line_offset + 2 == line_length) { + *out++ = e0[t1]; + *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *out++ = '\n'; + *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)]; + *out++ = e2[t3]; + line_offset = 2; + } else if (line_offset + 3 == line_length) { + *out++ = e0[t1]; + *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)]; + *out++ = '\n'; + *out++ = e2[t3]; + line_offset = 1; + } + } else { + *out++ = e0[t1]; + *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)]; + *out++ = e2[t3]; + line_offset += 4; + } + } else { + *out++ = e0[t1]; + *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)]; + *out++ = e2[t3]; + } + } + switch (srclen - i) { + case 0: + break; + case 1: + t1 = uint8_t(src[i]); + if constexpr (use_lines) { + if (use_padding) { + if (line_offset + 3 >= line_length) { + if (line_offset == line_length) { + *out++ = '\n'; + *out++ = e0[t1]; + *out++ = e1[(t1 & 0x03) << 4]; + *out++ = '='; + *out++ = '='; + } else if (line_offset + 1 == line_length) { + *out++ = e0[t1]; + *out++ = '\n'; + *out++ = e1[(t1 & 0x03) << 4]; + *out++ = '='; + *out++ = '='; + } else if (line_offset + 2 == line_length) { + *out++ = e0[t1]; + *out++ = e1[(t1 & 0x03) << 4]; + *out++ = '\n'; + *out++ = '='; + *out++ = '='; + } else if (line_offset + 3 == line_length) { + *out++ = e0[t1]; + *out++ = e1[(t1 & 0x03) << 4]; + *out++ = '='; + *out++ = '\n'; + *out++ = '='; + } + } else { + *out++ = e0[t1]; + *out++ = e1[(t1 & 0x03) << 4]; + *out++ = '='; + *out++ = '='; + } + } else { + if (line_offset + 2 >= line_length) { + if (line_offset == line_length) { + *out++ = '\n'; + *out++ = e0[uint8_t(src[i])]; + *out++ = e1[(uint8_t(src[i]) & 0x03) << 4]; + } else if (line_offset + 1 == line_length) { + *out++ = e0[uint8_t(src[i])]; + *out++ = '\n'; + *out++ = e1[(uint8_t(src[i]) & 0x03) << 4]; + } else { + *out++ = e0[uint8_t(src[i])]; + *out++ = e1[(uint8_t(src[i]) & 0x03) << 4]; + // *out++ = '\n'; ==> no newline at the end of the output + } + } else { + *out++ = e0[uint8_t(src[i])]; + *out++ = e1[(uint8_t(src[i]) & 0x03) << 4]; + } + } + } else { + *out++ = e0[t1]; + *out++ = e1[(t1 & 0x03) << 4]; + if (use_padding) { + *out++ = '='; + *out++ = '='; + } + } + break; + default: /* case 2 */ + t1 = uint8_t(src[i]); + t2 = uint8_t(src[i + 1]); + if constexpr (use_lines) { + if (use_padding) { + if (line_offset + 3 >= line_length) { + if (line_offset == line_length) { + *out++ = '\n'; + *out++ = e0[t1]; + *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *out++ = e2[(t2 & 0x0F) << 2]; + *out++ = '='; + } else if (line_offset + 1 == line_length) { + *out++ = e0[t1]; + *out++ = '\n'; + *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *out++ = e2[(t2 & 0x0F) << 2]; + *out++ = '='; + } else if (line_offset + 2 == line_length) { + *out++ = e0[t1]; + *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *out++ = '\n'; + *out++ = e2[(t2 & 0x0F) << 2]; + *out++ = '='; + } else if (line_offset + 3 == line_length) { + *out++ = e0[t1]; + *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *out++ = e2[(t2 & 0x0F) << 2]; + *out++ = '\n'; + *out++ = '='; + } + } else { + *out++ = e0[t1]; + *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *out++ = e2[(t2 & 0x0F) << 2]; + *out++ = '='; + } + } else { + if (line_offset + 3 >= line_length) { + if (line_offset == line_length) { + *out++ = '\n'; + *out++ = e0[t1]; + *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *out++ = e2[(t2 & 0x0F) << 2]; + } else if (line_offset + 1 == line_length) { + *out++ = e0[t1]; + *out++ = '\n'; + *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *out++ = e2[(t2 & 0x0F) << 2]; + } else if (line_offset + 2 == line_length) { + *out++ = e0[t1]; + *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *out++ = '\n'; + *out++ = e2[(t2 & 0x0F) << 2]; + } else { + *out++ = e0[t1]; + *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *out++ = e2[(t2 & 0x0F) << 2]; + // *out++ = '\n'; ==> no newline at the end of the output + } + } else { + *out++ = e0[t1]; + *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *out++ = e2[(t2 & 0x0F) << 2]; + } + } + } else { + *out++ = e0[t1]; + *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)]; + *out++ = e2[(t2 & 0x0F) << 2]; + if (use_padding) { + *out++ = '='; + } + } + } + return (size_t)(out - dst); +} + +// Returns the number of bytes written. The destination buffer must be large +// enough. It will add padding (=) if needed. +inline simdutf_constexpr23 size_t tail_encode_base64(char *dst, const char *src, + size_t srclen, + base64_options options) { + return tail_encode_base64_impl(dst, src, srclen, options); +} + +template +simdutf_warn_unused simdutf_constexpr23 size_t +maximal_binary_length_from_base64(InputPtr input, size_t length) noexcept { + // We process the padding characters ('=') at the end to make sure + // that we return an exact result when the input has no ignorable characters + // (e.g., spaces). + size_t padding = 0; + if (length > 0) { + if (input[length - 1] == '=') { + padding++; + if (length > 1 && input[length - 2] == '=') { + padding++; + } + } + } + // The input is not otherwise processed for ignorable characters or + // validation, so that the function runs in constant time (very fast). In + // practice, base64 inputs without ignorable characters are common and the + // common case are line separated inputs with relatively long lines (e.g., 76 + // characters) which leads this function to a slight (1%) overestimation of + // the output size. + // + // Of course, some inputs might contain an arbitrary number of spaces or + // newlines, which would make this function return a very pessimistic output + // size but systems that produce base64 outputs typically do not do that and + // if they do, they do not care much about minimizing memory usage. + // + // In specialized applications, users may know that their input is line + // separated, which can be checked very quickly by by iterating (e.g., over 76 + // character chunks, looking for the linefeed characters only). We could + // provide a specialized function for that, but it is not clear that the added + // complexity is worth it for us. + // + size_t actual_length = length - padding; + if (actual_length % 4 <= 1) { + return actual_length / 4 * 3; + } + // if we have a valid input, then the remainder must be 2 or 3 adding one or + // two extra bytes. + return actual_length / 4 * 3 + (actual_length % 4) - 1; +} + +// This function computes the binary length by iterating through the input +// and counting non-whitespace characters (excluding padding characters). +// We use a simple check (c > ' ') which is easy to parallelize and matches +// SIMD behavior. Only the last few characters are checked for padding '='. +template +simdutf_warn_unused simdutf_constexpr23 size_t +binary_length_from_base64(const char_type *input, size_t length) noexcept { + // Count non-whitespace characters (c > ' ') with loop unrolling + size_t count = 0; + for (size_t i = 0; i < length; i++) { + count += (input[i] > ' '); + } + + // Check for padding '=' at the end (at most 2 padding characters) + // Scan backwards, skipping whitespace, to find padding + size_t padding = 0; + size_t pos = length; + // Skip trailing whitespace + while (pos > 0 && padding < 2) { + char_type c = input[--pos]; + if (c == '=') { + padding++; + } else if (c > ' ') { + break; + } + } + return ((count - padding) * 3) / 4; +} + +template +simdutf_warn_unused simdutf_constexpr23 full_result +base64_to_binary_details_impl( + const char_type *input, size_t length, char *output, base64_options options, + last_chunk_handling_options last_chunk_options) noexcept { + const bool ignore_garbage = + (options == base64_options::base64_url_accept_garbage) || + (options == base64_options::base64_default_accept_garbage) || + (options == base64_options::base64_default_or_url_accept_garbage); + auto ri = simdutf::scalar::base64::find_end(input, length, options); + size_t equallocation = ri.equallocation; + size_t equalsigns = ri.equalsigns; + length = ri.srclen; + size_t full_input_length = ri.full_input_length; + if (length == 0) { + if (!ignore_garbage && equalsigns > 0) { + return {INVALID_BASE64_CHARACTER, equallocation, 0, true}; + } + return {SUCCESS, full_input_length, 0}; + } + full_result r = scalar::base64::base64_tail_decode( + output, input, length, equalsigns, options, last_chunk_options); + r = scalar::base64::patch_tail_result(r, 0, 0, equallocation, + full_input_length, last_chunk_options); + if (!is_partial(last_chunk_options) && r.error == error_code::SUCCESS && + equalsigns > 0 && !ignore_garbage) { + // additional checks + if ((r.output_count % 3 == 0) || + ((r.output_count % 3) + 1 + equalsigns != 4)) { + return {INVALID_BASE64_CHARACTER, equallocation, r.output_count, true}; + } + } + // When is_partial(last_chunk_options) is true, we must either end with + // the end of the stream (beyond whitespace) or right after a non-ignorable + // character or at the very beginning of the stream. + // See https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64 + if (is_partial(last_chunk_options) && r.error == error_code::SUCCESS && + r.input_count < full_input_length) { + // First check if we can extend the input to the end of the stream + while (r.input_count < full_input_length && + base64_ignorable(*(input + r.input_count), options)) { + r.input_count++; + } + // If we are still not at the end of the stream, then we must backtrack + // to the last non-ignorable character. + if (r.input_count < full_input_length) { + while (r.input_count > 0 && + base64_ignorable(*(input + r.input_count - 1), options)) { + r.input_count--; + } + } + } + return r; +} + +template +simdutf_constexpr23 simdutf_warn_unused full_result +base64_to_binary_details_safe_impl( + const char_type *input, size_t length, char *output, size_t outlen, + base64_options options, + last_chunk_handling_options last_chunk_options) noexcept { + const bool ignore_garbage = + (options == base64_options::base64_url_accept_garbage) || + (options == base64_options::base64_default_accept_garbage) || + (options == base64_options::base64_default_or_url_accept_garbage); + auto ri = simdutf::scalar::base64::find_end(input, length, options); + size_t equallocation = ri.equallocation; + size_t equalsigns = ri.equalsigns; + length = ri.srclen; + size_t full_input_length = ri.full_input_length; + if (length == 0) { + if (!ignore_garbage && equalsigns > 0) { + return {INVALID_BASE64_CHARACTER, equallocation, 0}; + } + return {SUCCESS, full_input_length, 0}; + } + full_result r = scalar::base64::base64_tail_decode_safe( + output, outlen, input, length, equalsigns, options, last_chunk_options); + r = scalar::base64::patch_tail_result(r, 0, 0, equallocation, + full_input_length, last_chunk_options); + if (!is_partial(last_chunk_options) && r.error == error_code::SUCCESS && + equalsigns > 0 && !ignore_garbage) { + // additional checks + if ((r.output_count % 3 == 0) || + ((r.output_count % 3) + 1 + equalsigns != 4)) { + return {INVALID_BASE64_CHARACTER, equallocation, r.output_count}; + } + } + + // When is_partial(last_chunk_options) is true, we must either end with + // the end of the stream (beyond whitespace) or right after a non-ignorable + // character or at the very beginning of the stream. + // See https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64 + if (is_partial(last_chunk_options) && r.error == error_code::SUCCESS && + r.input_count < full_input_length) { + // First check if we can extend the input to the end of the stream + while (r.input_count < full_input_length && + base64_ignorable(*(input + r.input_count), options)) { + r.input_count++; + } + // If we are still not at the end of the stream, then we must backtrack + // to the last non-ignorable character. + if (r.input_count < full_input_length) { + while (r.input_count > 0 && + base64_ignorable(*(input + r.input_count - 1), options)) { + r.input_count--; + } + } + } + return r; +} + +simdutf_warn_unused simdutf_constexpr23 size_t +base64_length_from_binary(size_t length, base64_options options) noexcept { + // By default, we use padding if we are not using the URL variant. + // This is check with ((options & base64_url) == 0) which returns true if we + // are not using the URL variant. However, we also allow 'inversion' of the + // convention with the base64_reverse_padding option. If the + // base64_reverse_padding option is set, we use padding if we are using the + // URL variant, and we omit it if we are not using the URL variant. This is + // checked with + // ((options & base64_reverse_padding) == base64_reverse_padding). + bool use_padding = + ((options & base64_url) == 0) ^ + ((options & base64_reverse_padding) == base64_reverse_padding); + if (!use_padding) { + return length / 3 * 4 + ((length % 3) ? (length % 3) + 1 : 0); + } + return (length + 2) / 3 * + 4; // We use padding to make the length a multiple of 4. +} + +simdutf_warn_unused simdutf_constexpr23 size_t +base64_length_from_binary_with_lines(size_t length, base64_options options, + size_t line_length) noexcept { + if (length == 0) { + return 0; + } + size_t base64_length = + scalar::base64::base64_length_from_binary(length, options); + if (line_length < 4) { + line_length = 4; + } + size_t lines = + (base64_length + line_length - 1) / line_length; // number of lines + return base64_length + lines - 1; +} + +// Return the length of the prefix that contains count base64 characters. +// Thus, if count is 3, the function returns the length of the prefix +// that contains 3 base64 characters. +// The function returns (size_t)-1 if there is not enough base64 characters in +// the input. +template +simdutf_warn_unused size_t prefix_length(size_t count, + simdutf::base64_options options, + const char_type *input, + size_t length) noexcept { + size_t i = 0; + while (i < length && is_ignorable(input[i], options)) { + i++; + } + if (count == 0) { + return i; // duh! + } + for (; i < length; i++) { + if (is_ignorable(input[i], options)) { + continue; + } + // We have a base64 character or a padding character. + count--; + if (count == 0) { + return i + 1; + } + } + simdutf_log_assert(false, "You never get here"); + + return -1; // should never happen +} + +} // namespace base64 +} // unnamed namespace +} // namespace scalar +} // namespace simdutf + +#endif +/* end file include/simdutf/scalar/base64.h */ + +namespace simdutf { + +inline std::string_view to_string(base64_options options) { + switch (options) { + case base64_default: + return "base64_default"; + case base64_url: + return "base64_url"; + case base64_reverse_padding: + return "base64_reverse_padding"; + case base64_url_with_padding: + return "base64_url_with_padding"; + case base64_default_accept_garbage: + return "base64_default_accept_garbage"; + case base64_url_accept_garbage: + return "base64_url_accept_garbage"; + case base64_default_or_url: + return "base64_default_or_url"; + case base64_default_or_url_accept_garbage: + return "base64_default_or_url_accept_garbage"; + } + return ""; +} + +inline std::string_view to_string(last_chunk_handling_options options) { + switch (options) { + case loose: + return "loose"; + case strict: + return "strict"; + case stop_before_partial: + return "stop_before_partial"; + case only_full_chunks: + return "only_full_chunks"; + } + return ""; +} + +/** + * Provide the maximal binary length in bytes given the base64 input. + * As long as the input does not contain ignorable characters (e.g., ASCII + * spaces or linefeed characters), the result is exact. In particular, the + * function checks for padding characters. + * + * The function is fast (constant time). It checks up to two characters at + * the end of the string. The input is not otherwise validated or read. + * + * @param input the base64 input to process + * @param length the length of the base64 input in bytes + * @return maximum number of binary bytes + */ +simdutf_warn_unused size_t +maximal_binary_length_from_base64(const char *input, size_t length) noexcept; + #if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t +maximal_binary_length_from_base64( + const detail::input_span_of_byte_like auto &input) noexcept { + #if SIMDUTF_CPLUSPLUS23 + if consteval { + return scalar::base64::maximal_binary_length_from_base64( + detail::constexpr_cast_ptr(input.data()), input.size()); + } else + #endif + { + return maximal_binary_length_from_base64( + reinterpret_cast(input.data()), input.size()); + } +} + #endif // SIMDUTF_SPAN + +/** + * Provide the maximal binary length in bytes given the base64 input. + * As long as the input does not contain ignorable characters (e.g., ASCII + * spaces or linefeed characters), the result is exact. In particular, the + * function checks for padding characters. + * + * The function is fast (constant time). It checks up to two characters at + * the end of the string. The input is not otherwise validated or read. + * + * @param input the base64 input to process, in ASCII stored as 16-bit + * units + * @param length the length of the base64 input in 16-bit units + * @return maximal number of binary bytes + */ +simdutf_warn_unused size_t maximal_binary_length_from_base64( + const char16_t *input, size_t length) noexcept; + #if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t +maximal_binary_length_from_base64(std::span input) noexcept { + #if SIMDUTF_CPLUSPLUS23 + if consteval { + return scalar::base64::maximal_binary_length_from_base64(input.data(), + input.size()); + } else + #endif + { + return maximal_binary_length_from_base64(input.data(), input.size()); + } +} + #endif // SIMDUTF_SPAN + +/** + * Compute the binary length from a base64 input. + * This function is useful for base64 inputs that may contain ASCII whitespaces + * (such as line breaks). For such inputs, the result is exact, and for any + * inputs the result can be used to size the output buffer passed to + * `base64_to_binary`. + * + * The function ignores whitespace and does not require padding characters + * ('='). + * + * @param input the base64 input to process + * @param length the length of the base64 input in bytes + * @return number of binary bytes + */ +simdutf_warn_unused size_t binary_length_from_base64(const char *input, + size_t length) noexcept; + #if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t +binary_length_from_base64( + const detail::input_span_of_byte_like auto &input) noexcept { + #if SIMDUTF_CPLUSPLUS23 + if consteval { + return scalar::base64::binary_length_from_base64(input.data(), + input.size()); + } else + #endif + { + return binary_length_from_base64( + reinterpret_cast(input.data()), input.size()); + } +} + #endif // SIMDUTF_SPAN + +/** + * Compute the binary length from a base64 input. + * This function is useful for base64 inputs that may contain ASCII whitespaces + * (such as line breaks). For such inputs, the result is exact, and for any + * inputs the result can be used to size the output buffer passed to + * `base64_to_binary`. + * + * The function ignores whitespace and does not require padding characters + * ('='). + * + * @param input the base64 input to process, in ASCII stored as 16-bit + * units + * @param length the length of the base64 input in 16-bit units + * @return number of binary bytes + */ +simdutf_warn_unused size_t binary_length_from_base64(const char16_t *input, + size_t length) noexcept; + #if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t +binary_length_from_base64(std::span input) noexcept { + #if SIMDUTF_CPLUSPLUS23 + if consteval { + return scalar::base64::binary_length_from_base64(input.data(), + input.size()); + } else + #endif + { + return binary_length_from_base64(input.data(), input.size()); + } +} + #endif // SIMDUTF_SPAN + +/** + * Convert a base64 input to a binary output. + * + * This function follows the WHATWG forgiving-base64 format, which means that it + * will ignore any ASCII spaces in the input. You may provide a padded input + * (with one or two equal signs at the end) or an unpadded input (without any + * equal signs at the end). + * + * See https://infra.spec.whatwg.org/#forgiving-base64-decode * * This function will fail in case of invalid input. When last_chunk_options = * loose, there are two possible reasons for failure: the input contains a @@ -1388,15 +7167,25 @@ simdutf_warn_unused result base64_to_binary( base64_options options = base64_default, last_chunk_handling_options last_chunk_options = loose) noexcept; #if SIMDUTF_SPAN -simdutf_really_inline simdutf_warn_unused result base64_to_binary( +simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result +base64_to_binary( const detail::input_span_of_byte_like auto &input, detail::output_span_of_byte_like auto &&binary_output, base64_options options = base64_default, last_chunk_handling_options last_chunk_options = loose) noexcept { - return base64_to_binary(reinterpret_cast(input.data()), - input.size(), - reinterpret_cast(binary_output.data()), - options, last_chunk_options); + #if SIMDUTF_CPLUSPLUS23 + if consteval { + return scalar::base64::base64_to_binary_details_impl( + input.data(), input.size(), binary_output.data(), options, + last_chunk_options); + } else + #endif + { + return base64_to_binary(reinterpret_cast(input.data()), + input.size(), + reinterpret_cast(binary_output.data()), + options, last_chunk_options); + } } #endif // SIMDUTF_SPAN @@ -1406,8 +7195,27 @@ simdutf_really_inline simdutf_warn_unused result base64_to_binary( * @param length the length of the input in bytes * @return number of base64 bytes */ -simdutf_warn_unused size_t base64_length_from_binary( - size_t length, base64_options options = base64_default) noexcept; +inline simdutf_warn_unused simdutf_constexpr23 size_t base64_length_from_binary( + size_t length, base64_options options = base64_default) noexcept { + return scalar::base64::base64_length_from_binary(length, options); +} + +/** + * Provide the base64 length in bytes given the length of a binary input, + * taking into account line breaks. + * + * @param length the length of the input in bytes + * @param line_length the length of lines, must be at least 4 (otherwise it is + * interpreted as 4), + * @return number of base64 bytes + */ +inline simdutf_warn_unused simdutf_constexpr23 size_t +base64_length_from_binary_with_lines( + size_t length, base64_options options = base64_default, + size_t line_length = default_line_length) noexcept { + return scalar::base64::base64_length_from_binary_with_lines(length, options, + line_length); +} /** * Convert a binary input to a base64 output. @@ -1433,13 +7241,70 @@ simdutf_warn_unused size_t base64_length_from_binary( size_t binary_to_base64(const char *input, size_t length, char *output, base64_options options = base64_default) noexcept; #if SIMDUTF_SPAN -simdutf_really_inline simdutf_warn_unused size_t +simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t binary_to_base64(const detail::input_span_of_byte_like auto &input, detail::output_span_of_byte_like auto &&binary_output, base64_options options = base64_default) noexcept { - return binary_to_base64( - reinterpret_cast(input.data()), input.size(), - reinterpret_cast(binary_output.data()), options); + #if SIMDUTF_CPLUSPLUS23 + if consteval { + return scalar::base64::tail_encode_base64( + binary_output.data(), input.data(), input.size(), options); + } else + #endif + { + return binary_to_base64( + reinterpret_cast(input.data()), input.size(), + reinterpret_cast(binary_output.data()), options); + } +} + #endif // SIMDUTF_SPAN + +/** + * Convert a binary input to a base64 output with line breaks. + * + * The default option (simdutf::base64_default) uses the characters `+` and `/` + * as part of its alphabet. Further, it adds padding (`=`) at the end of the + * output to ensure that the output length is a multiple of four. + * + * The URL option (simdutf::base64_url) uses the characters `-` and `_` as part + * of its alphabet. No padding is added at the end of the output. + * + * This function always succeeds. + * + * @param input the binary to process + * @param length the length of the input in bytes + * @param output the pointer to a buffer that can hold the conversion + * result (should be at least base64_length_from_binary_with_lines(length, + * options, line_length) bytes long) + * @param line_length the length of lines, must be at least 4 (otherwise it is + * interpreted as 4), + * @param options the base64 options to use, can be base64_default or + * base64_url, is base64_default by default. + * @return number of written bytes, will be equal to + * base64_length_from_binary_with_lines(length, options) + */ +size_t +binary_to_base64_with_lines(const char *input, size_t length, char *output, + size_t line_length = simdutf::default_line_length, + base64_options options = base64_default) noexcept; + #if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t +binary_to_base64_with_lines( + const detail::input_span_of_byte_like auto &input, + detail::output_span_of_byte_like auto &&binary_output, + size_t line_length = simdutf::default_line_length, + base64_options options = base64_default) noexcept { + #if SIMDUTF_CPLUSPLUS23 + if consteval { + return scalar::base64::tail_encode_base64_impl( + binary_output.data(), input.data(), input.size(), options, line_length); + } else + #endif + { + return binary_to_base64_with_lines( + reinterpret_cast(input.data()), input.size(), + reinterpret_cast(binary_output.data()), line_length, options); + } } #endif // SIMDUTF_SPAN @@ -1562,32 +7427,197 @@ base64_to_binary(const char16_t *input, size_t length, char *output, last_chunk_handling_options last_chunk_options = last_chunk_handling_options::loose) noexcept; #if SIMDUTF_SPAN -simdutf_really_inline simdutf_warn_unused result base64_to_binary( +simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result +base64_to_binary( + std::span input, + detail::output_span_of_byte_like auto &&binary_output, + base64_options options = base64_default, + last_chunk_handling_options last_chunk_options = loose) noexcept { + #if SIMDUTF_CPLUSPLUS23 + if consteval { + return scalar::base64::base64_to_binary_details_impl( + input.data(), input.size(), binary_output.data(), options, + last_chunk_options); + } else + #endif + { + return base64_to_binary(input.data(), input.size(), + reinterpret_cast(binary_output.data()), + options, last_chunk_options); + } +} + #endif // SIMDUTF_SPAN + +/** + * Convert a base64 input to a binary output while returning more details + * than base64_to_binary. + * + * This function follows the WHATWG forgiving-base64 format, which means that it + * will ignore any ASCII spaces in the input. You may provide a padded input + * (with one or two equal signs at the end) or an unpadded input (without any + * equal signs at the end). + * + * See https://infra.spec.whatwg.org/#forgiving-base64-decode + * + * Unlike base64_to_binary, this function returns a full_result with both + * input_count and output_count, so you always know how much input was consumed + * and how much output was written. There are three cases where the input may + * not be fully consumed: + * + * 1. stop_before_partial: When last_chunk_options is set to + * stop_before_partial, any incomplete 4-character group at the end of the + * input is left unconsumed. This is useful for streaming/chunked decoding + * where you can carry over the unconsumed input to the next chunk. + * + * 2. INVALID_BASE64_CHARACTER: The input contains a character that is not a + * valid base64 character. In this case, input_count indicates where the + * invalid character was found. + * + * 3. BASE64_INPUT_REMAINDER: When last_chunk_options is loose, the input + * contains a number of base64 characters that, when divided by 4, leaves + * a single remainder character (which cannot encode any bytes). + * + * You should call this function with a buffer that is at least + * maximal_binary_length_from_base64(input, length) bytes long. If you fail to + * provide that much space, the function may cause a buffer overflow. + * + * @param input the base64 string to process + * @param length the length of the string in bytes + * @param output the pointer to a buffer that can hold the conversion + * result (should be at least maximal_binary_length_from_base64(input, length) + * bytes long). + * @param options the base64 options to use, can be base64_default or + * base64_url, is base64_default by default. + * @param last_chunk_options the last chunk handling options, + * last_chunk_handling_options::loose by default + * but can also be last_chunk_handling_options::strict or + * last_chunk_handling_options::stop_before_partial. + * @return a full_result struct (of type simdutf::full_result containing the + * three fields error, input_count and output_count). + */ +simdutf_warn_unused full_result +base64_to_binary_details(const char *input, size_t length, char *output, + base64_options options = base64_default, + last_chunk_handling_options last_chunk_options = + last_chunk_handling_options::loose) noexcept; + #if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 full_result +base64_to_binary_details( + const detail::input_span_of_byte_like auto &input, + detail::output_span_of_byte_like auto &&binary_output, + base64_options options = base64_default, + last_chunk_handling_options last_chunk_options = loose) noexcept { + #if SIMDUTF_CPLUSPLUS23 + if consteval { + return scalar::base64::base64_to_binary_details_impl( + input.data(), input.size(), binary_output.data(), options, + last_chunk_options); + } else + #endif + { + return base64_to_binary_details( + reinterpret_cast(input.data()), input.size(), + reinterpret_cast(binary_output.data()), options, + last_chunk_options); + } +} + #endif // SIMDUTF_SPAN + +/** + * Convert a base64 input to a binary output while returning more details + * than base64_to_binary. + * + * This function follows the WHATWG forgiving-base64 format, which means that it + * will ignore any ASCII spaces in the input. You may provide a padded input + * (with one or two equal signs at the end) or an unpadded input (without any + * equal signs at the end). + * + * See https://infra.spec.whatwg.org/#forgiving-base64-decode + * + * Unlike base64_to_binary, this function returns a full_result with both + * input_count and output_count, so you always know how much input was consumed + * and how much output was written. There are three cases where the input may + * not be fully consumed: + * + * 1. stop_before_partial: When last_chunk_options is set to + * stop_before_partial, any incomplete 4-character group at the end of the + * input is left unconsumed. This is useful for streaming/chunked decoding + * where you can carry over the unconsumed input to the next chunk. + * + * 2. INVALID_BASE64_CHARACTER: The input contains a character that is not a + * valid base64 character. In this case, input_count indicates where the + * invalid character was found. + * + * 3. BASE64_INPUT_REMAINDER: When last_chunk_options is loose, the input + * contains a number of base64 characters that, when divided by 4, leaves + * a single remainder character (which cannot encode any bytes). + * + * You should call this function with a buffer that is at least + * maximal_binary_length_from_base64(input, length) bytes long. If you fail to + * provide that much space, the function may cause a buffer overflow. + * + * @param input the base64 string to process, in ASCII stored as 16-bit + * units + * @param length the length of the string in 16-bit units + * @param output the pointer to a buffer that can hold the conversion + * result (should be at least maximal_binary_length_from_base64(input, length) + * bytes long). + * @param options the base64 options to use, can be base64_default or + * base64_url, is base64_default by default. + * @param last_chunk_options the last chunk handling options, + * last_chunk_handling_options::loose by default + * but can also be last_chunk_handling_options::strict or + * last_chunk_handling_options::stop_before_partial. + * @return a full_result struct (of type simdutf::full_result containing the + * three fields error, input_count and output_count). + */ +simdutf_warn_unused full_result +base64_to_binary_details(const char16_t *input, size_t length, char *output, + base64_options options = base64_default, + last_chunk_handling_options last_chunk_options = + last_chunk_handling_options::loose) noexcept; + #if SIMDUTF_SPAN +simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 full_result +base64_to_binary_details( std::span input, detail::output_span_of_byte_like auto &&binary_output, base64_options options = base64_default, last_chunk_handling_options last_chunk_options = loose) noexcept { - return base64_to_binary(input.data(), input.size(), - reinterpret_cast(binary_output.data()), - options, last_chunk_options); + #if SIMDUTF_CPLUSPLUS23 + if consteval { + return scalar::base64::base64_to_binary_details_impl( + input.data(), input.size(), binary_output.data(), options, + last_chunk_options); + } else + #endif + { + return base64_to_binary_details( + input.data(), input.size(), + reinterpret_cast(binary_output.data()), options, + last_chunk_options); + } } #endif // SIMDUTF_SPAN /** - * Check if a character is an ignorabl base64 character. + * Check if a character is an ignorable base64 character. * Checking a large input, character by character, is not computationally * efficient. * * @param input the character to check * @param options the base64 options to use, is base64_default by default. - * @return true if the character is an ignorablee base64 character, false + * @return true if the character is an ignorable base64 character, false * otherwise. */ -simdutf_warn_unused bool -base64_ignorable(char input, base64_options options = base64_default) noexcept; -simdutf_warn_unused bool +simdutf_warn_unused simdutf_really_inline simdutf_constexpr23 bool +base64_ignorable(char input, base64_options options = base64_default) noexcept { + return scalar::base64::is_ignorable(input, options); +} +simdutf_warn_unused simdutf_really_inline simdutf_constexpr23 bool base64_ignorable(char16_t input, - base64_options options = base64_default) noexcept; + base64_options options = base64_default) noexcept { + return scalar::base64::is_ignorable(input, options); +} /** * Check if a character is a valid base64 character. @@ -1600,10 +7630,14 @@ base64_ignorable(char16_t input, * @param options the base64 options to use, is base64_default by default. * @return true if the character is a base64 character, false otherwise. */ -simdutf_warn_unused bool -base64_valid(char input, base64_options options = base64_default) noexcept; -simdutf_warn_unused bool -base64_valid(char16_t input, base64_options options = base64_default) noexcept; +simdutf_warn_unused simdutf_really_inline simdutf_constexpr23 bool +base64_valid(char input, base64_options options = base64_default) noexcept { + return scalar::base64::is_base64(input, options); +} +simdutf_warn_unused simdutf_really_inline simdutf_constexpr23 bool +base64_valid(char16_t input, base64_options options = base64_default) noexcept { + return scalar::base64::is_base64(input, options); +} /** * Check if a character is a valid base64 character or the padding character @@ -1614,12 +7648,16 @@ base64_valid(char16_t input, base64_options options = base64_default) noexcept; * @param options the base64 options to use, is base64_default by default. * @return true if the character is a base64 character, false otherwise. */ -simdutf_warn_unused bool +simdutf_warn_unused simdutf_really_inline simdutf_constexpr23 bool base64_valid_or_padding(char input, - base64_options options = base64_default) noexcept; -simdutf_warn_unused bool + base64_options options = base64_default) noexcept { + return scalar::base64::is_base64_or_padding(input, options); +} +simdutf_warn_unused simdutf_really_inline simdutf_constexpr23 bool base64_valid_or_padding(char16_t input, - base64_options options = base64_default) noexcept; + base64_options options = base64_default) noexcept { + return scalar::base64::is_base64_or_padding(input, options); +} /** * Convert a base64 input to a binary output. @@ -1694,25 +7732,7 @@ base64_to_binary_safe(const char *input, size_t length, char *output, last_chunk_handling_options last_chunk_options = last_chunk_handling_options::loose, bool decode_up_to_bad_char = false) noexcept; - #if SIMDUTF_SPAN -/** - * @brief span overload - * @return a tuple of result and outlen - */ -simdutf_really_inline simdutf_warn_unused std::tuple -base64_to_binary_safe(const detail::input_span_of_byte_like auto &input, - detail::output_span_of_byte_like auto &&binary_output, - base64_options options = base64_default, - last_chunk_handling_options last_chunk_options = loose, - bool decode_up_to_bad_char = false) noexcept { - size_t outlen = binary_output.size(); - auto r = base64_to_binary_safe( - reinterpret_cast(input.data()), input.size(), - reinterpret_cast(binary_output.data()), outlen, options, - last_chunk_options, decode_up_to_bad_char); - return {r, outlen}; -} - #endif // SIMDUTF_SPAN +// the span overload has moved to the bottom of the file simdutf_warn_unused result base64_to_binary_safe(const char16_t *input, size_t length, char *output, @@ -1720,25 +7740,7 @@ base64_to_binary_safe(const char16_t *input, size_t length, char *output, last_chunk_handling_options last_chunk_options = last_chunk_handling_options::loose, bool decode_up_to_bad_char = false) noexcept; - #if SIMDUTF_SPAN -/** - * @brief span overload - * @return a tuple of result and outlen - */ -simdutf_really_inline simdutf_warn_unused std::tuple -base64_to_binary_safe(std::span input, - detail::output_span_of_byte_like auto &&binary_output, - base64_options options = base64_default, - last_chunk_handling_options last_chunk_options = loose, - bool decode_up_to_bad_char = false) noexcept { - size_t outlen = binary_output.size(); - auto r = base64_to_binary_safe(input.data(), input.size(), - reinterpret_cast(binary_output.data()), - outlen, options, last_chunk_options, - decode_up_to_bad_char); - return {r, outlen}; -} - #endif // SIMDUTF_SPAN + // span overload moved to bottom of file #if SIMDUTF_ATOMIC_REF /** @@ -1832,21 +7834,6 @@ atomic_base64_to_binary_safe( #endif // SIMDUTF_SPAN #endif // SIMDUTF_ATOMIC_REF -/** - * Find the first occurrence of a character in a string. If the character is - * not found, return a pointer to the end of the string. - * @param start the start of the string - * @param end the end of the string - * @param character the character to find - * @return a pointer to the first occurrence of the character in the string, - * or a pointer to the end of the string if the character is not found. - * - */ -simdutf_warn_unused const char *find(const char *start, const char *end, - char character) noexcept; -simdutf_warn_unused const char16_t * -find(const char16_t *start, const char16_t *end, char16_t character) noexcept; - /** * An implementation of simdutf for a particular CPU architecture. * @@ -1865,7 +7852,7 @@ class implementation { * * @return the name of the implementation, e.g. "haswell", "westmere", "arm64" */ - virtual std::string name() const { return std::string(_name); } + virtual std::string_view name() const noexcept { return _name; } /** * The description of this implementation. @@ -1876,7 +7863,7 @@ class implementation { * * @return the name of the implementation, e.g. "haswell", "westmere", "arm64" */ - virtual std::string description() const { return std::string(_description); } + virtual std::string_view description() const noexcept { return _description; } /** * The instruction sets this implementation is compiled against @@ -1902,9 +7889,12 @@ class implementation { /** * Provide the maximal binary length in bytes given the base64 input. - * In general, if the input contains ASCII spaces, the result will be less - * than the maximum length. It is acceptable to pass invalid base64 strings - * but in such cases the result is implementation defined. + * As long as the input does not contain ignorable characters (e.g., ASCII + * spaces or linefeed characters), the result is exact. In particular, the + * function checks for padding characters. + * + * The function is fast (constant time). It checks up to two characters at + * the end of the string. The input is not otherwise validated or read.. * * @param input the base64 input to process * @param length the length of the base64 input in bytes @@ -1915,9 +7905,12 @@ class implementation { /** * Provide the maximal binary length in bytes given the base64 input. - * In general, if the input contains ASCII spaces, the result will be less - * than the maximum length. It is acceptable to pass invalid base64 strings - * but in such cases the result is implementation defined. + * As long as the input does not contain ignorable characters (e.g., ASCII + * spaces or linefeed characters), the result is exact. In particular, the + * function checks for padding characters. + * + * The function is fast (constant time). It checks up to two characters at + * the end of the string. The input is not otherwise validated or read. * * @param input the base64 input to process, in ASCII stored as 16-bit * units @@ -1927,6 +7920,38 @@ class implementation { simdutf_warn_unused size_t maximal_binary_length_from_base64( const char16_t *input, size_t length) const noexcept; + /** + * Compute the binary length from a base64 input with ASCII spaces. + * This function is useful for well-formed base64 inputs that may contain + * ASCII spaces (such as line breaks). For such inputs, the result is exact. + * + * The function counts non-whitespace characters (ASCII value > 0x20) and + * subtracts padding characters ('=') found at the end. + * + * @param input the base64 input to process + * @param length the length of the base64 input in bytes + * @return number of binary bytes + */ + simdutf_warn_unused virtual size_t + binary_length_from_base64(const char *input, size_t length) const noexcept; + + /** + * Compute the binary length from a base64 input with ASCII spaces. + * This function is useful for well-formed base64 inputs that may contain + * ASCII spaces (such as line breaks). For such inputs, the result is exact. + * + * The function counts non-whitespace characters (ASCII value > 0x20) and + * subtracts padding characters ('=') found at the end. + * + * @param input the base64 input to process, in ASCII stored as 16-bit + * units + * @param length the length of the base64 input in 16-bit units + * @return number of binary bytes + */ + simdutf_warn_unused virtual size_t + binary_length_from_base64(const char16_t *input, + size_t length) const noexcept; + /** * Convert a base64 input to a binary output. * @@ -2001,6 +8026,7 @@ class implementation { base64_options options = base64_default, last_chunk_handling_options last_chunk_options = last_chunk_handling_options::loose) const noexcept = 0; + /** * Convert a base64 input to a binary output. * @@ -2076,11 +8102,12 @@ class implementation { base64_options options = base64_default, last_chunk_handling_options last_chunk_options = last_chunk_handling_options::loose) const noexcept = 0; + /** * Provide the base64 length in bytes given the length of a binary input. * * @param length the length of the input in bytes - * @parem options the base64 options to use, can be base64_default or + * @param options the base64 options to use, can be base64_default or * base64_url, is base64_default by default. * @return number of base64 bytes */ @@ -2111,6 +8138,37 @@ class implementation { virtual size_t binary_to_base64(const char *input, size_t length, char *output, base64_options options = base64_default) const noexcept = 0; + + /** + * Convert a binary input to a base64 output with lines of given length. + * Lines are separated by a single linefeed character. + * + * The default option (simdutf::base64_default) uses the characters `+` and + * `/` as part of its alphabet. Further, it adds padding (`=`) at the end of + * the output to ensure that the output length is a multiple of four. + * + * The URL option (simdutf::base64_url) uses the characters `-` and `_` as + * part of its alphabet. No padding is added at the end of the output. + * + * This function always succeeds. + * + * @param input the binary to process + * @param length the length of the input in bytes + * @param output the pointer to a buffer that can hold the conversion + * result (should be at least base64_length_from_binary_with_lines(length, + * options, line_length) bytes long) + * @param line_length the length of each line, values smaller than 4 are + * interpreted as 4 + * @param options the base64 options to use, can be base64_default or + * base64_url, is base64_default by default. + * @return number of written bytes, will be equal to + * base64_length_from_binary_with_lines(length, options, line_length) + */ + virtual size_t binary_to_base64_with_lines( + const char *input, size_t length, char *output, + size_t line_length = simdutf::default_line_length, + base64_options options = base64_default) const noexcept = 0; + /** * Find the first occurrence of a character in a string. If the character is * not found, return a pointer to the end of the string. @@ -2137,7 +8195,7 @@ class implementation { struct TestProcedure { // display name - std::string name; + std::string_view name; // procedure should return whether given test pass or not void (*procedure)(const implementation &); @@ -2205,7 +8263,7 @@ class available_implementation_list { * @param name the implementation to find, e.g. "westmere", "haswell", "arm64" * @return the implementation, or nullptr if the parse failed. */ - const implementation *operator[](const std::string &name) const noexcept { + const implementation *operator[](std::string_view name) const noexcept { for (const implementation *impl : *this) { if (impl->name() == name) { return impl; @@ -2291,6 +8349,316 @@ get_active_implementation(); } // namespace simdutf + // this header is not part of the public api +/* begin file include/simdutf/base64_implementation.h */ +#ifndef SIMDUTF_BASE64_IMPLEMENTATION_H +#define SIMDUTF_BASE64_IMPLEMENTATION_H + +// this is not part of the public api + +namespace simdutf { + +template +simdutf_warn_unused simdutf_constexpr23 result slow_base64_to_binary_safe_impl( + const chartype *input, size_t length, char *output, size_t &outlen, + base64_options options, + last_chunk_handling_options last_chunk_options) noexcept { + const bool ignore_garbage = (options & base64_default_accept_garbage) != 0; + auto ri = simdutf::scalar::base64::find_end(input, length, options); + size_t equallocation = ri.equallocation; + size_t equalsigns = ri.equalsigns; + length = ri.srclen; + size_t full_input_length = ri.full_input_length; + (void)full_input_length; + if (length == 0) { + outlen = 0; + if (!ignore_garbage && equalsigns > 0) { + return {INVALID_BASE64_CHARACTER, equallocation}; + } + return {SUCCESS, 0}; + } + + // The parameters of base64_tail_decode_safe are: + // - dst: the output buffer + // - outlen: the size of the output buffer + // - srcr: the input buffer + // - length: the size of the input buffer + // - padded_characters: the number of padding characters + // - options: the options for the base64 decoder + // - last_chunk_options: the options for the last chunk + // The function will return the number of bytes written to the output buffer + // and the number of bytes read from the input buffer. + // The function will also return an error code if the input buffer is not + // valid base64. + full_result r = scalar::base64::base64_tail_decode_safe( + output, outlen, input, length, equalsigns, options, last_chunk_options); + r = scalar::base64::patch_tail_result(r, 0, 0, equallocation, + full_input_length, last_chunk_options); + outlen = r.output_count; + if (!is_partial(last_chunk_options) && r.error == error_code::SUCCESS && + equalsigns > 0) { + // additional checks + if ((outlen % 3 == 0) || ((outlen % 3) + 1 + equalsigns != 4)) { + r.error = error_code::INVALID_BASE64_CHARACTER; + } + } + return {r.error, r.input_count}; // we cannot return r itself because it gets + // converted to error/output_count +} + +template +simdutf_warn_unused simdutf_constexpr23 result base64_to_binary_safe_impl( + const chartype *input, size_t length, char *output, size_t &outlen, + base64_options options, + last_chunk_handling_options last_chunk_handling_options, + bool decode_up_to_bad_char) noexcept { + static_assert(std::is_same::value || + std::is_same::value, + "Only char and char16_t are supported."); + size_t remaining_input_length = length; + size_t remaining_output_length = outlen; + size_t input_position = 0; + size_t output_position = 0; + + // We also do a first pass using the fast path to decode as much as possible + size_t safe_input = (std::min)( + remaining_input_length, + base64_length_from_binary(remaining_output_length / 3 * 3, options)); + bool done_with_partial = (safe_input == remaining_input_length); + simdutf::full_result r; + +#if SIMDUTF_CPLUSPLUS23 + if consteval { + r = scalar::base64::base64_to_binary_details_impl( + input + input_position, safe_input, output + output_position, options, + done_with_partial + ? last_chunk_handling_options + : simdutf::last_chunk_handling_options::only_full_chunks); + } else +#endif + { + r = get_active_implementation()->base64_to_binary_details( + input + input_position, safe_input, output + output_position, options, + done_with_partial + ? last_chunk_handling_options + : simdutf::last_chunk_handling_options::only_full_chunks); + } + simdutf_log_assert(r.input_count <= safe_input, + "You should not read more than safe_input"); + simdutf_log_assert(r.output_count <= remaining_output_length, + "You should not write more than remaining_output_length"); + // Technically redundant, but we want to be explicit about it. + input_position += r.input_count; + output_position += r.output_count; + remaining_input_length -= r.input_count; + remaining_output_length -= r.output_count; + if (r.error != simdutf::error_code::SUCCESS) { + // There is an error. We return. + if (decode_up_to_bad_char && + r.error == error_code::INVALID_BASE64_CHARACTER) { + return slow_base64_to_binary_safe_impl( + input, length, output, outlen, options, last_chunk_handling_options); + } + outlen = output_position; + return {r.error, input_position}; + } + + if (done_with_partial) { + // We are done. We have decoded everything. + outlen = output_position; + return {simdutf::error_code::SUCCESS, input_position}; + } + // We have decoded some data, but we still have some data to decode. + // We need to decode the rest of the input buffer. + r = simdutf::scalar::base64::base64_to_binary_details_safe_impl( + input + input_position, remaining_input_length, output + output_position, + remaining_output_length, options, last_chunk_handling_options); + input_position += r.input_count; + output_position += r.output_count; + remaining_input_length -= r.input_count; + remaining_output_length -= r.output_count; + + if (r.error != simdutf::error_code::SUCCESS) { + // There is an error. We return. + if (decode_up_to_bad_char && + r.error == error_code::INVALID_BASE64_CHARACTER) { + return slow_base64_to_binary_safe_impl( + input, length, output, outlen, options, last_chunk_handling_options); + } + outlen = output_position; + return {r.error, input_position}; + } + if (input_position < length) { + // We cannot process the entire input in one go, so we need to + // process it in two steps: first the fast path, then the slow path. + // In some cases, the processing might 'eat up' trailing ignorable + // characters in the fast path, but that can be a problem. + // suppose we have just white space followed by a single base64 character. + // If we first process the white space with the fast path, it will + // eat all of it. But, by the JavaScript standard, we should consume + // no character. See + // https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64 + while (input_position > 0 && + base64_ignorable(input[input_position - 1], options)) { + input_position--; + } + } + outlen = output_position; + return {simdutf::error_code::SUCCESS, input_position}; +} + +} // namespace simdutf +#endif // SIMDUTF_BASE64_IMPLEMENTATION_H +/* end file include/simdutf/base64_implementation.h */ + +namespace simdutf { + #if SIMDUTF_SPAN +/** + * @brief span overload + * @return a tuple of result and outlen + */ +simdutf_really_inline + simdutf_constexpr23 simdutf_warn_unused std::tuple + base64_to_binary_safe( + const detail::input_span_of_byte_like auto &input, + detail::output_span_of_byte_like auto &&binary_output, + base64_options options = base64_default, + last_chunk_handling_options last_chunk_options = loose, + bool decode_up_to_bad_char = false) noexcept { + size_t outlen = binary_output.size(); + #if SIMDUTF_CPLUSPLUS23 + if consteval { + using CInput = std::decay_t; + static_assert(std::is_same_v, + "sorry, the constexpr implementation is for now limited to " + "input of type char"); + using COutput = std::decay_t; + static_assert(std::is_same_v, + "sorry, the constexpr implementation is for now limited to " + "output of type char"); + auto r = base64_to_binary_safe_impl( + input.data(), input.size(), binary_output.data(), outlen, options, + last_chunk_options, decode_up_to_bad_char); + return {r, outlen}; + } else + #endif + { + auto r = base64_to_binary_safe_impl( + reinterpret_cast(input.data()), input.size(), + reinterpret_cast(binary_output.data()), outlen, options, + last_chunk_options, decode_up_to_bad_char); + return {r, outlen}; + } +} + + #if SIMDUTF_SPAN +/** + * @brief span overload + * @return a tuple of result and outlen + */ +simdutf_really_inline + simdutf_warn_unused simdutf_constexpr23 std::tuple + base64_to_binary_safe( + std::span input, + detail::output_span_of_byte_like auto &&binary_output, + base64_options options = base64_default, + last_chunk_handling_options last_chunk_options = loose, + bool decode_up_to_bad_char = false) noexcept { + size_t outlen = binary_output.size(); + #if SIMDUTF_CPLUSPLUS23 + if consteval { + auto r = base64_to_binary_safe_impl( + input.data(), input.size(), binary_output.data(), outlen, options, + last_chunk_options, decode_up_to_bad_char); + return {r, outlen}; + } else + #endif + { + auto r = base64_to_binary_safe( + input.data(), input.size(), + reinterpret_cast(binary_output.data()), outlen, options, + last_chunk_options, decode_up_to_bad_char); + return {r, outlen}; + } +} + #endif // SIMDUTF_SPAN + + #endif // SIMDUTF_SPAN +} // namespace simdutf + +#if SIMDUTF_CPLUSPLUS23 && SIMDUTF_FEATURE_BASE64 + +namespace simdutf { +namespace literals { + +namespace detail { + +// the detail namespace is not part of the public api + +template struct base64_literal_helper { + std::array storage{}; + static constexpr std::size_t size() noexcept { return N - 1; } + consteval base64_literal_helper(const char (&str)[N]) { + for (std::size_t i = 0; i < size(); i++) { + storage[i] = str[i]; + } + } +}; + +template struct base64_decode_result { + static constexpr std::size_t max_out = (InputLen + 3) / 4 * 3; + std::array buffer{}; + std::size_t output_count{}; +}; + +template +consteval auto base64_decode_literal(const char *str) { + base64_decode_result result{}; + auto r = scalar::base64::base64_to_binary_details_impl( + str, InputLen, result.buffer.data(), base64_default, loose); + if (r.error != error_code::SUCCESS) { + #if __cpp_lib_unreachable >= 202202L + std::unreachable(); // invalid base64 input in _base64 literal + #else + // workaround for older stdlib + throw "invalid base64 input in _base64 literal"; + #endif + } + result.output_count = r.output_count; + return result; +} + +template consteval auto base64_make_array() { + constexpr auto decoded = base64_decode_literal(a.storage.data()); + std::array ret{}; + for (std::size_t i = 0; i < decoded.output_count; i++) { + ret[i] = decoded.buffer[i]; + } + return ret; +} + +} // namespace detail + +/** + * User-defined literal for compile-time base64 decoding. + * + * Usage: + * using namespace simdutf::literals; + * constexpr auto decoded = "SGVsbG8gV29ybGQh"_base64; + * // decoded is a std::array containing "Hello World!" + * + * The input must be valid base64. Whitepace is allowed and ignored. + * A compilation error occurs if the input is invalid. + */ +template consteval auto operator""_base64() { + return detail::base64_make_array(); +} + +} // namespace literals +} // namespace simdutf + +#endif // SIMDUTF_CPLUSPLUS23 && SIMDUTF_FEATURE_BASE64 + #endif // SIMDUTF_IMPLEMENTATION_H /* end file include/simdutf/implementation.h */ diff --git a/example/Gemfile.lock b/example/Gemfile.lock index f5fa9e6..4ab57d2 100644 --- a/example/Gemfile.lock +++ b/example/Gemfile.lock @@ -5,18 +5,12 @@ GEM base64 nkf rexml - activesupport (7.2.2.2) - base64 - benchmark (>= 0.3) - bigdecimal - concurrent-ruby (~> 1.0, >= 1.3.1) - connection_pool (>= 2.2.5) - drb + activesupport (6.1.7.10) + concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) - logger (>= 1.4.2) minitest (>= 5.1) - securerandom (>= 0.3) - tzinfo (~> 2.0, >= 2.0.5) + tzinfo (~> 2.0) + zeitwerk (~> 2.3) addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) algoliasearch (1.27.5) @@ -66,8 +60,6 @@ GEM cocoapods-try (1.2.0) colored2 (3.1.2) concurrent-ruby (1.3.3) - connection_pool (2.5.4) - drb (2.2.3) escape (0.0.4) ethon (0.15.0) ffi (>= 1.15.0) @@ -79,9 +71,9 @@ GEM mutex_m i18n (1.14.7) concurrent-ruby (~> 1.0) - json (2.15.0) + json (2.7.6) logger (1.7.0) - minitest (5.25.5) + minitest (5.25.4) molinillo (0.8.0) mutex_m (0.3.0) nanaimo (0.3.0) @@ -91,7 +83,6 @@ GEM public_suffix (4.0.7) rexml (3.4.4) ruby-macho (2.5.1) - securerandom (0.4.1) typhoeus (1.5.0) ethon (>= 0.9.0, < 0.16.0) tzinfo (2.0.6) @@ -103,6 +94,7 @@ GEM colored2 (~> 3.1) nanaimo (~> 0.3.0) rexml (>= 3.3.6, < 4.0) + zeitwerk (2.6.18) PLATFORMS ruby diff --git a/example/ios/NitroBase64Example.xcodeproj/project.pbxproj b/example/ios/NitroBase64Example.xcodeproj/project.pbxproj index 0444ca0..8cd2c4c 100644 --- a/example/ios/NitroBase64Example.xcodeproj/project.pbxproj +++ b/example/ios/NitroBase64Example.xcodeproj/project.pbxproj @@ -7,23 +7,23 @@ objects = { /* Begin PBXBuildFile section */ - 0C80B921A6F3F58F76C31292 /* libPods-NitroBase64Example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-NitroBase64Example.a */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; BA8731FA8F4AE30FD34E383A /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; }; + D891FF4993729C4A84C2F7E9 /* libPods-NitroBase64Example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 098BA51963414FC1C9E6DB12 /* libPods-NitroBase64Example.a */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 098BA51963414FC1C9E6DB12 /* libPods-NitroBase64Example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-NitroBase64Example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07F961A680F5B00A75B9A /* NitroBase64Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NitroBase64Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = NitroBase64Example/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = NitroBase64Example/Info.plist; sourceTree = ""; }; 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = NitroBase64Example/PrivacyInfo.xcprivacy; sourceTree = ""; }; - 3B4392A12AC88292D35C810B /* Pods-NitroBase64Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NitroBase64Example.debug.xcconfig"; path = "Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example.debug.xcconfig"; sourceTree = ""; }; - 5709B34CF0A7D63546082F79 /* Pods-NitroBase64Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NitroBase64Example.release.xcconfig"; path = "Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example.release.xcconfig"; sourceTree = ""; }; - 5DCACB8F33CDC322A6C60F78 /* libPods-NitroBase64Example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-NitroBase64Example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 66E9F03C53D7017FE9545A1B /* Pods-NitroBase64Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NitroBase64Example.release.xcconfig"; path = "Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example.release.xcconfig"; sourceTree = ""; }; 761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = NitroBase64Example/AppDelegate.swift; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = NitroBase64Example/LaunchScreen.storyboard; sourceTree = ""; }; + DAACC79F12F190ED23DE45D7 /* Pods-NitroBase64Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NitroBase64Example.debug.xcconfig"; path = "Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example.debug.xcconfig"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ @@ -32,7 +32,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 0C80B921A6F3F58F76C31292 /* libPods-NitroBase64Example.a in Frameworks */, + D891FF4993729C4A84C2F7E9 /* libPods-NitroBase64Example.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -55,7 +55,7 @@ isa = PBXGroup; children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - 5DCACB8F33CDC322A6C60F78 /* libPods-NitroBase64Example.a */, + 098BA51963414FC1C9E6DB12 /* libPods-NitroBase64Example.a */, ); name = Frameworks; sourceTree = ""; @@ -92,8 +92,8 @@ BBD78D7AC51CEA395F1C20DB /* Pods */ = { isa = PBXGroup; children = ( - 3B4392A12AC88292D35C810B /* Pods-NitroBase64Example.debug.xcconfig */, - 5709B34CF0A7D63546082F79 /* Pods-NitroBase64Example.release.xcconfig */, + DAACC79F12F190ED23DE45D7 /* Pods-NitroBase64Example.debug.xcconfig */, + 66E9F03C53D7017FE9545A1B /* Pods-NitroBase64Example.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -105,13 +105,13 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "NitroBase64Example" */; buildPhases = ( - C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */, + 698DAD2D36C86EE227AD3CC1 /* [CP] Check Pods Manifest.lock */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */, - E235C05ADACE081382539298 /* [CP] Copy Pods Resources */, + 9E13E8199B99474DE1C693E0 /* [CP] Embed Pods Frameworks */, + 65F550C0311E7C54C50B866B /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -183,24 +183,24 @@ shellPath = /bin/sh; shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; }; - 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = { + 65F550C0311E7C54C50B866B /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example-frameworks-${CONFIGURATION}-input-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example-resources-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Embed Pods Frameworks"; + name = "[CP] Copy Pods Resources"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example-frameworks-${CONFIGURATION}-output-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example-resources-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example-resources.sh\"\n"; showEnvVarsInLog = 0; }; - C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */ = { + 698DAD2D36C86EE227AD3CC1 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -222,21 +222,21 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = { + 9E13E8199B99474DE1C693E0 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example-resources-${CONFIGURATION}-input-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Copy Pods Resources"; + name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example-resources-${CONFIGURATION}-output-files.xcfilelist", + "${PODS_ROOT}/Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example-resources.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -255,11 +255,12 @@ /* Begin XCBuildConfiguration section */ 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-NitroBase64Example.debug.xcconfig */; + baseConfigurationReference = DAACC79F12F190ED23DE45D7 /* Pods-NitroBase64Example.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 4QDQZA9V92; ENABLE_BITCODE = NO; INFOPLIST_FILE = NitroBase64Example/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 16.6; @@ -283,11 +284,12 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5709B34CF0A7D63546082F79 /* Pods-NitroBase64Example.release.xcconfig */; + baseConfigurationReference = 66E9F03C53D7017FE9545A1B /* Pods-NitroBase64Example.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 4QDQZA9V92; INFOPLIST_FILE = NitroBase64Example/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 16.6; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index df8655d..217f83b 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -8,7 +8,7 @@ PODS: - hermes-engine (0.81.1): - hermes-engine/Pre-built (= 0.81.1) - hermes-engine/Pre-built (0.81.1) - - NitroBase64 (0.8.0): + - NitroBase64 (0.8.2): - boost - DoubleConversion - fast_float @@ -38,7 +38,7 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - NitroModules (0.29.8): + - NitroModules (0.35.6): - boost - DoubleConversion - fast_float @@ -1807,8 +1807,34 @@ PODS: - React-RCTFBReactNativeSpec - ReactCommon/turbomodule/core - SocketRocket - - react-native-quick-base64 (2.2.2): + - react-native-quick-base64 (3.0.0): + - boost + - DoubleConversion + - fast_float + - fmt + - glog + - hermes-engine + - RCT-Folly + - RCT-Folly/Fabric + - RCTRequired + - RCTTypeSafety - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsi + - React-NativeModulesApple + - React-RCTFabric + - React-renderercss + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - SocketRocket + - Yoga - react-native-safe-area-context (5.6.1): - boost - DoubleConversion @@ -2648,8 +2674,8 @@ SPEC CHECKSUMS: fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd glog: 5683914934d5b6e4240e497e0f4a3b42d1854183 hermes-engine: 4f8246b1f6d79f625e0d99472d1f3a71da4d28ca - NitroBase64: a902e6a58f53c5854d5fcfe1e8db4a29f89c2468 - NitroModules: 808eee1dfaf75b2c5a63ea3608cb651cc7d13687 + NitroBase64: e9d09ea94a839528b94a43d3c500ad1405c8a413 + NitroModules: 70e2ff77a0b718e47d5bccd837fefe8a9aa20f50 RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669 RCTDeprecation: c4b9e2fd0ab200e3af72b013ed6113187c607077 RCTRequired: e97dd5dafc1db8094e63bc5031e0371f092ae92a @@ -2683,7 +2709,7 @@ SPEC CHECKSUMS: React-logger: 7aef4d74123e5e3d267e5af1fbf5135b5a0d8381 React-Mapbuffer: 91e0eab42a6ae7f3e34091a126d70fc53bd3823e React-microtasksnativemodule: 1ead4fe154df3b1ba34b5a9e35ef3c4bdfa72ccb - react-native-quick-base64: 6568199bb2ac8e72ecdfdc73a230fbc5c1d3aac4 + react-native-quick-base64: e5eea0a73474108a7b80e33ce2c004a414649da2 react-native-safe-area-context: c6e2edd1c1da07bdce287fa9d9e60c5f7b514616 React-NativeModulesApple: eff2eba56030eb0d107b1642b8f853bc36a833ac React-oscompat: b12c633e9c00f1f99467b1e0e0b8038895dae436 diff --git a/example/package.json b/example/package.json index 31912f2..30bc9ad 100644 --- a/example/package.json +++ b/example/package.json @@ -13,8 +13,8 @@ "@react-native/new-app-screen": "0.81.1", "react": "19.1.0", "react-native": "0.81.1", - "react-native-nitro-modules": "^0.29.8", - "react-native-quick-base64": "^2.2.2", + "react-native-nitro-modules": "^0.35.6", + "react-native-quick-base64": "^3.0.0", "react-native-safe-area-context": "^5.5.2" }, "devDependencies": { diff --git a/example/src/Benchmark.tsx b/example/src/Benchmark.tsx index 0bfe45d..764e943 100644 --- a/example/src/Benchmark.tsx +++ b/example/src/Benchmark.tsx @@ -38,7 +38,7 @@ const NitroBase64Demo = () => { try { // First encode to get valid base64 const encoded = encode(inputText, urlSafeMode); - const result = decode(encoded, urlSafeMode); + const result = decode(encoded); logResult(`Nitro Decode (urlSafe: ${urlSafeMode})`, result); logResult('Round Trip Success', `${result === inputText ? 'YES' : 'NO'}`); Alert.alert( @@ -57,14 +57,14 @@ const NitroBase64Demo = () => { const nitroEncoded = encode(inputText, false); logResult('Nitro encode()', nitroEncoded); - const nitroDecoded = decode(nitroEncoded, false); + const nitroDecoded = decode(nitroEncoded); logResult('Nitro decode()', nitroDecoded); // Test URL-safe version const nitroUrlSafe = encode(inputText, true); logResult('Nitro URL-safe encode()', nitroUrlSafe); - const nitroUrlSafeDecoded = decode(nitroUrlSafe, true); + const nitroUrlSafeDecoded = decode(nitroUrlSafe); logResult('Nitro URL-safe decode()', nitroUrlSafeDecoded); // Test if they match @@ -101,7 +101,7 @@ const NitroBase64Demo = () => { testCases.forEach((testCase) => { try { const encoded = encode(testCase.data, urlSafeMode); - const decoded = decode(encoded, urlSafeMode); + const decoded = decode(encoded); const success = decoded === testCase.data; if (success) passCount++; logResult( @@ -128,7 +128,7 @@ const NitroBase64Demo = () => { try { const encoded = encode(largeText, urlSafeMode); - const decoded = decode(encoded, urlSafeMode); + const decoded = decode(encoded); const endTime = Date.now(); const duration = endTime - startTime; diff --git a/example/src/Benchmark2.tsx b/example/src/Benchmark2.tsx index 9d4a7e3..9c330b4 100644 --- a/example/src/Benchmark2.tsx +++ b/example/src/Benchmark2.tsx @@ -1,258 +1,292 @@ -/* global performance, atob, btoa */ -import jsBase64 from 'base64-js'; +import { + fromByteArray as quickFromByteArray, + toByteArray as quickToByteArray, +} from 'react-native-quick-base64'; import { useState } from 'react'; import { Platform, Pressable, StyleSheet, Text, View } from 'react-native'; -import { decode, encode } from 'react-native-nitro-base64'; +import { + decode, + decodeBuffer, + encode, + encodeBuffer, + fromByteArray, + toByteArray, +} from 'react-native-nitro-base64'; import { data as largeData } from './image-large.json'; import { data } from './image.json'; -const sleep = (t: number) => new Promise((resolve) => setTimeout(resolve, t)); -const round = (num: number, decimalPlaces = 0): string => { - return num.toFixed(decimalPlaces); +declare const performance: { now(): number }; +declare function atob(data: string): string; +declare function btoa(data: string): string; + +const sleep = (t: number) => + new Promise((resolve) => setTimeout(resolve, t)); +const round = (num: number, decimalPlaces = 0): string => + num.toFixed(decimalPlaces); +const speedup = (ms: number, base: number) => { + if (!ms || !base || base <= 0) return ''; + const ratio = base / ms; + if (ratio >= 1) return `(${round(ratio, 2)}x faster)`; + return `(${round(1 / ratio, 2)}x slower)`; }; const Benchmarks2 = () => { - const [processingJSBase64, setProcessingJSBase64] = useState(false); - const [jsBase64Result, setJSBase64Result] = useState(0); - const [processingNativeBase64, setProcessingNativeBase64] = - useState(false); - const [nativeBase64Result, setNativeBase64Result] = useState(0); - const [processingObjcBase64, setProcessingObjcBase64] = - useState(false); - const [objcBase64Result, setObjcBase64Result] = useState(0); - const [processingAtobBtoa, setProcessingAtobBtoa] = useState(false); - const [atobBtoaResult, setAtobBtoaResult] = useState(0); - const [processingAtobBtoaLarge, setProcessingAtobBtoaLarge] = - useState(false); - const [atobBtoaLargeResult, setAtobBtoaLargeResult] = useState(0); - const [processingJSBase64Large, setProcessingJSBase64Large] = - useState(false); - const [jsBase64LargeResult, setJSBase64LargeResult] = useState(0); - - const handleNativeBase64Press = async () => { - setProcessingNativeBase64(true); - let dataToProcess = data; - await sleep(1); - const startTime = performance.now(); + // Nitro string (small / large) + const [nitroResult, setNitroResult] = useState(0); + const [nitroLargeResult, setNitroLargeResult] = useState(0); - for (let iter = 0; iter < 30; iter++) { - const decoded = encode(dataToProcess); - dataToProcess = decode(decoded); - if (dataToProcess !== data) { - throw new Error('Data does not match'); - } - } - const finishedTime = performance.now(); - console.log( - 'Nitro C++ done! took', - finishedTime - startTime, - 'milliseconds' - ); - setNativeBase64Result(finishedTime - startTime); - setProcessingNativeBase64(false); - }; + // quick-base64 (small / large) + const [quickResult, setQuickResult] = useState(0); + const [quickLargeResult, setQuickLargeResult] = useState(0); - const handleJSBase64Press = async () => { - setProcessingJSBase64(true); - let dataToProcess = data; - await sleep(1); - const startTime = performance.now(); + // atob/btoa (small / large) + const [atobResult, setAtobResult] = useState(0); + const [atobLargeResult, setAtobLargeResult] = useState(0); + + // decodeBuffer (small / large) + const [decodeBufferResult, setDecodeBufferResult] = useState(0); + const [decodeBufferLargeResult, setDecodeBufferLargeResult] = useState(0); + + // encodeBuffer (small / large) + const [encodeBufferResult, setEncodeBufferResult] = useState(0); + const [encodeBufferLargeResult, setEncodeBufferLargeResult] = useState(0); - for (let iter = 0; iter < 30; iter++) { - const decoded = jsBase64.toByteArray(dataToProcess); - dataToProcess = jsBase64.fromByteArray(decoded); + // fromByteArray / toByteArray (small / large) + const [fromByteArrayResult, setFromByteArrayResult] = useState(0); + const [fromByteArrayLargeResult, setFromByteArrayLargeResult] = useState(0); + const [toByteArrayResult, setToByteArrayResult] = useState(0); + const [toByteArrayLargeResult, setToByteArrayLargeResult] = useState(0); + + const [processing, setProcessing] = useState(false); + + // ── Nitro string ────────────────────────────────────────────────────────── + + const runNitro = async (src: string): Promise => { + let d = src; + await sleep(1); + const t = performance.now(); + for (let i = 0; i < 30; i++) { + const enc = encode(d); + d = decode(enc); } - const finishedTime = performance.now(); - console.log( - 'base64-js done! took', - finishedTime - startTime, - 'milliseconds' - ); - setJSBase64Result(finishedTime - startTime); - setProcessingJSBase64(false); + return performance.now() - t; }; - // atob/btoa benchmark (native browser API) - Small image - const handleAtobBtoaPress = async () => { - setProcessingAtobBtoa(true); - let dataToProcess = data; - await sleep(1); - const startTime = performance.now(); + // ── quick-base64 ────────────────────────────────────────────────────────── - try { - for (let iter = 0; iter < 30; iter++) { - // btoa expects a binary string, atob returns a binary string - const decoded = atob(dataToProcess); - dataToProcess = btoa(decoded); - } - const finishedTime = performance.now(); - console.log( - 'atob/btoa done! took', - finishedTime - startTime, - 'milliseconds' - ); - setAtobBtoaResult(finishedTime - startTime); - } catch (error) { - console.error('atob/btoa error:', error); - setAtobBtoaResult(-1); // Indicate error + const runQuick = async (src: string): Promise => { + let d = src; + await sleep(1); + const t = performance.now(); + for (let i = 0; i < 30; i++) { + const bytes = quickToByteArray(d); + d = quickFromByteArray(bytes); } - setProcessingAtobBtoa(false); + return performance.now() - t; }; - // atob/btoa benchmark (native browser API) - Large image - const handleAtobBtoaLargePress = async () => { - setProcessingAtobBtoaLarge(true); - let dataToProcess = largeData; - await sleep(1); - const startTime = performance.now(); + // ── atob/btoa ───────────────────────────────────────────────────────────── + const runAtob = async (src: string): Promise => { + let d = src; + await sleep(1); + const t = performance.now(); try { - for (let iter = 0; iter < 30; iter++) { - const decoded = atob(dataToProcess); - dataToProcess = btoa(decoded); + for (let i = 0; i < 30; i++) { + const bin = atob(d); + d = btoa(bin); } - const finishedTime = performance.now(); - console.log( - 'atob/btoa Large done! took', - finishedTime - startTime, - 'milliseconds' - ); - setAtobBtoaLargeResult(finishedTime - startTime); - } catch (error) { - console.error('atob/btoa Large error:', error); - setAtobBtoaLargeResult(-1); // Indicate error + } catch { + return -1; } - setProcessingAtobBtoaLarge(false); + return performance.now() - t; }; - // Large image base64-js benchmark - const handleJSBase64LargePress = async () => { - setProcessingJSBase64Large(true); - let dataToProcess = largeData; + // ── decodeBuffer ────────────────────────────────────────────────────────── + + const runDecodeBuffer = async (src: string): Promise => { + const encoded = encode(src); await sleep(1); - const startTime = performance.now(); + const t = performance.now(); + for (let i = 0; i < 30; i++) decodeBuffer(encoded); + return performance.now() - t; + }; - for (let iter = 0; iter < 30; iter++) { - const decoded = jsBase64.toByteArray(dataToProcess); - dataToProcess = jsBase64.fromByteArray(decoded); - } - const finishedTime = performance.now(); - console.log( - 'base64-js Large done! took', - finishedTime - startTime, - 'milliseconds' - ); - setJSBase64LargeResult(finishedTime - startTime); - setProcessingJSBase64Large(false); + // ── encodeBuffer ────────────────────────────────────────────────────────── + + const runEncodeBuffer = async (src: string): Promise => { + const binary = decodeBuffer(src); + await sleep(1); + const t = performance.now(); + for (let i = 0; i < 30; i++) encodeBuffer(binary); + return performance.now() - t; }; - // Large Nitro C++ base64 benchmark - const handleObjcBase64Press = async () => { - setProcessingObjcBase64(true); - let dataToProcess = largeData; + // ── fromByteArray ───────────────────────────────────────────────────────── + + const runFromByteArray = async (src: string): Promise => { + const bytes = new Uint8Array(decodeBuffer(src)); await sleep(1); - const startTime = performance.now(); - for (let iter = 0; iter < 30; iter++) { - const decoded = encode(dataToProcess); - dataToProcess = decode(decoded); - } - const finishedTime = performance.now(); - console.log( - 'Nitro C++ Large done! took', - finishedTime - startTime, - 'milliseconds' - ); - setObjcBase64Result(finishedTime - startTime); - setProcessingObjcBase64(false); + const t = performance.now(); + for (let i = 0; i < 30; i++) fromByteArray(bytes); + return performance.now() - t; }; - const calculateSpeedup = (baseTime: number, compareTime: number) => { - if (!baseTime || !compareTime || compareTime <= 0) return ''; - return `(${round(baseTime / compareTime, 2)}x faster)`; + // ── toByteArray ─────────────────────────────────────────────────────────── + + const runToByteArray = async (src: string): Promise => { + const encoded = encode(src); + await sleep(1); + const t = performance.now(); + for (let i = 0; i < 30; i++) toByteArray(encoded); + return performance.now() - t; }; - return ( - - Small Image (7KB) - 30 iterations + // ── Run all ─────────────────────────────────────────────────────────────── - - Nitro Simdutf C++ - - {nativeBase64Result > 0 ? `${round(nativeBase64Result, 2)}ms` : ''} - - + const handleRunAll = async () => { + setProcessing(true); - - base64-js (JS) - - {jsBase64Result > 0 - ? `${round(jsBase64Result, 2)}ms ${calculateSpeedup(jsBase64Result, nativeBase64Result)}` - : ''} - - + const [n, nL, q, qL, a, aL, db, dbL, eb, ebL, fba, fbaL, tba, tbaL] = + await Promise.all([ + runNitro(data), + runNitro(largeData), + runQuick(data), + runQuick(largeData), + runAtob(data), + runAtob(largeData), + runDecodeBuffer(data), + runDecodeBuffer(largeData), + runEncodeBuffer(data), + runEncodeBuffer(largeData), + runFromByteArray(data), + runFromByteArray(largeData), + runToByteArray(data), + runToByteArray(largeData), + ]); - - atob/btoa (Native) - - {atobBtoaResult > 0 - ? `${round(atobBtoaResult, 2)}ms ${calculateSpeedup(atobBtoaResult, nativeBase64Result)}` - : atobBtoaResult === -1 - ? 'Error/Not supported' - : ''} - - + setNitroResult(n); + setNitroLargeResult(nL); + setQuickResult(q); + setQuickLargeResult(qL); + setAtobResult(a); + setAtobLargeResult(aL); + setDecodeBufferResult(db); + setDecodeBufferLargeResult(dbL); + setEncodeBufferResult(eb); + setEncodeBufferLargeResult(ebL); + setFromByteArrayResult(fba); + setFromByteArrayLargeResult(fbaL); + setToByteArrayResult(tba); + setToByteArrayLargeResult(tbaL); - - Large Image (1.3MB) - 30 iterations + setProcessing(false); + }; + + // ── Render ──────────────────────────────────────────────────────────────── + + const Row = ({ + label, + ms, + base, + }: { + label: string; + ms: number; + base: number; + }) => ( + + {label} + + {ms > 0 + ? `${round(ms, 2)}ms ${speedup(ms, base)}` + : ms === -1 + ? 'Not supported' + : ''} + + ); - - Nitro Simdutf C++ - - {objcBase64Result > 0 ? `${round(objcBase64Result, 2)}ms` : ''} - - + return ( + + Small Image (7KB) — 30 iterations - base64-js (JS) + Nitro simdutf C++ (string) - {jsBase64LargeResult > 0 - ? `${round(jsBase64LargeResult, 2)}ms ${calculateSpeedup(jsBase64LargeResult, objcBase64Result)}` - : ''} + {nitroResult > 0 ? `${round(nitroResult, 2)}ms` : ''} + + + + + + + + + Large Image (1.3MB) — 30 iterations + - atob/btoa (Native) + Nitro simdutf C++ (string) - {atobBtoaLargeResult > 0 - ? `${round(atobBtoaLargeResult, 2)}ms ${calculateSpeedup(atobBtoaLargeResult, objcBase64Result)}` - : atobBtoaLargeResult === -1 - ? 'Error/Not supported' - : ''} + {nitroLargeResult > 0 ? `${round(nitroLargeResult, 2)}ms` : ''} + + + + + + - { - handleNativeBase64Press(); - handleJSBase64Press(); - handleAtobBtoaPress(); - handleObjcBase64Press(); - handleJSBase64LargePress(); - handleAtobBtoaLargePress(); - }} - style={styles.button} - > + - {processingNativeBase64 || - processingJSBase64 || - processingObjcBase64 || - processingAtobBtoa || - processingAtobBtoaLarge || - processingJSBase64Large - ? 'Processing...' - : 'Run Benchmarks'} + {processing ? 'Processing...' : 'Run Benchmarks'} @@ -262,11 +296,6 @@ const Benchmarks2 = () => { export default Benchmarks2; const styles = StyleSheet.create({ - box: { - width: 60, - height: 60, - marginVertical: 20, - }, lib: { flexDirection: 'row', justifyContent: 'space-between', @@ -292,11 +321,6 @@ const styles = StyleSheet.create({ marginTop: 20, borderRadius: 8, }, - speedup: { - marginVertical: 5, - fontSize: 18, - textAlign: 'center', - }, sectionTitle: { fontSize: 16, fontWeight: 'bold', diff --git a/nitro.json b/nitro.json index f896413..26f1b00 100644 --- a/nitro.json +++ b/nitro.json @@ -9,7 +9,10 @@ }, "autolinking": { "NitroBase64": { - "cpp": "NitroBase64" + "all": { + "language": "c++", + "implementationClassName": "NitroBase64" + } } }, "ignorePaths": ["node_modules"] diff --git a/nitrogen/generated/android/NitroBase64+autolinking.cmake b/nitrogen/generated/android/NitroBase64+autolinking.cmake index 0602aee..2c51da3 100644 --- a/nitrogen/generated/android/NitroBase64+autolinking.cmake +++ b/nitrogen/generated/android/NitroBase64+autolinking.cmake @@ -2,7 +2,7 @@ # NitroBase64+autolinking.cmake # This file was generated by nitrogen. DO NOT MODIFY THIS FILE. # https://github.com/mrousavy/nitro -# Copyright © 2025 Marc Rousavy @ Margelo +# Copyright © Marc Rousavy @ Margelo # # This is a CMake file that adds all files generated by Nitrogen diff --git a/nitrogen/generated/android/NitroBase64+autolinking.gradle b/nitrogen/generated/android/NitroBase64+autolinking.gradle index 1cef298..4e22201 100644 --- a/nitrogen/generated/android/NitroBase64+autolinking.gradle +++ b/nitrogen/generated/android/NitroBase64+autolinking.gradle @@ -2,7 +2,7 @@ /// NitroBase64+autolinking.gradle /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. /// https://github.com/mrousavy/nitro -/// Copyright © 2025 Marc Rousavy @ Margelo +/// Copyright © Marc Rousavy @ Margelo /// /// This is a Gradle file that adds all files generated by Nitrogen diff --git a/nitrogen/generated/android/NitroBase64OnLoad.cpp b/nitrogen/generated/android/NitroBase64OnLoad.cpp index 1a14934..9173907 100644 --- a/nitrogen/generated/android/NitroBase64OnLoad.cpp +++ b/nitrogen/generated/android/NitroBase64OnLoad.cpp @@ -2,7 +2,7 @@ /// NitroBase64OnLoad.cpp /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. /// https://github.com/mrousavy/nitro -/// Copyright © 2025 Marc Rousavy @ Margelo +/// Copyright © Marc Rousavy @ Margelo /// #ifndef BUILDING_NITROBASE64_WITH_GENERATED_CMAKE_PROJECT @@ -20,25 +20,30 @@ namespace margelo::nitro::nitrobase64 { int initialize(JavaVM* vm) { + return facebook::jni::initialize(vm, []() { + ::margelo::nitro::nitrobase64::registerAllNatives(); + }); +} + + + +void registerAllNatives() { using namespace margelo::nitro; using namespace margelo::nitro::nitrobase64; - using namespace facebook; - - return facebook::jni::initialize(vm, [] { - // Register native JNI methods - - - // Register Nitro Hybrid Objects - HybridObjectRegistry::registerHybridObjectConstructor( - "NitroBase64", - []() -> std::shared_ptr { - static_assert(std::is_default_constructible_v, - "The HybridObject \"NitroBase64\" is not default-constructible! " - "Create a public constructor that takes zero arguments to be able to autolink this HybridObject."); - return std::make_shared(); - } - ); - }); + + // Register native JNI methods + + + // Register Nitro Hybrid Objects + HybridObjectRegistry::registerHybridObjectConstructor( + "NitroBase64", + []() -> std::shared_ptr { + static_assert(std::is_default_constructible_v, + "The HybridObject \"NitroBase64\" is not default-constructible! " + "Create a public constructor that takes zero arguments to be able to autolink this HybridObject."); + return std::make_shared(); + } + ); } } // namespace margelo::nitro::nitrobase64 diff --git a/nitrogen/generated/android/NitroBase64OnLoad.hpp b/nitrogen/generated/android/NitroBase64OnLoad.hpp index c4ac6d1..9208fde 100644 --- a/nitrogen/generated/android/NitroBase64OnLoad.hpp +++ b/nitrogen/generated/android/NitroBase64OnLoad.hpp @@ -2,24 +2,33 @@ /// NitroBase64OnLoad.hpp /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. /// https://github.com/mrousavy/nitro -/// Copyright © 2025 Marc Rousavy @ Margelo +/// Copyright © Marc Rousavy @ Margelo /// #include +#include #include namespace margelo::nitro::nitrobase64 { + [[deprecated("Use registerNatives() instead.")]] + int initialize(JavaVM* vm); + /** - * Initializes the native (C++) part of NitroBase64, and autolinks all Hybrid Objects. - * Call this in your `JNI_OnLoad` function (probably inside `cpp-adapter.cpp`). + * Register the native (C++) part of NitroBase64, and autolinks all Hybrid Objects. + * Call this in your `JNI_OnLoad` function (probably inside `cpp-adapter.cpp`), + * inside a `facebook::jni::initialize(vm, ...)` call. * Example: * ```cpp (cpp-adapter.cpp) * JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) { - * return margelo::nitro::nitrobase64::initialize(vm); + * return facebook::jni::initialize(vm, []() { + * // register all NitroBase64 HybridObjects + * margelo::nitro::nitrobase64::registerNatives(); + * // any other custom registrations go here. + * }); * } * ``` */ - int initialize(JavaVM* vm); + void registerAllNatives(); } // namespace margelo::nitro::nitrobase64 diff --git a/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrobase64/NitroBase64OnLoad.kt b/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrobase64/NitroBase64OnLoad.kt index dc4eb11..7418548 100644 --- a/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrobase64/NitroBase64OnLoad.kt +++ b/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrobase64/NitroBase64OnLoad.kt @@ -2,7 +2,7 @@ /// NitroBase64OnLoad.kt /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. /// https://github.com/mrousavy/nitro -/// Copyright © 2025 Marc Rousavy @ Margelo +/// Copyright © Marc Rousavy @ Margelo /// package com.margelo.nitro.nitrobase64 diff --git a/nitrogen/generated/ios/NitroBase64+autolinking.rb b/nitrogen/generated/ios/NitroBase64+autolinking.rb index 02f41ce..656ede5 100644 --- a/nitrogen/generated/ios/NitroBase64+autolinking.rb +++ b/nitrogen/generated/ios/NitroBase64+autolinking.rb @@ -2,7 +2,7 @@ # NitroBase64+autolinking.rb # This file was generated by nitrogen. DO NOT MODIFY THIS FILE. # https://github.com/mrousavy/nitro -# Copyright © 2025 Marc Rousavy @ Margelo +# Copyright © Marc Rousavy @ Margelo # # This is a Ruby script that adds all files generated by Nitrogen @@ -52,9 +52,11 @@ def add_nitrogen_files(spec) spec.pod_target_xcconfig = current_pod_target_xcconfig.merge({ # Use C++ 20 "CLANG_CXX_LANGUAGE_STANDARD" => "c++20", - # Enables C++ <-> Swift interop (by default it's only C) + # Enables C++ <-> Swift interop (by default it's only ObjC) "SWIFT_OBJC_INTEROP_MODE" => "objcxx", # Enables stricter modular headers "DEFINES_MODULE" => "YES", + # Disable auto-generated ObjC header for Swift (Static linkage on Xcode 26.4 breaks here) + "SWIFT_INSTALL_OBJC_HEADER" => "NO", }) end diff --git a/nitrogen/generated/ios/NitroBase64-Swift-Cxx-Bridge.cpp b/nitrogen/generated/ios/NitroBase64-Swift-Cxx-Bridge.cpp index 8d77468..ea1cf66 100644 --- a/nitrogen/generated/ios/NitroBase64-Swift-Cxx-Bridge.cpp +++ b/nitrogen/generated/ios/NitroBase64-Swift-Cxx-Bridge.cpp @@ -2,7 +2,7 @@ /// NitroBase64-Swift-Cxx-Bridge.cpp /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. /// https://github.com/mrousavy/nitro -/// Copyright © 2025 Marc Rousavy @ Margelo +/// Copyright © Marc Rousavy @ Margelo /// #include "NitroBase64-Swift-Cxx-Bridge.hpp" diff --git a/nitrogen/generated/ios/NitroBase64-Swift-Cxx-Bridge.hpp b/nitrogen/generated/ios/NitroBase64-Swift-Cxx-Bridge.hpp index 0868b04..c06a4d2 100644 --- a/nitrogen/generated/ios/NitroBase64-Swift-Cxx-Bridge.hpp +++ b/nitrogen/generated/ios/NitroBase64-Swift-Cxx-Bridge.hpp @@ -2,7 +2,7 @@ /// NitroBase64-Swift-Cxx-Bridge.hpp /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. /// https://github.com/mrousavy/nitro -/// Copyright © 2025 Marc Rousavy @ Margelo +/// Copyright © Marc Rousavy @ Margelo /// #pragma once diff --git a/nitrogen/generated/ios/NitroBase64-Swift-Cxx-Umbrella.hpp b/nitrogen/generated/ios/NitroBase64-Swift-Cxx-Umbrella.hpp index b38ad5e..e986634 100644 --- a/nitrogen/generated/ios/NitroBase64-Swift-Cxx-Umbrella.hpp +++ b/nitrogen/generated/ios/NitroBase64-Swift-Cxx-Umbrella.hpp @@ -2,7 +2,7 @@ /// NitroBase64-Swift-Cxx-Umbrella.hpp /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. /// https://github.com/mrousavy/nitro -/// Copyright © 2025 Marc Rousavy @ Margelo +/// Copyright © Marc Rousavy @ Margelo /// #pragma once diff --git a/nitrogen/generated/ios/NitroBase64Autolinking.mm b/nitrogen/generated/ios/NitroBase64Autolinking.mm index b703394..68c6450 100644 --- a/nitrogen/generated/ios/NitroBase64Autolinking.mm +++ b/nitrogen/generated/ios/NitroBase64Autolinking.mm @@ -2,7 +2,7 @@ /// NitroBase64Autolinking.mm /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. /// https://github.com/mrousavy/nitro -/// Copyright © 2025 Marc Rousavy @ Margelo +/// Copyright © Marc Rousavy @ Margelo /// #import diff --git a/nitrogen/generated/ios/NitroBase64Autolinking.swift b/nitrogen/generated/ios/NitroBase64Autolinking.swift index 3dfbabd..5f773f4 100644 --- a/nitrogen/generated/ios/NitroBase64Autolinking.swift +++ b/nitrogen/generated/ios/NitroBase64Autolinking.swift @@ -2,9 +2,13 @@ /// NitroBase64Autolinking.swift /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. /// https://github.com/mrousavy/nitro -/// Copyright © 2025 Marc Rousavy @ Margelo +/// Copyright © Marc Rousavy @ Margelo /// +import NitroModules + +// TODO: Use empty enums once Swift supports exporting them as namespaces +// See: https://github.com/swiftlang/swift/pull/83616 public final class NitroBase64Autolinking { public typealias bridge = margelo.nitro.nitrobase64.bridge.swift diff --git a/nitrogen/generated/shared/c++/HybridNitroBase64Spec.cpp b/nitrogen/generated/shared/c++/HybridNitroBase64Spec.cpp index f8c1765..6dbcbb9 100644 --- a/nitrogen/generated/shared/c++/HybridNitroBase64Spec.cpp +++ b/nitrogen/generated/shared/c++/HybridNitroBase64Spec.cpp @@ -2,7 +2,7 @@ /// HybridNitroBase64Spec.cpp /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. /// https://github.com/mrousavy/nitro -/// Copyright © 2025 Marc Rousavy @ Margelo +/// Copyright © Marc Rousavy @ Margelo /// #include "HybridNitroBase64Spec.hpp" @@ -17,6 +17,8 @@ namespace margelo::nitro::nitrobase64 { prototype.registerHybridMethod("install", &HybridNitroBase64Spec::install); prototype.registerHybridMethod("encode", &HybridNitroBase64Spec::encode); prototype.registerHybridMethod("decode", &HybridNitroBase64Spec::decode); + prototype.registerHybridMethod("decodeBuffer", &HybridNitroBase64Spec::decodeBuffer); + prototype.registerHybridMethod("encodeBuffer", &HybridNitroBase64Spec::encodeBuffer); }); } diff --git a/nitrogen/generated/shared/c++/HybridNitroBase64Spec.hpp b/nitrogen/generated/shared/c++/HybridNitroBase64Spec.hpp index a707954..a00b5ab 100644 --- a/nitrogen/generated/shared/c++/HybridNitroBase64Spec.hpp +++ b/nitrogen/generated/shared/c++/HybridNitroBase64Spec.hpp @@ -2,7 +2,7 @@ /// HybridNitroBase64Spec.hpp /// This file was generated by nitrogen. DO NOT MODIFY THIS FILE. /// https://github.com/mrousavy/nitro -/// Copyright © 2025 Marc Rousavy @ Margelo +/// Copyright © Marc Rousavy @ Margelo /// #pragma once @@ -16,6 +16,7 @@ #include +#include namespace margelo::nitro::nitrobase64 { @@ -50,7 +51,9 @@ namespace margelo::nitro::nitrobase64 { // Methods virtual void install() = 0; virtual std::string encode(const std::string& input, bool urlSafe) = 0; - virtual std::string decode(const std::string& base64, bool urlSafe) = 0; + virtual std::string decode(const std::string& base64) = 0; + virtual std::shared_ptr decodeBuffer(const std::string& base64) = 0; + virtual std::string encodeBuffer(const std::shared_ptr& input, bool urlSafe) = 0; protected: // Hybrid Setup diff --git a/package.json b/package.json index 4a568aa..55623a4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-nitro-base64", - "version": "0.8.2", + "version": "1.1.0", "description": "NitroModule Base64", "main": "./lib/module/index.js", "types": "./lib/typescript/src/index.d.ts", @@ -79,12 +79,12 @@ "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.4", "jest": "^29.7.0", - "nitrogen": "^0.29.8", + "nitrogen": "^0.35.6", "prettier": "^3.6.2", "react": "19.1.0", "react-native": "0.81.1", "react-native-builder-bob": "^0.40.13", - "react-native-nitro-modules": "^0.29.8", + "react-native-nitro-modules": "^0.35.6", "release-it": "^19.0.4", "turbo": "^2.5.6", "typescript": "^5.9.2" diff --git a/src/NitroBase64.nitro.ts b/src/NitroBase64.nitro.ts index 48f47d4..a509caf 100644 --- a/src/NitroBase64.nitro.ts +++ b/src/NitroBase64.nitro.ts @@ -4,7 +4,9 @@ export interface NitroBase64 extends HybridObject<{ ios: 'c++'; android: 'c++' }> { install(): void; encode(input: string, urlSafe: boolean): string; - decode(base64: string, urlSafe: boolean): string; + decode(base64: string): string; + decodeBuffer(base64: string): ArrayBuffer; + encodeBuffer(input: ArrayBuffer, urlSafe: boolean): string; } // export interface NitroBase64Module extends HybridObject<{ android: 'kotlin' }> { diff --git a/src/index.tsx b/src/index.tsx index f827427..ec45442 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -6,14 +6,33 @@ const NitroBase64HybridObject = // const NitroBase64ModuleHybridObject = // NitroModules.createHybridObject('NitroBase64Module'); -export function decode(base64: string, urlSafe = false): string { - return NitroBase64HybridObject.decode(base64, urlSafe); +export function decode(base64: string): string { + return NitroBase64HybridObject.decode(base64); } export function encode(input: string, urlSafe = false): string { return NitroBase64HybridObject.encode(input, urlSafe); } +export function decodeBuffer(base64: string): ArrayBuffer { + return NitroBase64HybridObject.decodeBuffer(base64); +} + +export function encodeBuffer(input: ArrayBuffer, urlSafe = false): string { + return NitroBase64HybridObject.encodeBuffer(input, urlSafe); +} + +export function fromByteArray(input: Uint8Array, urlSafe = false): string { + return NitroBase64HybridObject.encodeBuffer( + input.buffer as ArrayBuffer, + urlSafe + ); +} + +export function toByteArray(base64: string): Uint8Array { + return new Uint8Array(NitroBase64HybridObject.decodeBuffer(base64)); +} + export function install(): void { NitroBase64HybridObject.install(); } diff --git a/yarn.lock b/yarn.lock index 18046c3..ec1e0c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3793,7 +3793,7 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^6.1.0": +"ansi-styles@npm:^6.1.0, ansi-styles@npm:^6.2.1": version: 6.2.3 resolution: "ansi-styles@npm:6.2.3" checksum: f1b0829cf048cce870a305819f65ce2adcebc097b6d6479e12e955fd6225df9b9eb8b497083b764df796d94383ff20016cc4dbbae5b40f36138fb65a9d33c2e2 @@ -4584,6 +4584,17 @@ __metadata: languageName: node linkType: hard +"cliui@npm:^9.0.1": + version: 9.0.1 + resolution: "cliui@npm:9.0.1" + dependencies: + string-width: ^7.2.0 + strip-ansi: ^7.1.0 + wrap-ansi: ^9.0.0 + checksum: 143879ae462bf76822f341bf40979f0225fdba8dde6dfe429018b13396fd0532752cc2a809ac48cecc0ea189406184ad7568c0af44eea73d2ac3b432c4c6431f + languageName: node + linkType: hard + "clone@npm:^1.0.2": version: 1.0.4 resolution: "clone@npm:1.0.4" @@ -5390,6 +5401,13 @@ __metadata: languageName: node linkType: hard +"emoji-regex@npm:^10.3.0": + version: 10.6.0 + resolution: "emoji-regex@npm:10.6.0" + checksum: 8785f6a7ec4559c931bd6640f748fe23791f5af4c743b131d458c5551b4aa7da2a9cd882518723cb3859e8b0b59b0cc08f2ce0f8e65c61a026eed71c2dc407d5 + languageName: node + linkType: hard + "emoji-regex@npm:^8.0.0": version: 8.0.0 resolution: "emoji-regex@npm:8.0.0" @@ -6401,6 +6419,13 @@ __metadata: languageName: node linkType: hard +"get-east-asian-width@npm:^1.0.0": + version: 1.6.0 + resolution: "get-east-asian-width@npm:1.6.0" + checksum: 88faf98aaade2b24ad260be369b026d979ff4d0e6201bfd991654da17f2aa720bbddb812ce5612110eef6b469d8370c863f69a2f9818d61733f1eb2a83d779b8 + languageName: node + linkType: hard + "get-east-asian-width@npm:^1.3.0": version: 1.4.0 resolution: "get-east-asian-width@npm:1.4.0" @@ -9140,18 +9165,18 @@ __metadata: languageName: node linkType: hard -"nitrogen@npm:^0.29.8": - version: 0.29.8 - resolution: "nitrogen@npm:0.29.8" +"nitrogen@npm:^0.35.6": + version: 0.35.6 + resolution: "nitrogen@npm:0.35.6" dependencies: chalk: ^5.3.0 - react-native-nitro-modules: ^0.29.8 + react-native-nitro-modules: ^0.35.6 ts-morph: ^27.0.0 - yargs: ^17.7.2 + yargs: ^18.0.0 zod: ^4.0.5 bin: nitrogen: lib/index.js - checksum: ec56919ee570e1a9475ec95a90ed3779110bf9139811102d63d3b40a9b2747ab54962c081ff4f0cc5f0df83f06719f020d55489ce8d4f2a7a08ea1d0299a1977 + checksum: 105ff9cf455c3c12def88181e37d2b681b5b50dc891a725db9e36e16695809f9c80056b60bc94d19dfaf7bf71c77820660f7ac9f29f9e5b7ed3e5c2e0a5e8d8a languageName: node linkType: hard @@ -10120,8 +10145,8 @@ __metadata: react-native: 0.81.1 react-native-builder-bob: ^0.40.13 react-native-monorepo-config: ^0.1.9 - react-native-nitro-modules: ^0.29.8 - react-native-quick-base64: ^2.2.2 + react-native-nitro-modules: ^0.35.6 + react-native-quick-base64: ^3.0.0 react-native-safe-area-context: ^5.5.2 languageName: unknown linkType: soft @@ -10146,12 +10171,12 @@ __metadata: eslint-config-prettier: ^10.1.8 eslint-plugin-prettier: ^5.5.4 jest: ^29.7.0 - nitrogen: ^0.29.8 + nitrogen: ^0.35.6 prettier: ^3.6.2 react: 19.1.0 react-native: 0.81.1 react-native-builder-bob: ^0.40.13 - react-native-nitro-modules: ^0.29.8 + react-native-nitro-modules: ^0.35.6 release-it: ^19.0.4 turbo: ^2.5.6 typescript: ^5.9.2 @@ -10162,23 +10187,23 @@ __metadata: languageName: unknown linkType: soft -"react-native-nitro-modules@npm:^0.29.8": - version: 0.29.8 - resolution: "react-native-nitro-modules@npm:0.29.8" +"react-native-nitro-modules@npm:^0.35.6": + version: 0.35.6 + resolution: "react-native-nitro-modules@npm:0.35.6" peerDependencies: react: "*" react-native: "*" - checksum: 54e9ddb7d9c28f39000e9ec06c1ae59fde9dc53454bade6697ae842c4996becd388cdfa1bb0cb504a6ddc25873e1409533a141f94d2f36153bdbf4bf51cf97b1 + checksum: b057dc606c4717bff2447cff3efdeebf5cd7342fa11b3f51cfd420ccce1224d0b4be5999ea63c9856721520f1cbb50fba9372109f49a4c8b14202d7ab4d3b37e languageName: node linkType: hard -"react-native-quick-base64@npm:^2.2.2": - version: 2.2.2 - resolution: "react-native-quick-base64@npm:2.2.2" +"react-native-quick-base64@npm:^3.0.0": + version: 3.0.0 + resolution: "react-native-quick-base64@npm:3.0.0" peerDependencies: react: "*" react-native: "*" - checksum: 51cd8df0b7f78d4c10996f157927cc075b8a9e85f7d0e48790a4247a033ca335e440c96572969fb6f6b49135d51dbf87c6d659fba509b51b9237957d41a6c580 + checksum: b9d447a66178e8f72ae3a6d852056bb928256bb9c8703c0b3bcfb9338df12d89ff82617535114f3d04643c908aaf0b48d5c46b0f0fb281bab34855d95c23f448 languageName: node linkType: hard @@ -11098,6 +11123,17 @@ __metadata: languageName: node linkType: hard +"string-width@npm:^7.0.0, string-width@npm:^7.2.0": + version: 7.2.0 + resolution: "string-width@npm:7.2.0" + dependencies: + emoji-regex: ^10.3.0 + get-east-asian-width: ^1.0.0 + strip-ansi: ^7.1.0 + checksum: 42f9e82f61314904a81393f6ef75b832c39f39761797250de68c041d8ba4df2ef80db49ab6cd3a292923a6f0f409b8c9980d120f7d32c820b4a8a84a2598a295 + languageName: node + linkType: hard + "string-width@npm:^8.1.0": version: 8.1.0 resolution: "string-width@npm:8.1.0" @@ -12020,6 +12056,17 @@ __metadata: languageName: node linkType: hard +"wrap-ansi@npm:^9.0.0": + version: 9.0.2 + resolution: "wrap-ansi@npm:9.0.2" + dependencies: + ansi-styles: ^6.2.1 + string-width: ^7.0.0 + strip-ansi: ^7.1.0 + checksum: 9827bf8bbb341d2d15f26d8507d98ca2695279359073422fe089d374b30e233d24ab95beca55cf9ab8dcb89face00e919be4158af50d4b6d8eab5ef4ee399e0c + languageName: node + linkType: hard + "wrappy@npm:1": version: 1.0.2 resolution: "wrappy@npm:1.0.2" @@ -12131,6 +12178,13 @@ __metadata: languageName: node linkType: hard +"yargs-parser@npm:^22.0.0": + version: 22.0.0 + resolution: "yargs-parser@npm:22.0.0" + checksum: 55df0d94f3f9f933f1349f244ddf72a6978a9d5a972b69332965cdfd5ec849ff26386965512f4179065b0573cc6e8df33ca44334958a892c47fedae08a967c99 + languageName: node + linkType: hard + "yargs@npm:^15.1.0": version: 15.4.1 resolution: "yargs@npm:15.4.1" @@ -12150,7 +12204,7 @@ __metadata: languageName: node linkType: hard -"yargs@npm:^17.0.0, yargs@npm:^17.3.1, yargs@npm:^17.5.1, yargs@npm:^17.6.2, yargs@npm:^17.7.2": +"yargs@npm:^17.0.0, yargs@npm:^17.3.1, yargs@npm:^17.5.1, yargs@npm:^17.6.2": version: 17.7.2 resolution: "yargs@npm:17.7.2" dependencies: @@ -12165,6 +12219,20 @@ __metadata: languageName: node linkType: hard +"yargs@npm:^18.0.0": + version: 18.0.0 + resolution: "yargs@npm:18.0.0" + dependencies: + cliui: ^9.0.1 + escalade: ^3.1.1 + get-caller-file: ^2.0.5 + string-width: ^7.2.0 + y18n: ^5.0.5 + yargs-parser: ^22.0.0 + checksum: a7cf1b97cb4e81c059f78fd32a4160505d421ecdce5409f5e3840fdcc4c982885fc645b44af961eab94d673cb46f81207d831aa87862246907ffacf45884976a + languageName: node + linkType: hard + "yocto-queue@npm:^0.1.0": version: 0.1.0 resolution: "yocto-queue@npm:0.1.0" From 24228bcf044b325319e43533c479aa1aa6b65e24 Mon Sep 17 00:00:00 2001 From: Rom Date: Sun, 17 May 2026 14:53:25 +0700 Subject: [PATCH 2/3] feat: update README and project files for NitroBase64 v1.1.1 release --- README.md | 164 +++++++++++------- docs/android.png | Bin 86456 -> 314001 bytes docs/ios.png | Bin 121466 -> 286991 bytes example/index.js | 2 +- .../project.pbxproj | 58 +++---- example/ios/Podfile.lock | 4 +- package.json | 2 +- 7 files changed, 136 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index 660bcef..b847371 100644 --- a/README.md +++ b/README.md @@ -1,107 +1,149 @@ -# NitroBase64 +# react-native-nitro-base64 ⚡ -A high-performance, cross-platform Base64 encoding/decoding module for React Native, powered by C++ and simdutf. Implements WHATWG forgiving-base64 and supports both standard and URL-safe variants. +The fastest Base64 library for React Native — powered by [simdutf](https://github.com/simdutf/simdutf) SIMD C++ and [Nitro Modules](https://github.com/mrousavy/nitro). -| iPhone | Android | -| ------------------------------------------------- | --------------------------------------------------- | +Processes a 1.3MB image **26x faster** than `atob`/`btoa` and **1.8x faster** than `react-native-quick-base64`. + +| iPhone | Android | +| --- | --- | | ![iPhone](./docs/ios.png) | ![Android](./docs/android.png) | -## Features -- Fast C++ implementation using [simdutf](https://github.com/simdutf/simdutf) -- WHATWG forgiving-base64 compliance (removes whitespace, optional padding) -- Supports both standard and URL-safe base64 -- Consistent API for iOS and Android -- Detailed error handling -## Installation +--- + +## 🤔 Why this library? + +Most React Native base64 libraries hit the same bottlenecks: + +- 🐢 **Pure JS** (`base64-js`) — slow. No native acceleration. +- ⚠️ **`atob`/`btoa`** — fast for tiny strings, falls apart on binary data at scale. +- ⚠️ **String-based native** — crosses the JSI bridge as UTF-16, doubling memory for binary payloads. + +`react-native-nitro-base64` uses SIMD instructions (AVX-512 on x86, NEON on Apple Silicon) to process 64 bytes per CPU instruction, and exposes `ArrayBuffer`/`Uint8Array` APIs that bypass string marshaling entirely. + +### 📊 Benchmark — 30 iterations, iPhone (Apple Silicon) + +> Tested against `react-native-quick-base64@3.0.0` + + +#### Small image (7KB) + +| Library | Time | vs. nitro-base64 | +|---|---|---| +| ⚡ **nitro-base64** `decodeBuffer` / `fromByteArray` | **0.06ms** | — | +| react-native-quick-base64 | 0.11ms | 1.8x slower | +| atob / btoa (Hermes) | 1.04ms | 17x slower | + +#### Large image (1.3MB) + +| Library | Time | vs. nitro-base64 | +|---|---|---| +| ⚡ **nitro-base64** `decodeBuffer` / `fromByteArray` | **7.5ms** | — | +| react-native-quick-base64 | 13.6ms | 1.8x slower | +| atob / btoa (Hermes) | 203ms | 26x slower | + +--- + +## 📦 Installation ```sh yarn add react-native-nitro-base64 yarn add react-native-nitro-modules ``` -### Linking -This module uses [react-native-nitro-modules](https://github.com/mrousavy/nitro). Follow Nitro's setup instructions for autolinking and native builds. +Then follow [Nitro's setup guide](https://nitro.margelo.com) for autolinking. -## API +### 🔧 Setup -| Function | Input | Output | Use case | -|---|---|---|---| -| `encode(str, urlSafe?)` | binary string | base64 string | Text/JWT encoding | -| `decode(base64)` | base64 string | binary string | Text/JWT decoding — accepts standard and URL-safe automatically | -| `encodeBuffer(ArrayBuffer, urlSafe?)` | ArrayBuffer | base64 string | Image/file → base64 | -| `decodeBuffer(base64)` | base64 string | ArrayBuffer | base64 → raw binary — accepts standard and URL-safe automatically | -| `fromByteArray(Uint8Array, urlSafe?)` | Uint8Array | base64 string | Crypto/hashing output → base64 | -| `toByteArray(base64)` | base64 string | Uint8Array | base64 → typed binary — accepts standard and URL-safe automatically | +Call `install()` once before your app mounts: -### Which one should I use? +```js +// index.js +import { install } from 'react-native-nitro-base64'; +import { AppRegistry } from 'react-native'; +import App from './src/App'; +import { name as appName } from './app.json'; + +install(); +AppRegistry.registerComponent(appName, () => App); +``` + +--- + +## 📖 API + +| Function | Input → Output | Notes | +|---|---|---| +| `encode(str, urlSafe?)` | string → base64 string | URL-safe optional | +| `decode(base64)` | base64 string → string | Auto-detects standard & URL-safe | +| `encodeBuffer(ArrayBuffer, urlSafe?)` | ArrayBuffer → base64 string | 🚀 Zero-copy, fastest for images | +| `decodeBuffer(base64)` | base64 string → ArrayBuffer | 🚀 Zero-copy, fastest for images | +| `fromByteArray(Uint8Array, urlSafe?)` | Uint8Array → base64 string | Drop-in for `react-native-quick-base64` | +| `toByteArray(base64)` | base64 string → Uint8Array | Drop-in for `react-native-quick-base64` | + +--- + +## 🚀 Usage -**Working with images?** Use `encodeBuffer` / `decodeBuffer`. +### 🖼️ Images & files — `encodeBuffer` / `decodeBuffer` -Images are binary data. Passing them through a JS string crosses the JSI bridge as UTF-16, doubling memory and adding overhead. `ArrayBuffer` avoids that — raw bytes go directly to C++. +Images are binary data. Sending binary through a JS string crosses the JSI bridge as UTF-16, doubling memory. `ArrayBuffer` skips that entirely — raw bytes go straight to C++. -```typescript +```ts import { encodeBuffer, decodeBuffer } from 'react-native-nitro-base64'; -import { readFile } from 'react-native-fs'; -// Image file → base64 (for API upload) -const bytes = await readFile(imagePath, 'ascii'); // or use arrayBuffer APIs -const base64 = encodeBuffer(buffer); // ArrayBuffer → base64 +// Upload image to API +const response = await fetch(imageUri); +const buffer = await response.arrayBuffer(); +const base64 = encodeBuffer(buffer); // → "iVBORw0KGgo..." -// base64 from API → display in -const buffer = decodeBuffer(base64String); // base64 → ArrayBuffer +// Decode API response for component +const buffer = decodeBuffer(base64String); // → ArrayBuffer ``` -**Working with text / JWT?** Use `encode` / `decode`. +### 🔑 Text & JWT — `encode` / `decode` -```typescript +```ts import { encode, decode } from 'react-native-nitro-base64'; -const encoded = encode('Hello World!'); // "SGVsbG8gV29ybGQh" -const decoded = decode(encoded); // "Hello World!" +const encoded = encode('Hello World!'); // "SGVsbG8gV29ybGQh" +const decoded = decode(encoded); // "Hello World!" -// URL-safe (JWT, URL params) +// URL-safe for JWT / URL params const token = encode(payload, true); ``` -**Working with Web Crypto / hashing?** Use `fromByteArray` / `toByteArray`. +### 🔐 Web Crypto & hashing — `fromByteArray` / `toByteArray` -Web Crypto APIs (`crypto.subtle`) return `Uint8Array`. These are drop-in compatible with `react-native-quick-base64`. +Drop-in replacement for `react-native-quick-base64`. Same API, faster engine. -```typescript +```ts import { fromByteArray, toByteArray } from 'react-native-nitro-base64'; -// Hash → base64 +// SHA-256 hash → base64 const hash = new Uint8Array(await crypto.subtle.digest('SHA-256', data)); const encoded = fromByteArray(hash); -// base64 → bytes for TextDecoder +// base64 → text const bytes = toByteArray(base64String); const text = new TextDecoder().decode(bytes); ``` -## Setup +--- -```javascript -// index.js -import { install } from 'react-native-nitro-base64'; -import { AppRegistry } from 'react-native'; -import { name as appName } from './app.json'; -import App from './src/App'; +## 📱 Platform Support -install(); // call before app mounts -AppRegistry.registerComponent(appName, () => App); -``` +| Platform | Engine | +|---|---| +| iOS | C++ (simdutf, NEON SIMD) | +| Android | C++ (simdutf, NEON / AVX2 SIMD) | -## Platform Support -- **iOS:** C++ implementation via simdutf -- **Android:** C++ implementation via simdutf +--- -## Error Handling -Throws descriptive errors for invalid base64 input, remainder issues, or extra bits in padding. +## 📄 License -## License MIT -## Credits -- [simdutf](https://github.com/simdutf/simdutf) -- [Nitro Modules](https://github.com/mrousavy/nitro) +## 🙏 Credits + +- [simdutf](https://github.com/simdutf/simdutf) — SIMD-accelerated Unicode & Base64 +- [Nitro Modules](https://github.com/mrousavy/nitro) — JSI native module framework diff --git a/docs/android.png b/docs/android.png index 120011b8c06b86ab19ca57331c712e9f7bdc9347..2cb121bbeda4aaaba8bcf62a8c5048f1d57b1dc8 100644 GIT binary patch literal 314001 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

Q+Q-r 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 literal 86456 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

0w4W?XD>j+(;0+<<-f_l`{<+h^b9=TMbQU-aFkJ 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 boe5U%U#ll{d^-IX@N>cR%Gu&Gw<7)z5KtRO diff --git a/docs/ios.png b/docs/ios.png index 0bc2da2e8c604578853f5465fd812d74adfeccd2..501e167bd57a8935d5f6e73af19b61d3a2dd3188 100644 GIT binary patch literal 286991 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 z z?hV^ywE`+a)kCj2J5MCElhQEGxAmg}WQ-$pjsI@8oCR)vPH6W(EQz=yeEw_`{Cni8 zXni*!8$~26F~7IhKjWIZ3k(V;cXDo!cwcIwar$AnN$ho7w0k_LeIuj~p=_zP%x;?X z!q)PU{Q&=n@*aQwwBv$}Z==nvX!4oL1mt`Nh3kkvx&iLaD(vv4mGXlcXN#k>$;s)^ z5pioZ(8$sTNv!7dl#5;BIFHWkX%?)~X>Ppnh>2f}z~lbCp5%{7T^3Uf6j8@tvJdS_ z^45DQ&fNKJZtVVB5GTIAi#bh5kXjfaOOE=+%VF?pv6-DCHRf$$LAQ2+CZ<9$QL&~J zX@M-1HBA#U>D6MGW^y9W#(mxucsR#>()Xl&=_)XH?qIcX;`W7rJ5a(Wli$$rm>}kq zZz2!8iKaW^m%tt_P$2meRVPD)Nm@`MT&;c;MM)17T}_lrrUP?~d7&63FKm^$Uw+x0GosP^3Z*QkmW;>31Pp36|2f`ksgPwP9F z8;#H`i>8RtTe~6C$qz^<2IiA2&~j9977bQ$WK2*>KTU2vBsNDsz z>MGzl;0vd~@?&=hP+MHdRS8j}f;Yb-v&?S?vVU~>ocqphj_bn7?eO+h>pPyGmdcP$ z6>&`aYT9%;=M#m9y;ebvzZ!pHZ-HhD>VoKQn(z5!R+v$(e`foW^tH$qiG3~;@$MrZ z{pwBGXGkPbw%~=gLH&?g$UN`lRtrp}efsQa{T~9lG=asi1Lpbcv;l ze`6C{yIi(|=2S(iXeX!wrC{GpUmI=o3)xX-U5gQ7KCDjZauh+fYN?u21KE4^@+^Oc zAX3PW+Ky1&dKvJ4BwE@S3B%fO5*e-3B>T>%TC_P>G}6_YpGe19>qAw{S#VTky>Cz? zmcd6qH&YZ7Wk||NV(yIbint55C@Z1GvnWx)xzX+VNTlP3ospN34s9%XE66u9A6u5~ zZ+tI{aa58h>=_k>!86*jazRvgh8s`R>23pf4o|< z8JiXw>7Bg;9mEBy=%yC0Tt4vX5blY0HGXT2i8oixbFefu*AHmH){#fD0L?@6P9Lf! zD}J!tc+qMT8nC@rtzsOwv*>2X630}w7&H7MNN5i^eIvOFCBRkq2&yHEYpq@5StgiZk9YAC&SYi0GTDX?Gl8b! zW0O@I%^sm0ohJ_=3ewwx`;($fn2<{x+W0> z>7-+9lR};f;2)w!7fLtDc`C-v(hy83YSUJsWp-n@;7A%rGGn~nl1S?B-+FFOaLuVx6i;jBMO|eaV+LN@a`BMx<&|(`n}yu zV*B|L&U}f5UZXrUAtXDBS1#CfY@VMf#?(we>?B!B6!mQnmbX#T5s0a z#qaw|mayOUWS*BvV&*E(_bU&x8vRxklSxQd^o1DGe>}g0$8j>RM1<)}$uBdg#=d7s zu2m*`wtQ}BD4Ec5x>a9Ld5Sx}ZDiZ{^oB&(aDdnN8F26Z#+*|J_;60$kd~|eYUn*mfa*8 z!FP??g+7!l=t%5<9L6oB@@(Ja{exsgCL%=c06pl9Nm?~jY<##cMd;~T9yBS$ zLGw>we}0F}AOC0QpQ0x>`ai7Q> z?~-4a_5J)lezqrIC)13#gCADPN2URmS|m^@0J^Kh;zA@}w3H#CWtG)!n=xq$`G3Iz zq1nb{-Ls)sjw;-Taummf5s7*)-~;aQ?2y%0A+P8Q<`f1a>C*b7RX_)lhx53V=h9z( zGJ-8(R#~YQ+vqV8<>E{mGU3D+I!W0VT_43LjKBo?Pr@Y1@y8V;L&~1PsBvh}KK+Td zXJ@(wZIC+a(P^MD=SeHk`B;^<$jf#(#h)@|VeL1SDKwQc!|Llbk-u>47UGNW$yW)F z!psOP@i#ierazm53l%5k<6{_Ak`R#O$yhIWvl9H~kAlfc47b=SO%gm7bcz~oO5K!V z+o@VzOGo}~pgC@gas3aYscr${3>yRR#ix$fxr(yM){CLds9=Liv9;Z%Nxt1IY zP6Zu!OFcMzQ*zG*zLDUDA0bWryn@!kQAox?5a#{i-Xh^GoX>F;?AhB?oG_sMk;LLK z*gQ(>hq>v%n&as(20gX%ETKm|lgDqe5f3^NKYRGYynN^jO+{F4NVk?FIs~6mggE-j z;;Yglfpb@+z)sCrz%0Q@F#Wq7)yYgcf*8zZTch`bcftHAw(#WbC+K10EEaWD*GYr# zmDZfbFjhb`ZnL?I!>qM>;D8K6r^3q|iBx+f%(?^QGadrrjC~4PMsDol4%$-!Poj90 zUzSkR?(<8xz+EuQG%}@YFg-fKPh=>#Tp`<{A1WGu_+=x60x3P7$Sk4qUB80=XOh(B z69%xkPXvn+H*E^ORK(ejZS7n5iIM3lsp9)8TcX0ok_vX&z;nf|H^7_Q%W7BV`J|MP?#u2idh*P5NejQGB9lP!@d3!*1{9H-R~9RODF%8Igz$nYnl7WfK4N z7HpMDxO5Q|I%QAsZ=4Y*nFyAUUCLM>e4s1%wxR??rr_`A!n6+qgnt?BzS__&GML>? zXGkz(bFlW1J3blIY-HyCq26e*`nr4m(54z}zw@>2j@yYe!d`-&o^xI9l?0saEo$@* z8LEY&>eQWlGbc#9K8q+N9l97@$tBWhA6zc2kCPft>fWzc)+F80dTq0w@Q zj&e+RSTD3`hE=;9MrXnjyP_y_%f1Q0ngpTL(Z$k-1!RP=t@G=GN9k7(Oc*zvPLZ_e zv$wBQx-3Som~)3LMvMonWEb)K8}W%V{r|y2f)}6cTSDBiAs*V8`*60#C`=+sFuO&w zUfwC<@20|qv5n(UK+*I4`@z!jPzxO@)x4=va96CAJ@G(i+0!~^I2tZX`KF|11{?J= zH167ieg#`hXV4%sWDzX(wDKl+BglvpSi|M7FDHRE1sV`q`N-nS* zgZsfHGRj4|gj;k@Nq=>a?A0-o0>b!-_oSDRPl#^8FX#R3WF5Ktdh_Hxg=C+Id@zt6_`e(;~7l-wuW(1qDvl#tX~ zk8Y~G&Kv4|iL(pMtWDoA@REYF-Kbu4c>7A;U@DWkovmpY_IDLYbH7Z%s1Sw0;#CXm z^8HOJjSIbW=nuaS?k6bA3SNREwAD>HD{74A6EMYHJz$oK8YcR=#B@Tf(vNnrdTh_< zoDyc^pl5tclcqX$`NzHT__92hJ51Az;1F}c<}}`s*HMDjuM(wHP(AGk&m%*!;GeHg z%of7PHQYV7D=@LBXHAmWai(#=I0SQw0gX}WTW}o{ji=>WjXbYxDXumZ-g67_!@H1a zpz-jME!#jniFvt$j>C(TI(1CDf#34o2oudit-3D)N~o*Obje!tlc7mLEPTXHpp|); zqb!&nHEHs|^s2*k!97eSSYA(kWbj8UW!faT)0b)6nY(R0B)(^w50~mQ=UU4awWg7> zs~N#NMsf zhUFxD9V3+Hr%Z744>k7}VeBzTVTTQah>t?T*)^qAF%?mT%k=%3NhiFz_K@Z&7hB-Q zc^gP)5V`y)cGAV zOP@&44eBDds_?q(%RU^HEa&4e?*UN^B$+$!)UvEJyqd!|?=&REqSe|H9`a(AG{@(% zxvNPI{1w#ohGlL-mj zDdgH}q7CA6R&yLhkuR(A9u93OE%#M|t^uBx8vW%kp!oX!TIrLRw!+PypqVgX5`^L4 zNa;{FlQ`+Td4;7tA$dO;DqJ135uA9&Y2g^mEKDWq_lY-!fs|r20I2+j%}ftDP?^6{ zGoPQr%T{02P)HGd2!nZLubfC9vRKrjlhz^Xyuvwo!?ZBS{>h|~>1`>KJl4M5QghuA z?vSl)&zK@>(!Lx#I1dZ1n-yW}RYjXbENjnxKiNxqmcY3!>S-r9uCi^i@HE5DQF-Zn zeG{1qltNCG!-nQGFbV0fXp_A&B}{{znWk$v!ly}jayE`yga`H?mlbK*o)x60Ab=5v z-dZRnxd^ObY51XoKh@1!#QIQY>D0qyzvXExWeZx+MUXhG-e~B&fSnBx6n6{XwD7a_ z)_~meT8L;Gf7nbt$#)s3+r0}#SECHZttbMj`?a>tLK?MvuN^dxwakz&ky655qnHtF zDm=)+drvfdqEuow712&1Wj1Kd}Tv5sEOm^mg&Dc@;gX@NLHk z7=JKsGE;T5KixoA#MGAvl9S?cyC7J)lATwqbYmvac*NN(Nf?z$>^gCtjP5bgVjVwQ zEHb_eIY+rnZ1He1YLU>2z2^Q&5j23Ox+K_%vz8v6iJxa16VU+U3yP-qZ2Ojd5_3Hd za}OS(i%psTB1ZqBi1r$C8a(pkhd+A~a3Zz6{N1IzU%_rV)y>2mLt&&^d?`z(+|;}N zyk&T_OQYXE%4>q)d#&YRxI{#NRTAB2E*2m80i`D+A^QE{Ch}e0q|IY(g52Q5=N9kC z4*42FN702p$I=bUHH+wK$pUPP?%@q+>{$!Uap)#t-(P==+LCNsnj(P!r-}*r11Rcc z>XO=}2xI4X>t%;yh_C3xMhX@(=H?T92J!~pBQ`tC@P)ES{*p6XYq`*N=*JEV1HxFO z2TQ^tQA?`qhp4R(##k{jeF)@lzqG;6&&bg&+l{VD!6e1uS=8+ya6k|Xv;NMJl*Ti% zNrVNvMNGq|h@T?x940pUrVlKV3)0NEh@bw23=|}D#_n-f7vr4+8n0;OmX&Bu>EY;( z@CJH8%E)?;D_)N4D0@Mr?+}x;Xo4t(Z3#qT%Av(7TY1!Co-{$ z6O0pjteyG7OQ5%5eZFff0p%aZKB5mf?Tqe{XDlW~>|;e4kqfqVd_X^UPAJ6V3uEN4nonCNY&k9pw1ad9NZ!)Gxq7rj^-vuIMKH0MQ%@gR=q$dBQ@L-Ag8LQeajptn z=$-Q1D(#o??1q$(W~;ROa~^atTi#-MMCgwFELp&xsG0hWXHr_*uMP|6Dt;n^)wWXN zRY$S6mgG5bZPCnOh=S}tDA28>9@VUv5Z0E$?1;wTaxgsLdK&zPQ5jCDNl~+wExZ7o z!AFqM9-;kU$6lQP936k`+hVA`p81+FDM?fei=~@?#;PS{X?ls?NEt@GR_Zkf_zJ{nZnK1W$b=nkVplN8(GcBCs!Blc7Y zZJ=m9lJPZx=s^|zlrVN&YAG;Cn`#na5DkSQ6C5Tv3L&b~f-1PB{BM@upn;qcS}@h% z3p$x|k7}j`_X@*K z?2YMeyP!%|XS>Cl6|UcI+&MlBY@Ht1l<0%xS&xYSc`$ z@@Y5qW0Sl%Qrh$(i%?~Jm()}&$certuQUGPa$TokOCi~`y}Q*g?)SX>X@%vs&qAtP z$V?OPbnJ|bEYIpo`IqIJ-RtT;z+d5MVLm2sh4u;rM{3J-n-d+yHjMD)M0)u8Iicm&7&(x~(+yJ9MD0u+F) zF(NU&g%oS^N5vB}d3&)w9)tS0gw6QKSz(f^Zf_jB6l7h$1s5STmooO5<)M5sMsusM zrrpgMBQ$9C$#jhWNg{eZZnS^h;z66#cMNBW{7S+48xp?(o-s)mTS2@?PRF#yXX7*s z5ERDxutBnODMzOeKE&CC+#)u#CJ-DyCUEFpMC;Z(e^9amhuSPBDuaGnb7*MkiuUlueJr3lxOi{jilgPa6p!V zrb^$(cmA~#K9Yfw55jh;@AS2GEkiGybYQY8QREax{?v5|dV>s>ZWICvkXLI>M_?b@ zL(H(zeir@~MMw|CAhMlc_V{c+3SwCqH+3C!#3Inb&fMaz?p3YngX>}EE?FNmeE9{-sEU8QS_Z$sI=)+dLwH=9!o41yN<1f& zq6<$SeoOH>axnIw;~kiAV97esQy!HlTlmylGeglkUXTE|@+QJZmh!MBOCBf$n#-`i zQr&(Mr6X=_P>r6aW(uIU(a-jLK7g|$HRycFH?GBr-3cwwh;so~p;}^dC)VWu3;XB_ z{`7FwcpZIby3EFgWt*me34e}KWNB`HDz{nvbz!J%wAeo^hya0yo~UbWnJ_`YQUi=o z@W1Tr?^)T+vL4(vlIzU{9E<_U8Jirj)M^C~tNGL{{;yd@N+w%RAr%5L1*N=_*nDI} z;4&~b#YxWG5bos0{uU`8!Dh$d*WqNmjRp32io!u}TjG#R0%|r}a@NlR#IFY!Bv-6p zum27ZkqY}+Qhx6@XAzq98o(79PleQ&VG8^rEG&WGQP4WcQS?|P(PL`tO7!K+sB|Ck z)kegy=;tUd+n)QhbjQzFxUDp;C8-@gCGwa8dZP=S{zyusBR zU~$5NR_Gt0LwOn}rc-c`1q&>ggElhDI|C0D%<*)nQ^LO`h3y0@PH`^@XGIu#aAjin z|L%Z2o73)Dy7fB*_}s?alL!#3=!uDs&80850OW>Gx~ry1cn|@*;uEMdO*l9{YKQkf zQWu>HhopR@!TQR~q*GzPQ=bkLVhzQdgX63clhaqj$o(djp88eHFhG>s@VWCI!KWSkU(OLo$l>fmUXnTyrW+v1)cG4Z6RB{wX#nehu}S>BfZyARULU^@-T=;w!Q# zE8n;f;<@muVnXBeH{qgyfGj{wo1z2^UHW@wDqep=P#P>|t4AWx;_Y?#PKed>yQS=q zkja0fs|25Fc_Aj6794qgj7%N*a(UU4{1=D+Rq^ zT-872XCp$DmBwt6c>;U(BYgfZ(F6?Laac!C0TXfO3a2TSS{hMR(X42|X!)f&Fzj}^ zqbc8qri$tCM~`SJj`#Wg$p+6*5uWcE#UK|G))wnv5!Uqk(;drcoZ{{EU`iIXmuc#M z`SH1ZYD1BA94h3Fp}Q$Tkh5l3e>J^nxAcGGGDjvUe!5PrLGFS4!(5}zx9xebmQ zbH~`d(f4r%>8BlvKFXa@e*mF@pi}?G%aZuB%i@Whk)l8Rds*^k7M!s+=9vy%n}{qR z{?djLr=X99Mf0hIvv1Xpvak@lx6(e_dm%CsGk92A?4xcgVW@MNnJaJa{0$E(av!lr zN{Fw-NFVRCBu?kLd0pb1b^l9As-k@316}Apfbg3Y?Sh1IZ3&+?yh45Oofp6=Rq?6l zV&W%5dWeuN$@1mhJkOMiP?5MTf>fUM`PMi!GWB)Z=dkUzP)-q(;eYh!lhtpWoF^{X z!*xf=Es^uxneGDH13}>6ZJZP^MX5YAKD&J>^d}R-KAMhj#_wMmwUxPOoa8{NX#Uy0 zb2z6SB#-lhiNeT$9@bmM7?c(Lc9a0K=G6e|)2pVP?%H96t_6cv09Cp1eaJYfz~F{H za-QGCiqh*_m{4KiOB^DB)FYGXtoLzhbJ~Ucd-$XCo6U$K1CT001^mVwG-y?k9_=#$ zb&aK)o6`RASP{%k(nQWPp<>^Osv0l+fDq5XZzV=DTvj~hF0Fe#jsZ8sBt)tX$hxL| z^1Q9e##M-z*BHV60hCkzg|*EU|2P`U)-zs$83M-BZ`z=eC^7>cC^{R*J?^&+F*77O zIWfRDY@`X0IoKdc5B$(YaLZf28^~LL%5io)0Fylp?Mr3TGEh-z z5Y+P84;_1t_4$fnod(j`(4>qtM&Ul0Mo<7AFAJ#JbXMk05N zSe-|wgBk8*>m}342&cS3oENsawe?6UsOjc5All)r4#=%1It$UL0~`%Nhw_nrG!rdv zLRjSdYaI!f-ZQ@1P$3;Hr7azMfqfEAZYl~{(ugh}vLf4*5J`BCrKxD0T5CpX87nw$ z;#uOnPA{M}dbW2J*yFGK6mcC;f>V;Ue=rHLkw>pGSM1N>WO6N54XfM0lxBE(=8EmcH~M=9KZ+~y@*bXwl<6I|sb8$_rw_np=U zS1{ogI?}RC&0oIZDN>G>(TXJDRsS<-M{JWgMK1?Peu_!NCV=ZzX`6%(O17Z)Zzhh7ojMq$sgY!EXVWTfA4Na^ zOnxmpoyoifH9g~tk8L7_b0gYMAFh`rh*-fRF;SA_kwV#zcel0J&oZge5LUGZ3%cn& zk-v4rt?E6lIU+8m7)wC{L{Sgw^IsTfk%CG8IW_D1q3EQNT_s|KS~Qn(BFSJ$ zhO(@++!OZ!6k0Z~W6IJ70XuV^R>vG(&YYd?20|?^E0t`4Y=gM-d|q&m$_FWysXez@ zc(okf3zq86g-(ZXld@|-`*MjAnoS;`k#yUQ$m zv|^ZYoU5XXfPQYP>OJp&uW4Z5)PsWH!%O4-R9IWx6xt$w`n)GPT(i%}4*~PThPyue zz;%@nu4U)lXs-e7!vkGOjwv!$rmtEIL6(v^zXna zC7|Pz^Cs_~mpqN>uWg!s^_4>Vkrj_UMMWQnwF__GgLCXGC6>P>MGhry0eYyzKme>S zo}$f;L%b#k9JO{lI&aF89k6;gksl3L9H~P3?fpV07bC%{YMb`2|?-~n)0ahocJ43@6f z5JEOQ^TZQ9kcqybRSM$0joOI#=Dg>UlgY;^*OaVx$O|u|{Ck@*-Yux`S9Dt{B*7b( zp%f$3qHYQH9=<@f@+)o&;M?)oMqLstnqRXGMsnj@9EN8806au*e0m!`A zzc;x+D%uY}zP`1g#k!a4Q@Kg|BWyz8_ggvcrcNd;vuSvPI8Fyi`{bN_=Z@jY{(ufdliJslOwU;cVmZcExpN`q1Cbn1wb3 z1tyvGABHK0CRqpq)`XxhZCjR>AEB}1c(4LlVrZUl^O5~-M7~#h`oSiM z^5*KP{Mqbm#T_R%&THW$1{RqKPiwP1_T>Mu$=F~8PsJ{c6LnNnKK`ruD?)H27M{>+ z%yPm;-0jIrHuNxQGL)h`G_6=Rz;01XSbBi#TsV`zIq^AdafROi6DQ(-d>V)dxM6|L zn#gc^H%s~O7In;<} z+E;*?%Yry&Z?0}WU=h;Amu7NUwlR(tQtoKVCYLPsA0O~v2j46M15y`P+L!xr+d$}` zs{^jQcrO9tbIOragCY6LTqkrO|H5>_ad}z$q1*P)DzM9NFw777PB@sRn6|!H0K{-B zfTN2Z{Zp3#8QeBis^?t)YX=H8PylAiAmF^B^>JkKQBbL!nv#qF?VJA>K$$pf;=K`$ zYrzpkfS^VHjd%amt7j%)Un_6FavIPbv_EQV{g(ks!-5lFXB<#msQf<$1b9|D;Y!lH zAz#4mXY&}aX;-Q2S$;QGyemeC3jACuqwD?YaOb1wLMm{6;9`M&0D846i8|@?)qPp) zplSH&?SQvr_%Qvbr@dI}8s)6K$cArXJ&yH%4wbnLp|`SyU}Ll1 z5OCf3kD%edUg-bzml86Ra;7&&m0ErOZ$ATG0;W-D>BOv={P8LO?;jD?JIlm?{4v`j zMr!>3^xu!)-53`{Vbu8WSI6(aAMgM2Q7Sx-@y!2zkpG%JY0UpUBLCOr_PwcKPA)eecZ;KZ?-eM@IPD+h>!gPU;>s|KLEFhh5(*$VjNw0{5|Gydr=KsR3>!?N7MHoFcnvM(>j;JpIcb$0-OLagA&M~|nMf9exd zYdxlMQP-bqodK4PolC&yI}lqF`aNp4L-8f&`xrvJ`I>^C(ffZBpa*g}A6fyxlgH@K zDjt1>pwE9VYmZ-^_#}t}=tUZpE+whksbfIH-6{29aVB?1@@7P>W3KzygOuZbcM2df ztp5O<1s7d!o~!{*aw~vDH}`Y(0Szi3>#H>aaLCI5=wo-8co*;t!uJ9I5|_(!aX>(H z{#Cp)@9#zFF(B%Hk00<$ZrF%oTT&2x(a8^|n&Oo{W-7DmIAplZ_T_=WUc$|MFJLd)2_Y@=pBkd;ZTZJ04njjCPmLz*1QPwk?-O8 zSTJ7UGCh5M1-F$L=-#3|aaB&MyUx}2z3N;8bl1+}t$<%bhl~Ff^)aw^LOX$6$TGma zJfx%sLwSK&501fS4)IU?y8u|xVZ3^sR7n^F`wcJ;#=C#A@|EMgRfAbM13}7hoQr>< ztxW)+aDEREcvIy16TKd7TslR%_f(I&PXcO}TnBJzRqEV`w+m=#0seyid}ll(W}1}eKt-yB zoXa$_y=2VMkSG3k0+;cF6lm^co)x22jONo=Jv_AlNNj{Lk|y~P_CSZKb?^$naO6dY zZe=;tKvlL`41i@#l=5|tgMKY?SMsWT1h92Xq988X^_ihYQa~NAfZt0kFqr~v$Uh87iwXowZ|K-hmEUuwyZ8n7ON(EHdHto zKr|>bBfmfP`b=%Bk$Ocym^i2_axYWN|BUb&VAz}}^vZoVy{ym+*P<KSrqW5tdr9${bKV?EALi&SR~~123S&#W;*S_w{(+GSGWNL92GM= zc1;2FL~~#r|J`P-${O@Oaxgo9%lsVBE2nh=j809P@ykoxrU4=KuXw4y-d?>Z&)R@S zo$@;Nc??c$AVn%QV zfjJH4K?r`}?@;%0`-<0LK%hxT(HJ<&V!l_L@#e#P9>O|Cha~rUkS+cwwDxMchtw2s z(0UBNF9_{s05Yx9oji#gCQei@;0?K_(mz!C4qztVh5rbGa&{0FjECH6TTuvt4fCKy=*s<7sFV6wd`HF1QrZ z5=XKGK#{(&zrhUCaBuWWeYKR&kCCzoAb4B>SW*4JIW10v_g9!_Lf5m=$8B~smRWIj z2|NSNibwwo2%&e1lM{D7)$sXF#df9QQA?F&7IeI@3%h#Jvj=93yyS_j58ZEDSfez$ z?926(UaIDLw%-VLCO3Th>CI>?6_e9uTCKmewQrN3a1Six>1Ox0NOOw8LBGP4`Y8%b zTTYX-7exy@qN)$;fFW<1I)_U$gTgpLVAiMi=DOW&{Vxm&rwSu@2S9D163Au8Jk4PA(wMS4Be)w+2b^`|rSB9-wsa8v%sr+zEo z{6~J)oJMg`m0My6MWyZQOxdVV`3Y;naFKB>z_i@!bg+2)g92gay?U9iXf;8m3hI8e zc}+o$8-;+-$B%p$K`Jk8REpJGWb^&dK~vsckZ__~W8(fc;jO@Iw?#*`TTybr=ob_V zLT*jyYKxI2)cYckCKL%GMh+uebXTGYdC*f5!C2;9i(;&^9TdCbi8WV z!3p}^Hxxm;ly68vLitj59JkTYOBst`rbIJM9{eH%iJS$^j!6Vxm@+uNz^;*p zpa1=J1`4(Bfn0`ep{N@JoSZqc86bb&E+O-Ozj;&Bw)vb77CeaB5)W4X3`7&RYXJZ3 zn6)}lv>jmE&`yINO%QS%C)<5c{t$^4#h#NOX^I+Zy3Stl83Zq`ux1!&j3dmdTl*;Zmy$jwcibb6!K5ADNxQL_tmN3`p2-H_2Y_P*{Euual{>@ z<^P0H{u0t4<`l**f=zKN=oIzGQT(QQNg!qY^TUx#&+SF1KKe%mT&J9#5vv%5{G)lB z{E8U0cE{2&wH68~0`XnFVmdidBpQ_t z2XE^?geXgX=Eu)guyaiqBFYzU1UMTaJl`yO zhXU1I?zf|`1FVxi8dswHSBKDqHj7}+Qho+vI8QSO?yy44HpnAJb=4!tL&xv~E?Rjc zoLb@#YHJ~m7QG-;rott`t##9=;Aeqm$k~w1SG(uK7InJBB*F6imon`kT5|foL^(VT zxXAcB)d9a~nY{uY&ECU_ObD1oopGReblxP?F zP7TXa38S34!P|f8(86y%N;2bE9Qbg?s8KADzJ_z8MXOPjbt1Yme*#93lPn4=>MI;E zauS1c0KI2cC8r}6wobb11CY{-H;+*Ut}%04UeCX}SUFTJLQoC}eSNblYi>`7kk^qn z3SxUk<0@^bqVW-MM|Sd_{ebv<2oS%+VcBI&5m9B5{3Yi0`BCa3O%v_?kR8Bq;g6fs zc^!Mj=i%!xLuN~B+RR~@yj*DY_ouXb?mOQ8dc7k>vlw5OC@`WIBvf>Ym6vk-SXxfS zTo+A1(nj#lA{#j^wx$i834I?-**J|$lN-}~_Z&)k#ffFqLAYec&KW69v)Tk4Ex{&SQMzx$raxjj!%z$pUI zjjRx8x}whlp1eh8eYhR@N)v6jN=M0}E(tfKO?$~k_mfu{PW1#o$wZv47S$SL&A^wn ziuJ=2^95#E@@gd$;bJ7TH(x9h3gge<9JiTAi=;a+o8>CLPtXE(|Eg*+v(-&l5p-Qo5llK0) z%UhMKFZOX!md<5n#(Hx|beVX-Rd0*cWLQCc!XEYeK@K(S!YBV{pFWP+j(1k+qwn1i_;<5g zK#fX1%zJG(k=o&fq;~CC7^iB#RAA(@=WscGXogSg2xr+#_w2qA+r{$FZN~ zSd^*UclXDTFxpanbYjTt{#b5mze%lJRFuRNPC426lLcS(&l)I-H)M+rcZ~NrIlt4P zpk0(Ay!dF)x#K<&ZlaSHzqg89 zUvY|==dI!7i0|mKB0>E1atQ>`iTvnw! zpwVI%xoS6b&Vx!wu?g^D1wNq)Mci9kh29wv!k*49V#1**z+0y4l&PR+2O>;=732?L2PrsCpF~!2 z+g~xQTmaD#i67%~V9It(%kfrk(wKXKVucgeHL+s|3M@WbF{SY^GNvdLQ*)>)3$TBu zCOJ_Rlx|Stw8S~;h|`f;uTNS@`}c|Ge3ffz+25ieN}cb%V&m-NJQhMC$v_%kK$vc&j~m$Aa1J znhJEuJ+FVpRx#MbfzLZW%F5%8#k;QsLiKu{;~w{WNCHMZK8>N@c!N9{FToCURM7N5 z{?BP`IGYwP1xWBDRe2v>T2`@NOEh zkTAb2aE)avwSX?To^!^C4MW zmLg{2(>6q~gH}k`c%P~W%=EKL&M#`^U)n`;j9bCKxgXkaQZc?OP%jHpd4hjQf~|GL zPovw&e%4A*!;=g(a$$5^UT=kTa%)o!6hnCXD5FFMsRyG@wUcz1@xkwCLXYU6WGPz1 zsd!G6e3R42cUre*F=9nen|maEt}so1KA|gTTZ1b+pA-z*w8mP*KLH6F9l!C%P@hZ(o21&C9qYq1cE4RL^vQY|~)nN14V?w>UF6fr+*y z+`O-t{6tV>5B6ee%PW$8gk4o}g>|1`(fW>TKkEuRqdSm{pboeYO{d_7sC@Z#mU9L8 zBf#%+82e_6rO&ux4XG2G%i3@-E9**4Kd>DC>@#E%NIy7v_6m34wV<}?V#!zkmAeDC z(6uyHRdAnoU#zdMlNdiP)jNX3s^Da;)OJFasyCXjWf}pX$zaXn_4cReF!e>T`(2G( zZS`fJ#{~m>fE%u4*GDzs%L0{!2fM>0wHJP=D^EqMaC0saOz~a*er}r2n{RgH!C7%0 za#eia+O>9uE;8F`@fGrI2}oVqR~O$d$U5yJY~Fd-Em2 zDUX9Fa1J32L4Yy1pRaoyH>;%2dcZHD79R!m5!v|OT(6o-m^YI&0wQR-vGnw z*5*rg(xY*_b%n1{M>L`Jyost#?AvcChvyaZKZS_s8mNjVE|t+uy(FjUdI(;p@=_8| z5wuc&NoPiF)C)`O_o}CtKlgbYE@Fa19P&8{^6^cNH)%x>A}{;)7sn{5I=XF)nQni0 z`u>Ky8q#}v+dU1xj$mXtHi|0U9F;hfv>Jx4W;OKHWJCO(RfdbHo66v<+#Z?Qp1T-8 z73dP1xX)rn~SQHNSr+}Dm`woc@?mNze{NVTW@u$!D3M-h4j~qxc+Sm zoo9^vbMVIqR32?}(7(ew_4=)uqRDWSkcA97MwfbokPzRtPO^nef?UZ?&nph)Ai>84 z`I=IB?{4D~I2XPRzEsTpu3Jb%vK2dx$+E~y?8+PQ2gk`&;<6-~$<*AYn`$*!h20Ld0_f5)ljtkwj|aH{2TnB^I`vN5(tm){`-r)!3A;FGJM2Kg02Mxdo%1JLCJ`*B#hVGNm(MsM_)IJe4pu zOlT}oD^yobF{~&uf9RpOGsvEtW*A)kQPpCIv>umvvl>u170Ibh&!zqJ@%$pw2=p+g z4EC&LHGKF5)R-a7#ATQQEe>$=K=r2*(4P>dx}6vdp0YRnwZ{Cpb(ue{7@9LTJoZ< z^fxd%0u(QYzV(3b1{VK~iF7kGc_i880Em2fFZ5}|3~ZetWc74Z_2aKZo)*O>{8EpN z(`U*_8Mf`T&)*K|%(XmfnBR9%{)9OIya<%AZPE8;|Be{<(rKdh^dXYbdX357;k%(WS#bT_0-&2B^1WoDJRLi8VZ3@*0lO zYk7SD=zC&O_7^ndTek)B=P!26<$jhR{C;TilV203qA?e*bnCQgdsDSc&KJufw6BKg zuteF>@5tbDrTkbkYjH}$-h>x}G3?Pu>hC4JvT{j9PbDu`5v8oHguiuEk8=vTW`Q&70WL2HA&d^az=!1tX{sq3n- z>V}Y7@j;Nw1KN}l*VqgI`5>pyYYYa)7TUMc>rDb^1%ZFeNVWn4zky=B;q&1%>}kL4 z*{>w?xlbzBSQw?VjpRPh3N!ujV4{Bg9LyGQN1L@JGppdV;l(~nZ#{-NFvXH@`|2|1{kv>Wex}!I@=ZT{5MjgJ=rUlLgI_}lr^-~F zw?Sg#u^8m8y3N)#1`e%1>a`TS`eTYpl__Q~0ClFbW#;l?L~pXSjCt1dGE3ysjdmM5 zME;rVr$%66x&(-u-wa@&(ZWFc&4+DSjTRxJiy38&Kt0UT40G1 zDgdBbDSt=t`2Nc~%?|cgdKMI1@5e0Gi;wR)k4yvl0{nYP@OK)dAP@n)rThK@l5knJ zrqrc%oP4fci6oeKr59Js2xyD>37VqtB(}>=gsumxzatP+b}fkO)=Qyxxn|kkSN>TBe_c&p#fRj z;o$PIF#$fxZ`^*lA+S!NUs~ zmN*HjWeUK$I*xOU5&;(s($RMMu}TwI&bldUO;E;7Zb>81xC3h+BAKFC)kLm-i-0R_ ztdR3yW7y#XCfM)SDzM-G=e@zk~#gLmJA}qT7J-$~pbjU<8$sOuNJ&6J6kOzd-g4U0agh)#TF0oSXBjNC1NP9Ed-34R;!VD-Jq zCwvf2=owB34xbMn45}Kimc!|&^j?&l~z&ch1 z5zN*z)4mfpwmagDis>Bkd)zQH@z12kBGs;n=q7KAoiMSjUAV9Pqz_?N+|GH`cL5XO z=E2@C{pl`gpYY58Va@pab-QScS`hvaCG97X5NBv=S;k#Y|E0}4zQlm9gIzMLs&vIa z{dPEyBJd*??FONVgR1izA3~h9e^^T%PzyX&Bc@se=6gIisn6vWT%Rpgh>rrQmS8hu zT^}b+QVFLwKrA;|4WTwg;dxpqKl`v6=ljgMQm4muTVz+mQR`ViS5f}mB}3XHAL$nX zC&Wl=z}KED6_o#&Jo(B1$1KZCU!IZgz&JzV|muM}=g$&63+ur<&HE*spP|Q~@)yZNG9!Fy6Mm|K>6QGy- zDz7_%<2{A?<;Jz{mnl6iZF)l>(+hVLL;mg8tBtB5(EI>UP3WblBE?1nl`DpAqCFPQ z?P4zGEr*3HodeL$HDCALxknHFPzm%Xmen0?;Dq_M(o8;;cr}}MoLzZflaxy3l4FPbW}H95OvAs`o&QjFc(cjP{eHO>+Mc2Ui-b?f zw&LUUi)Z$oN!oAiJ)dW@VA&&z!jEn8R*I%Va5mrG#+{pN#WC)P%k8s$rY?QZXFmM} zPWUaa!BF_e-&aXlS-AdM%zis7mAJS`sN^+jMBgVwJjTFF$sarqh5uAyI|GpyH;g+S z%6vI_DS8^P3}NT+IDi)2=T&lmoPp^meTG*_B=G^F2zvIgK{UE}jI{Lp4uD2^U0k%F_pPs{bN+tqF{k)sm%6Maq9@Wd_=WD#yMX|5-F$L&QRXjf zA7U1?(zGKUZXFY7+Q_zn6$`9QS zE0g2a`AAYf&m3LNdi@u&nWlVPlXv2M>FufD^k=v-4}9F}eVHd2z$A}!Yc zm^D@mG(Gtk+e+R!T?%hq)&3EkD@2t<4rGg_PtnK)NOpF_W41gbGn%d#l=L~6$5gr^ z!`bT;!J2eafXtf4m@=z>pWnVB*xyC_U*cv-bkMaDknl>m(x!kC#Q6*3Fnx=d=Afm6 z&ykI&0W^4e-bI9t7E_N_hT|M5&%R!ZfN&PwpAp7mPEy*l$eF7@(382(`v7k6!l7n2 z*eXVx|IV3@-+(G+3p5yYVxpA$6Yku?so+hSA7*C*0)BhlY%M8T^}Q@~e=bZF(FlT| zFSwB>_Fk<(rnlv_!;<1+kr4hm2Wf>{3xb~dY{EpeX~)N;QcN@cm$iFQ{=Za-x5Q}E zX_^z=hzl#}leE85<7udW2lVaM={cUmqAcmnD4DmyA*Iu`teO-iv;Me_>663wE#S^h zFrUOwr9i&R#X1mZ70KSSM&|Vyw@li_sR}VOok+?Mx^Z12%bS}kMaHwDslAEc=laE%}4gyrv5Q3P_^UqbmzzE4#ickL1hb6&2y zemU1x0&74=vAYmj6p*ZHPW(spbB-D)@BpYTITbsiq$LD&4rYOJVdFv5KdOCBIX@xx ztO`;ik`cpy9=%&6o+{J3JS?|~b)$P6H<}C#HQc~-$ognq_%yzKxLJ}iWoXsVtmud% z(L;g^Tz|0pYSGsop+je8&vjC_Mr`_zn%4yCtsdZ)72UW|d<1w{G(AL0DVunkX+0PG zdv&0l6=YbCv1;P?6{U9Epi!AScFW2TtK;~(h*r#Z(kIHkV)rODB0qPDbG_ zzTiA2WoT3#_5Q#C7}ih^;Q#4;rj%uR^A_-2viSoo2jW5Xe!l^v$A$j%L!Y(xgfNF{6iy>32KwnrCqymLbCAX5xhxsPQ)_IimugA|^1C?sSgKigc%Ib(N=_?H z9K$Y^?4ihjB;#m4?4%$CSA*KRyVim{Oe|N zuYQjLkt}@vG#j7k&1T?$^F1Gpk6@}svLpMGscAhf>!;j8Z`MZ>wD!yD%+C`_s+hY6!O|r0x~xM;6o$@?T#V3&8hq zdCBH^0< zqJqPSwz2lCkGtc%QhM@8v1Sb2mN&tJw@X{nb5eKP8WkhxHux|XO!=na>AJ4Xj(*>v z88(E97hk@0q?%(cySr$p3~S|EZ5m3RQ}M7;2y51 z=WJWnt3~Sjj>vJ>D7ZuWouoj*B!ZCCMw0FI|J@`1IRyUsRs=CXwT&{86!rxz`lGQIToI z;&}e?tR(gA08Y1D&L7i4GN}3Q@YFCAqaZ>w)cAYZ>*x-Y!zxO!E=Xb_7FQZKnC_09lmGaecVfbjsvwq zcj%w9|J&jU>QDuMyjj&2OWkT5e|X#M6XUDFJf=QDA#QwO>Sg ze`rk<6Z!)Y(Mw-|9F;aW(C4pS1QO|rzu|FL{P&Lq*gibe1E8o-38uqBKS7Q-qvF3b zJW;R&pZ7X`Xngb}9V^Fzt81A&w>A6?k~i&Hui(6|M?w(g>OyQ!5Yk-W{zd!towVL=B z!T2||3;g%?qmO-<@Fm)I^kg+-ML z9iZ$&C4Vtb=~^e%QJ~iddHx7c^asj?R9B44FyO5Ai5Xa6WjGG&o7omILJ&Lx4sHKY zlHc|1=d=|y0U9>)sC>ai0Bu_WfsK~-QvEg=9Eq(v)Qy{Y=5cTPWwF7WguJwRa`}_<1KdCwC zZRvlIIgb5;>^~^wuszV!b}&qG@IA?}2O4M&^bQLww>x_69}c}%Z4XL#Zx|a|&liR?Mp2Z_^}t|0!x?8iaH-It%vp6$3Rgew z281f?^iQM#z(vDJP3N>ROt$^*$Etf`z&c7peLe(G|K7iP|DR}^kvJ}L6h*bgdCPv- z1?H=?9*q(Fbguf)4W61|{ul>|$RdF;$*ElkO)#3SlEU8vDLjr7ar;S3t(9iu3YF&JT&y^i2*X?2eH1V`3DlKS1~uh>)6OT z$=qttYrmc;l1dAi>!;5{@i=NbW(|5cO(Q59cNTlZhM$0kQ%TAUj22zvT z$fZulpDhE+k`{=s-MUh^2QWLSi87VDWD(^8SmS1L*40~VIDpuRG5+Gx@OWW3Ug|qa z^Lxa>w+84AZ_)u4(6yGq0MP@MAvOZw1Hc7P* z>dULhSFw3q6e1R}GwjWP%1ZBFV≦>k`&a0P5@b0pQ*Jo&tv}Wg}7KK-(0ZWslt8 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%0A`)l)jQD*&r}#dF>KvXfYo%8 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

Ylvspw<9<1D$3Pm5Frj(qj8jzU56pUY*{6nrSm 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 literal 121466 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%0D+_Zw^ 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 AfdBvi diff --git a/example/index.js b/example/index.js index 3ddfc58..4ae8f67 100644 --- a/example/index.js +++ b/example/index.js @@ -2,6 +2,6 @@ import { AppRegistry } from 'react-native'; import { name as appName } from './app.json'; import App from './src/App'; import { install } from 'react-native-nitro-base64'; - +import 'react-native-quick-base64'; install(); AppRegistry.registerComponent(appName, () => App); diff --git a/example/ios/NitroBase64Example.xcodeproj/project.pbxproj b/example/ios/NitroBase64Example.xcodeproj/project.pbxproj index 8cd2c4c..363fef2 100644 --- a/example/ios/NitroBase64Example.xcodeproj/project.pbxproj +++ b/example/ios/NitroBase64Example.xcodeproj/project.pbxproj @@ -11,19 +11,19 @@ 761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; BA8731FA8F4AE30FD34E383A /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; }; - D891FF4993729C4A84C2F7E9 /* libPods-NitroBase64Example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 098BA51963414FC1C9E6DB12 /* libPods-NitroBase64Example.a */; }; + FF36CB20B19F64C38348A6E0 /* libPods-NitroBase64Example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AE440FAA83DDAAECFBFDE91 /* libPods-NitroBase64Example.a */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 098BA51963414FC1C9E6DB12 /* libPods-NitroBase64Example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-NitroBase64Example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07F961A680F5B00A75B9A /* NitroBase64Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NitroBase64Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = NitroBase64Example/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = NitroBase64Example/Info.plist; sourceTree = ""; }; 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = NitroBase64Example/PrivacyInfo.xcprivacy; sourceTree = ""; }; - 66E9F03C53D7017FE9545A1B /* Pods-NitroBase64Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NitroBase64Example.release.xcconfig"; path = "Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example.release.xcconfig"; sourceTree = ""; }; + 5970CB6C7B61052C670099BF /* Pods-NitroBase64Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NitroBase64Example.debug.xcconfig"; path = "Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example.debug.xcconfig"; sourceTree = ""; }; 761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = NitroBase64Example/AppDelegate.swift; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = NitroBase64Example/LaunchScreen.storyboard; sourceTree = ""; }; - DAACC79F12F190ED23DE45D7 /* Pods-NitroBase64Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NitroBase64Example.debug.xcconfig"; path = "Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example.debug.xcconfig"; sourceTree = ""; }; + 9AE440FAA83DDAAECFBFDE91 /* libPods-NitroBase64Example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-NitroBase64Example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 9E8DAE6BEF6EB943E3A5C42E /* Pods-NitroBase64Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NitroBase64Example.release.xcconfig"; path = "Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example.release.xcconfig"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ @@ -32,7 +32,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D891FF4993729C4A84C2F7E9 /* libPods-NitroBase64Example.a in Frameworks */, + FF36CB20B19F64C38348A6E0 /* libPods-NitroBase64Example.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -55,7 +55,7 @@ isa = PBXGroup; children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - 098BA51963414FC1C9E6DB12 /* libPods-NitroBase64Example.a */, + 9AE440FAA83DDAAECFBFDE91 /* libPods-NitroBase64Example.a */, ); name = Frameworks; sourceTree = ""; @@ -92,8 +92,8 @@ BBD78D7AC51CEA395F1C20DB /* Pods */ = { isa = PBXGroup; children = ( - DAACC79F12F190ED23DE45D7 /* Pods-NitroBase64Example.debug.xcconfig */, - 66E9F03C53D7017FE9545A1B /* Pods-NitroBase64Example.release.xcconfig */, + 5970CB6C7B61052C670099BF /* Pods-NitroBase64Example.debug.xcconfig */, + 9E8DAE6BEF6EB943E3A5C42E /* Pods-NitroBase64Example.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -105,13 +105,13 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "NitroBase64Example" */; buildPhases = ( - 698DAD2D36C86EE227AD3CC1 /* [CP] Check Pods Manifest.lock */, + B60AA6539ECF2D1C459B7F53 /* [CP] Check Pods Manifest.lock */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - 9E13E8199B99474DE1C693E0 /* [CP] Embed Pods Frameworks */, - 65F550C0311E7C54C50B866B /* [CP] Copy Pods Resources */, + B25FA5EDB160A78A757DC68E /* [CP] Embed Pods Frameworks */, + 7EF932ECA7B9FEC381831A14 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -183,7 +183,7 @@ shellPath = /bin/sh; shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; }; - 65F550C0311E7C54C50B866B /* [CP] Copy Pods Resources */ = { + 7EF932ECA7B9FEC381831A14 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -200,43 +200,43 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 698DAD2D36C86EE227AD3CC1 /* [CP] Check Pods Manifest.lock */ = { + B25FA5EDB160A78A757DC68E /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; + name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-NitroBase64Example-checkManifestLockResult.txt", + "${PODS_ROOT}/Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 9E13E8199B99474DE1C693E0 /* [CP] Embed Pods Frameworks */ = { + B60AA6539ECF2D1C459B7F53 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Embed Pods Frameworks"; + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-NitroBase64Example-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-NitroBase64Example/Pods-NitroBase64Example-frameworks.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -255,7 +255,7 @@ /* Begin XCBuildConfiguration section */ 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = DAACC79F12F190ED23DE45D7 /* Pods-NitroBase64Example.debug.xcconfig */; + baseConfigurationReference = 5970CB6C7B61052C670099BF /* Pods-NitroBase64Example.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -284,7 +284,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 66E9F03C53D7017FE9545A1B /* Pods-NitroBase64Example.release.xcconfig */; + baseConfigurationReference = 9E8DAE6BEF6EB943E3A5C42E /* Pods-NitroBase64Example.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 217f83b..1175979 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -8,7 +8,7 @@ PODS: - hermes-engine (0.81.1): - hermes-engine/Pre-built (= 0.81.1) - hermes-engine/Pre-built (0.81.1) - - NitroBase64 (0.8.2): + - NitroBase64 (1.1.0): - boost - DoubleConversion - fast_float @@ -2674,7 +2674,7 @@ SPEC CHECKSUMS: fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd glog: 5683914934d5b6e4240e497e0f4a3b42d1854183 hermes-engine: 4f8246b1f6d79f625e0d99472d1f3a71da4d28ca - NitroBase64: e9d09ea94a839528b94a43d3c500ad1405c8a413 + NitroBase64: f35489547b9de21ebce93bfd2f5f39f42fe83d1e NitroModules: 70e2ff77a0b718e47d5bccd837fefe8a9aa20f50 RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669 RCTDeprecation: c4b9e2fd0ab200e3af72b013ed6113187c607077 diff --git a/package.json b/package.json index 55623a4..e3a3c47 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-nitro-base64", - "version": "1.1.0", + "version": "1.1.1", "description": "NitroModule Base64", "main": "./lib/module/index.js", "types": "./lib/typescript/src/index.d.ts", From 16e387f8e39c5db85709a8a083462ffacc7a4c38 Mon Sep 17 00:00:00 2001 From: Rom Date: Sun, 17 May 2026 14:57:14 +0700 Subject: [PATCH 3/3] feat: enhance package description and expand keywords in package.json --- package.json | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index e3a3c47..4a4f7cb 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-native-nitro-base64", - "version": "1.1.1", - "description": "NitroModule Base64", + "version": "1.1.2", + "description": "The fastest Base64 library for React Native, powered by simdutf SIMD C++ and Nitro Modules", "main": "./lib/module/index.js", "types": "./lib/typescript/src/index.d.ts", "exports": { @@ -47,7 +47,24 @@ "keywords": [ "react-native", "ios", - "android" + "android", + "base64", + "base64url", + "encoding", + "decoding", + "simdutf", + "simd", + "performance", + "fast", + "nitro", + "nitro-modules", + "jsi", + "arraybuffer", + "uint8array", + "binary", + "image", + "crypto", + "jwt" ], "repository": { "type": "git",