# Design Document - basexx
## 1. Architecture
The `basexx` library follows a modular architecture, with each encoding scheme implemented in its own module. A central `lib.rs` file serves as the main entry point, exporting the public APIs for each module.
### 1.1. Module Structure
The primary modules correspond to the different base encodings:
- `base64`
- `base58`
- `base56`
- `base32`
- `ags`
Additionally, variant implementations exist in separate modules:
- `base64g`
- `base58b`
- `base58r`
- `base32i`
Each module encapsulates the logic for its specific encoding and decoding scheme.
### 1.2. Performance Optimization with SIMD
For performance-critical encodings like Base64 and Base32, the library employs a strategy of runtime CPU feature detection to select the most optimal implementation. The hierarchy of implementations is as follows:
1. **AVX2:** If the CPU supports AVX2, the AVX2-optimized implementation is used.
2. **SSSE3:** If AVX2 is not available but SSSE3 is, the SSSE3-optimized version is used.
3. **Scalar:** If neither AVX2 nor SSSE3 is supported, a fallback scalar implementation is used.
This dynamic dispatch is handled within the main function of each base module (e.g., `Base64::encode`), which checks for CPU features using `is_x86_feature_detected!`. The SIMD-specific code is located in sub-modules within each base's module directory (e.g., `src/base64/base64_avx2.rs`).
## 2. Data Structures
- **`AsciiGraphicSet` (`ags.rs`):** This struct is a core component used by many of the encoding modules. It holds the character set for a given base and provides methods for converting between binary data and the ASCII representation.
- **`BaseXX` Structs (e.g., `Base64`):** Each encoding scheme has a primary struct (e.g., `Base64`, `Base58`) that serves as the main interface for encoding and decoding. These structs typically hold an `AsciiGraphicSet` instance configured with the correct character map for that base.
## 3. Public API
The public API for each encoding scheme is consistent. For a given `BaseXX` struct, the following methods are typically provided:
- **`new()`:** Creates a new instance with the default character set.
- **`with_str(a: &str)` / `with_slice(a: &[u8])`:** Creates a new instance with a custom character set.
- **`encode(a: &[u8]) -> Result<String, EncodeError>`:** Encodes a byte slice into a string.
- **`decode(a: &str) -> Result<Vec<u8>, DecodeError>`:** Decodes a string into a byte vector.
## 4. Error Handling
The library defines two primary error enums:
- **`EncodeError`:** For errors that occur during the encoding process (e.g., an invalid character in the input).
- **`DecodeError`:** For errors that occur during decoding (e.g., invalid byte sequence, incorrect length).
## 5. Feature Flags
The design incorporates the use of feature flags to control optional functionality and dependencies:
- **`rug`:** Conditionally compiles the `base58r` module and includes the `rug` dependency.
- **`bench`, `abench`, `ubench`, `nobench`:** Control the compilation of benchmarking code.
- **`aligned_data`:** Enables functionality related to aligned data structures.
## 6. Testing and Benchmarking
The project has a clear structure for tests and benchmarks:
- **Unit Tests:** Located within each module, often in a `tests` sub-module.
- **Integration Tests:** In the `tests/` directory.
- **Benchmarks:** In the `benches/` directory and also within each module, conditionally compiled with the `bench`-related feature flags.