build: simplify production dependencies and hosted checks

This commit is contained in:
Francesco Bozzo committed 2026-09-20 01:20:16 +02:00
1 parent f6d3bbb7b0
commit e06c3785ca
81 files changed
+3663 -6998

No files matched your search

@@ -1,724 +0,0 @@
---
name: cpp-coding-standards
description: C++ coding standards based on the C++ Core Guidelines (isocpp.github.io). Use when writing, reviewing, or refactoring C++ code to enforce modern, safe, and idiomatic practices.
metadata:
origin: ECC
---
# C++ Coding Standards (C++ Core Guidelines)
Comprehensive coding standards for modern C++ (C++17/20/23) derived from the [C++ Core Guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines). Enforces type safety, resource safety, immutability, and clarity.
## When to Use
- Writing new C++ code (classes, functions, templates)
- Reviewing or refactoring existing C++ code
- Making architectural decisions in C++ projects
- Enforcing consistent style across a C++ codebase
- Choosing between language features (e.g., `enum` vs `enum class`, raw pointer vs smart pointer)
### When NOT to Use
- Non-C++ projects
- Legacy C codebases that cannot adopt modern C++ features
- Embedded/bare-metal contexts where specific guidelines conflict with hardware constraints (adapt selectively)
## Cross-Cutting Principles
These themes recur across the entire guidelines and form the foundation:
1. **RAII everywhere** (P.8, R.1, E.6, CP.20): Bind resource lifetime to object lifetime
2. **Immutability by default** (P.10, Con.1-5, ES.25): Start with `const`/`constexpr`; mutability is the exception
3. **Type safety** (P.4, I.4, ES.46-49, Enum.3): Use the type system to prevent errors at compile time
4. **Express intent** (P.3, F.1, NL.1-2, T.10): Names, types, and concepts should communicate purpose
5. **Minimize complexity** (F.2-3, ES.5, Per.4-5): Simple code is correct code
6. **Value semantics over pointer semantics** (C.10, R.3-5, F.20, CP.31): Prefer returning by value and scoped objects
## Philosophy & Interfaces (P.*, I.*)
### Key Rules
| Rule | Summary |
|------|---------|
| **P.1** | Express ideas directly in code |
| **P.3** | Express intent |
| **P.4** | Ideally, a program should be statically type safe |
| **P.5** | Prefer compile-time checking to run-time checking |
| **P.8** | Don't leak any resources |
| **P.10** | Prefer immutable data to mutable data |
| **I.1** | Make interfaces explicit |
| **I.2** | Avoid non-const global variables |
| **I.4** | Make interfaces precisely and strongly typed |
| **I.11** | Never transfer ownership by a raw pointer or reference |
| **I.23** | Keep the number of function arguments low |
### DO
```cpp
// P.10 + I.4: Immutable, strongly typed interface
struct Temperature {
double kelvin;
};
Temperature boil(const Temperature& water);
```
### DON'T
```cpp
// Weak interface: unclear ownership, unclear units
double boil(double* temp);
// Non-const global variable
int g_counter = 0; // I.2 violation
```
## Functions (F.*)
### Key Rules
| Rule | Summary |
|------|---------|
| **F.1** | Package meaningful operations as carefully named functions |
| **F.2** | A function should perform a single logical operation |
| **F.3** | Keep functions short and simple |
| **F.4** | If a function might be evaluated at compile time, declare it `constexpr` |
| **F.6** | If your function must not throw, declare it `noexcept` |
| **F.8** | Prefer pure functions |
| **F.16** | For "in" parameters, pass cheaply-copied types by value and others by `const&` |
| **F.20** | For "out" values, prefer return values to output parameters |
| **F.21** | To return multiple "out" values, prefer returning a struct |
| **F.43** | Never return a pointer or reference to a local object |
### Parameter Passing
```cpp
// F.16: Cheap types by value, others by const&
void print(int x); // cheap: by value
void analyze(const std::string& data); // expensive: by const&
void transform(std::string s); // sink: by value (will move)
// F.20 + F.21: Return values, not output parameters
struct ParseResult {
std::string token;
int position;
};
ParseResult parse(std::string_view input); // GOOD: return struct
// BAD: output parameters
void parse(std::string_view input,
std::string& token, int& pos); // avoid this
```
### Pure Functions and constexpr
```cpp
// F.4 + F.8: Pure, constexpr where possible
constexpr int factorial(int n) noexcept {
return (n <= 1) ? 1 : n * factorial(n - 1);
}
static_assert(factorial(5) == 120);
```
### Anti-Patterns
- Returning `T&&` from functions (F.45)
- Using `va_arg` / C-style variadics (F.55)
- Capturing by reference in lambdas passed to other threads (F.53)
- Returning `const T` which inhibits move semantics (F.49)
## Classes & Class Hierarchies (C.*)
### Key Rules
| Rule | Summary |
|------|---------|
| **C.2** | Use `class` if invariant exists; `struct` if data members vary independently |
| **C.9** | Minimize exposure of members |
| **C.20** | If you can avoid defining default operations, do (Rule of Zero) |
| **C.21** | If you define or `=delete` any copy/move/destructor, handle them all (Rule of Five) |
| **C.35** | Base class destructor: public virtual or protected non-virtual |
| **C.41** | A constructor should create a fully initialized object |
| **C.46** | Declare single-argument constructors `explicit` |
| **C.67** | A polymorphic class should suppress public copy/move |
| **C.128** | Virtual functions: specify exactly one of `virtual`, `override`, or `final` |
### Rule of Zero
```cpp
// C.20: Let the compiler generate special members
struct Employee {
std::string name;
std::string department;
int id;
// No destructor, copy/move constructors, or assignment operators needed
};
```
### Rule of Five
```cpp
// C.21: If you must manage a resource, define all five
class Buffer {
public:
explicit Buffer(std::size_t size)
: data_(std::make_unique<char[]>(size)), size_(size) {}
~Buffer() = default;
Buffer(const Buffer& other)
: data_(std::make_unique<char[]>(other.size_)), size_(other.size_) {
std::copy_n(other.data_.get(), size_, data_.get());
}
Buffer& operator=(const Buffer& other) {
if (this != &other) {
auto new_data = std::make_unique<char[]>(other.size_);
std::copy_n(other.data_.get(), other.size_, new_data.get());
data_ = std::move(new_data);
size_ = other.size_;
}
return *this;
}
Buffer(Buffer&&) noexcept = default;
Buffer& operator=(Buffer&&) noexcept = default;
private:
std::unique_ptr<char[]> data_;
std::size_t size_;
};
```
### Class Hierarchy
```cpp
// C.35 + C.128: Virtual destructor, use override
class Shape {
public:
virtual ~Shape() = default;
virtual double area() const = 0; // C.121: pure interface
};
class Circle : public Shape {
public:
explicit Circle(double r) : radius_(r) {}
double area() const override { return 3.14159 * radius_ * radius_; }
private:
double radius_;
};
```
### Anti-Patterns
- Calling virtual functions in constructors/destructors (C.82)
- Using `memset`/`memcpy` on non-trivial types (C.90)
- Providing different default arguments for virtual function and overrider (C.140)
- Making data members `const` or references, which suppresses move/copy (C.12)
## Resource Management (R.*)
### Key Rules
| Rule | Summary |
|------|---------|
| **R.1** | Manage resources automatically using RAII |
| **R.3** | A raw pointer (`T*`) is non-owning |
| **R.5** | Prefer scoped objects; don't heap-allocate unnecessarily |
| **R.10** | Avoid `malloc()`/`free()` |
| **R.11** | Avoid calling `new` and `delete` explicitly |
| **R.20** | Use `unique_ptr` or `shared_ptr` to represent ownership |
| **R.21** | Prefer `unique_ptr` over `shared_ptr` unless sharing ownership |
| **R.22** | Use `make_shared()` to make `shared_ptr`s |
### Smart Pointer Usage
```cpp
// R.11 + R.20 + R.21: RAII with smart pointers
auto widget = std::make_unique<Widget>("config"); // unique ownership
auto cache = std::make_shared<Cache>(1024); // shared ownership
// R.3: Raw pointer = non-owning observer
void render(const Widget* w) { // does NOT own w
if (w) w->draw();
}
render(widget.get());
```
### RAII Pattern
```cpp
// R.1: Resource acquisition is initialization
class FileHandle {
public:
explicit FileHandle(const std::string& path)
: handle_(std::fopen(path.c_str(), "r")) {
if (!handle_) throw std::runtime_error("Failed to open: " + path);
}
~FileHandle() {
if (handle_) std::fclose(handle_);
}
FileHandle(const FileHandle&) = delete;
FileHandle& operator=(const FileHandle&) = delete;
FileHandle(FileHandle&& other) noexcept
: handle_(std::exchange(other.handle_, nullptr)) {}
FileHandle& operator=(FileHandle&& other) noexcept {
if (this != &other) {
if (handle_) std::fclose(handle_);
handle_ = std::exchange(other.handle_, nullptr);
}
return *this;
}
private:
std::FILE* handle_;
};
```
### Anti-Patterns
- Naked `new`/`delete` (R.11)
- `malloc()`/`free()` in C++ code (R.10)
- Multiple resource allocations in a single expression (R.13 -- exception safety hazard)
- `shared_ptr` where `unique_ptr` suffices (R.21)
## Expressions & Statements (ES.*)
### Key Rules
| Rule | Summary |
|------|---------|
| **ES.5** | Keep scopes small |
| **ES.20** | Always initialize an object |
| **ES.23** | Prefer `{}` initializer syntax |
| **ES.25** | Declare objects `const` or `constexpr` unless modification is intended |
| **ES.28** | Use lambdas for complex initialization of `const` variables |
| **ES.45** | Avoid magic constants; use symbolic constants |
| **ES.46** | Avoid narrowing/lossy arithmetic conversions |
| **ES.47** | Use `nullptr` rather than `0` or `NULL` |
| **ES.48** | Avoid casts |
| **ES.50** | Don't cast away `const` |
### Initialization
```cpp
// ES.20 + ES.23 + ES.25: Always initialize, prefer {}, default to const
const int max_retries{3};
const std::string name{"widget"};
const std::vector<int> primes{2, 3, 5, 7, 11};
// ES.28: Lambda for complex const initialization
const auto config = [&] {
Config c;
c.timeout = std::chrono::seconds{30};
c.retries = max_retries;
c.verbose = debug_mode;
return c;
}();
```
### Anti-Patterns
- Uninitialized variables (ES.20)
- Using `0` or `NULL` as pointer (ES.47 -- use `nullptr`)
- C-style casts (ES.48 -- use `static_cast`, `const_cast`, etc.)
- Casting away `const` (ES.50)
- Magic numbers without named constants (ES.45)
- Mixing signed and unsigned arithmetic (ES.100)
- Reusing names in nested scopes (ES.12)
## Error Handling (E.*)
### Key Rules
| Rule | Summary |
|------|---------|
| **E.1** | Develop an error-handling strategy early in a design |
| **E.2** | Throw an exception to signal that a function can't perform its assigned task |
| **E.6** | Use RAII to prevent leaks |
| **E.12** | Use `noexcept` when throwing is impossible or unacceptable |
| **E.14** | Use purpose-designed user-defined types as exceptions |
| **E.15** | Throw by value, catch by reference |
| **E.16** | Destructors, deallocation, and swap must never fail |
| **E.17** | Don't try to catch every exception in every function |
### Exception Hierarchy
```cpp
// E.14 + E.15: Custom exception types, throw by value, catch by reference
class AppError : public std::runtime_error {
public:
using std::runtime_error::runtime_error;
};
class NetworkError : public AppError {
public:
NetworkError(const std::string& msg, int code)
: AppError(msg), status_code(code) {}
int status_code;
};
void fetch_data(const std::string& url) {
// E.2: Throw to signal failure
throw NetworkError("connection refused", 503);
}
void run() {
try {
fetch_data("https://api.example.com");
} catch (const NetworkError& e) {
log_error(e.what(), e.status_code);
} catch (const AppError& e) {
log_error(e.what());
}
// E.17: Don't catch everything here -- let unexpected errors propagate
}
```
### Anti-Patterns
- Throwing built-in types like `int` or string literals (E.14)
- Catching by value (slicing risk) (E.15)
- Empty catch blocks that silently swallow errors
- Using exceptions for flow control (E.3)
- Error handling based on global state like `errno` (E.28)
## Constants & Immutability (Con.*)
### All Rules
| Rule | Summary |
|------|---------|
| **Con.1** | By default, make objects immutable |
| **Con.2** | By default, make member functions `const` |
| **Con.3** | By default, pass pointers and references to `const` |
| **Con.4** | Use `const` for values that don't change after construction |
| **Con.5** | Use `constexpr` for values computable at compile time |
```cpp
// Con.1 through Con.5: Immutability by default
class Sensor {
public:
explicit Sensor(std::string id) : id_(std::move(id)) {}
// Con.2: const member functions by default
const std::string& id() const { return id_; }
double last_reading() const { return reading_; }
// Only non-const when mutation is required
void record(double value) { reading_ = value; }
private:
const std::string id_; // Con.4: never changes after construction
double reading_{0.0};
};
// Con.3: Pass by const reference
void display(const Sensor& s) {
std::cout << s.id() << ": " << s.last_reading() << '\n';
}
// Con.5: Compile-time constants
constexpr double PI = 3.14159265358979;
constexpr int MAX_SENSORS = 256;
```
## Concurrency & Parallelism (CP.*)
### Key Rules
| Rule | Summary |
|------|---------|
| **CP.2** | Avoid data races |
| **CP.3** | Minimize explicit sharing of writable data |
| **CP.4** | Think in terms of tasks, rather than threads |
| **CP.8** | Don't use `volatile` for synchronization |
| **CP.20** | Use RAII, never plain `lock()`/`unlock()` |
| **CP.21** | Use `std::scoped_lock` to acquire multiple mutexes |
| **CP.22** | Never call unknown code while holding a lock |
| **CP.42** | Don't wait without a condition |
| **CP.44** | Remember to name your `lock_guard`s and `unique_lock`s |
| **CP.100** | Don't use lock-free programming unless you absolutely have to |
### Safe Locking
```cpp
// CP.20 + CP.44: RAII locks, always named
class ThreadSafeQueue {
public:
void push(int value) {
std::lock_guard<std::mutex> lock(mutex_); // CP.44: named!
queue_.push(value);
cv_.notify_one();
}
int pop() {
std::unique_lock<std::mutex> lock(mutex_);
// CP.42: Always wait with a condition
cv_.wait(lock, [this] { return !queue_.empty(); });
const int value = queue_.front();
queue_.pop();
return value;
}
private:
std::mutex mutex_; // CP.50: mutex with its data
std::condition_variable cv_;
std::queue<int> queue_;
};
```
### Multiple Mutexes
```cpp
// CP.21: std::scoped_lock for multiple mutexes (deadlock-free)
void transfer(Account& from, Account& to, double amount) {
std::scoped_lock lock(from.mutex_, to.mutex_);
from.balance_ -= amount;
to.balance_ += amount;
}
```
### Anti-Patterns
- `volatile` for synchronization (CP.8 -- it's for hardware I/O only)
- Detaching threads (CP.26 -- lifetime management becomes nearly impossible)
- Unnamed lock guards: `std::lock_guard<std::mutex>(m);` destroys immediately (CP.44)
- Holding locks while calling callbacks (CP.22 -- deadlock risk)
- Lock-free programming without deep expertise (CP.100)
## Templates & Generic Programming (T.*)
### Key Rules
| Rule | Summary |
|------|---------|
| **T.1** | Use templates to raise the level of abstraction |
| **T.2** | Use templates to express algorithms for many argument types |
| **T.10** | Specify concepts for all template arguments |
| **T.11** | Use standard concepts whenever possible |
| **T.13** | Prefer shorthand notation for simple concepts |
| **T.43** | Prefer `using` over `typedef` |
| **T.120** | Use template metaprogramming only when you really need to |
| **T.144** | Don't specialize function templates (overload instead) |
### Concepts (C++20)
```cpp
#include <concepts>
// T.10 + T.11: Constrain templates with standard concepts
template<std::integral T>
T gcd(T a, T b) {
while (b != 0) {
a = std::exchange(b, a % b);
}
return a;
}
// T.13: Shorthand concept syntax
void sort(std::ranges::random_access_range auto& range) {
std::ranges::sort(range);
}
// Custom concept for domain-specific constraints
template<typename T>
concept Serializable = requires(const T& t) {
{ t.serialize() } -> std::convertible_to<std::string>;
};
template<Serializable T>
void save(const T& obj, const std::string& path);
```
### Anti-Patterns
- Unconstrained templates in visible namespaces (T.47)
- Specializing function templates instead of overloading (T.144)
- Template metaprogramming where `constexpr` suffices (T.120)
- `typedef` instead of `using` (T.43)
## Standard Library (SL.*)
### Key Rules
| Rule | Summary |
|------|---------|
| **SL.1** | Use libraries wherever possible |
| **SL.2** | Prefer the standard library to other libraries |
| **SL.con.1** | Prefer `std::array` or `std::vector` over C arrays |
| **SL.con.2** | Prefer `std::vector` by default |
| **SL.str.1** | Use `std::string` to own character sequences |
| **SL.str.2** | Use `std::string_view` to refer to character sequences |
| **SL.io.50** | Avoid `endl` (use `'\n'` -- `endl` forces a flush) |
```cpp
// SL.con.1 + SL.con.2: Prefer vector/array over C arrays
const std::array<int, 4> fixed_data{1, 2, 3, 4};
std::vector<std::string> dynamic_data;
// SL.str.1 + SL.str.2: string owns, string_view observes
std::string build_greeting(std::string_view name) {
return "Hello, " + std::string(name) + "!";
}
// SL.io.50: Use '\n' not endl
std::cout << "result: " << value << '\n';
```
## Enumerations (Enum.*)
### Key Rules
| Rule | Summary |
|------|---------|
| **Enum.1** | Prefer enumerations over macros |
| **Enum.3** | Prefer `enum class` over plain `enum` |
| **Enum.5** | Don't use ALL_CAPS for enumerators |
| **Enum.6** | Avoid unnamed enumerations |
```cpp
// Enum.3 + Enum.5: Scoped enum, no ALL_CAPS
enum class Color { red, green, blue };
enum class LogLevel { debug, info, warning, error };
// BAD: plain enum leaks names, ALL_CAPS clashes with macros
enum { RED, GREEN, BLUE }; // Enum.3 + Enum.5 + Enum.6 violation
#define MAX_SIZE 100 // Enum.1 violation -- use constexpr
```
## Source Files & Naming (SF.*, NL.*)
### Key Rules
| Rule | Summary |
|------|---------|
| **SF.1** | Use `.cpp` for code files and `.h` for interface files |
| **SF.7** | Don't write `using namespace` at global scope in a header |
| **SF.8** | Use `#include` guards for all `.h` files |
| **SF.11** | Header files should be self-contained |
| **NL.5** | Avoid encoding type information in names (no Hungarian notation) |
| **NL.8** | Use a consistent naming style |
| **NL.9** | Use ALL_CAPS for macro names only |
| **NL.10** | Prefer `underscore_style` names |
### Header Guard
```cpp
// SF.8: Include guard (or #pragma once)
#ifndef PROJECT_MODULE_WIDGET_H
#define PROJECT_MODULE_WIDGET_H
// SF.11: Self-contained -- include everything this header needs
#include <string>
#include <vector>
namespace project::module {
class Widget {
public:
explicit Widget(std::string name);
const std::string& name() const;
private:
std::string name_;
};
} // namespace project::module
#endif // PROJECT_MODULE_WIDGET_H
```
### Naming Conventions
```cpp
// NL.8 + NL.10: Consistent underscore_style
namespace my_project {
constexpr int max_buffer_size = 4096; // NL.9: not ALL_CAPS (it's not a macro)
class tcp_connection { // underscore_style class
public:
void send_message(std::string_view msg);
bool is_connected() const;
private:
std::string host_; // trailing underscore for members
int port_;
};
} // namespace my_project
```
### Anti-Patterns
- `using namespace std;` in a header at global scope (SF.7)
- Headers that depend on inclusion order (SF.10, SF.11)
- Hungarian notation like `strName`, `iCount` (NL.5)
- ALL_CAPS for anything other than macros (NL.9)
## Performance (Per.*)
### Key Rules
| Rule | Summary |
|------|---------|
| **Per.1** | Don't optimize without reason |
| **Per.2** | Don't optimize prematurely |
| **Per.6** | Don't make claims about performance without measurements |
| **Per.7** | Design to enable optimization |
| **Per.10** | Rely on the static type system |
| **Per.11** | Move computation from run time to compile time |
| **Per.19** | Access memory predictably |
### Guidelines
```cpp
// Per.11: Compile-time computation where possible
constexpr auto lookup_table = [] {
std::array<int, 256> table{};
for (int i = 0; i < 256; ++i) {
table[i] = i * i;
}
return table;
}();
// Per.19: Prefer contiguous data for cache-friendliness
std::vector<Point> points; // GOOD: contiguous
std::vector<std::unique_ptr<Point>> indirect_points; // BAD: pointer chasing
```
### Anti-Patterns
- Optimizing without profiling data (Per.1, Per.6)
- Choosing "clever" low-level code over clear abstractions (Per.4, Per.5)
- Ignoring data layout and cache behavior (Per.19)
## Quick Reference Checklist
Before marking C++ work complete:
- [ ] No raw `new`/`delete` -- use smart pointers or RAII (R.11)
- [ ] Objects initialized at declaration (ES.20)
- [ ] Variables are `const`/`constexpr` by default (Con.1, ES.25)
- [ ] Member functions are `const` where possible (Con.2)
- [ ] `enum class` instead of plain `enum` (Enum.3)
- [ ] `nullptr` instead of `0`/`NULL` (ES.47)
- [ ] No narrowing conversions (ES.46)
- [ ] No C-style casts (ES.48)
- [ ] Single-argument constructors are `explicit` (C.46)
- [ ] Rule of Zero or Rule of Five applied (C.20, C.21)
- [ ] Base class destructors are public virtual or protected non-virtual (C.35)
- [ ] Templates are constrained with concepts (T.10)
- [ ] No `using namespace` in headers at global scope (SF.7)
- [ ] Headers have include guards and are self-contained (SF.8, SF.11)
- [ ] Locks use RAII (`scoped_lock`/`lock_guard`) (CP.20)
- [ ] Exceptions are custom types, thrown by value, caught by reference (E.14, E.15)
- [ ] `'\n'` instead of `std::endl` (SL.io.50)
- [ ] No magic numbers (ES.45)
-117
View File
@@ -1,117 +0,0 @@
---
name: cpp-pro
description: Writes, optimizes, and debugs C++ applications using modern C++20/23 features, template metaprogramming, and high-performance systems techniques. Use when building or refactoring C++ code requiring concepts, ranges, coroutines, SIMD optimization, or careful memory management — or when addressing performance bottlenecks, concurrency issues, and build system configuration with CMake.
license: MIT
metadata:
author: https://github.com/Jeffallan
version: "1.1.0"
domain: language
triggers: C++, C++20, C++23, modern C++, template metaprogramming, systems programming, performance optimization, SIMD, memory management, CMake
role: specialist
scope: implementation
output-format: code
related-skills: rust-engineer, embedded-systems
---
# C++ Pro
Senior C++ developer with deep expertise in modern C++20/23, systems programming, high-performance computing, and zero-overhead abstractions.
## Core Workflow
1. **Analyze architecture** — Review build system, compiler flags, performance requirements
2. **Design with concepts** — Create type-safe interfaces using C++20 concepts
3. **Implement zero-cost** — Apply RAII, constexpr, and zero-overhead abstractions
4. **Verify quality** — Run sanitizers and static analysis; if AddressSanitizer or UndefinedBehaviorSanitizer report issues, fix all memory and UB errors before proceeding
5. **Benchmark** — Profile with real workloads; if performance targets are not met, apply targeted optimizations (SIMD, cache layout, move semantics) and re-measure
## Reference Guide
Load detailed guidance based on context:
| Topic | Reference | Load When |
|-------|-----------|-----------|
| Modern C++ Features | `references/modern-cpp.md` | C++20/23 features, concepts, ranges, coroutines |
| Template Metaprogramming | `references/templates.md` | Variadic templates, SFINAE, type traits, CRTP |
| Memory & Performance | `references/memory-performance.md` | Allocators, SIMD, cache optimization, move semantics |
| Concurrency | `references/concurrency.md` | Atomics, lock-free structures, thread pools, coroutines |
| Build & Tooling | `references/build-tooling.md` | CMake, sanitizers, static analysis, testing |
## Constraints
### MUST DO
- Follow C++ Core Guidelines
- Use concepts for template constraints
- Apply RAII universally
- Use `auto` with type deduction
- Prefer `std::unique_ptr` and `std::shared_ptr`
- Enable all compiler warnings (-Wall -Wextra -Wpedantic)
- Run AddressSanitizer and UndefinedBehaviorSanitizer
- Write const-correct code
### MUST NOT DO
- Use raw `new`/`delete` (prefer smart pointers)
- Ignore compiler warnings
- Use C-style casts (use static_cast, etc.)
- Mix exception and error code patterns inconsistently
- Write non-const-correct code
- Use `using namespace std` in headers
- Ignore undefined behavior
- Skip move semantics for expensive types
## Key Patterns
### Concept Definition (C++20)
```cpp
// Define a reusable, self-documenting constraint
template<typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;
template<Numeric T>
T clamp(T value, T lo, T hi) {
return std::clamp(value, lo, hi);
}
```
### RAII Resource Wrapper
```cpp
// Wraps a raw handle; no manual cleanup needed at call sites
class FileHandle {
public:
explicit FileHandle(const char* path)
: handle_(std::fopen(path, "r")) {
if (!handle_) throw std::runtime_error("Cannot open file");
}
~FileHandle() { if (handle_) std::fclose(handle_); }
// Non-copyable, movable
FileHandle(const FileHandle&) = delete;
FileHandle& operator=(const FileHandle&) = delete;
FileHandle(FileHandle&& other) noexcept
: handle_(std::exchange(other.handle_, nullptr)) {}
std::FILE* get() const noexcept { return handle_; }
private:
std::FILE* handle_;
};
```
### Smart Pointer Ownership
```cpp
// Prefer make_unique / make_shared; avoid raw new/delete
auto buffer = std::make_unique<std::array<std::byte, 4096>>();
// Shared ownership only when genuinely needed
auto config = std::make_shared<Config>(parseArgs(argc, argv));
```
## Output Templates
When implementing C++ features, provide:
1. Header file with interfaces and templates
2. Implementation file (when needed)
3. CMakeLists.txt updates (if applicable)
4. Test file demonstrating usage
5. Brief explanation of design decisions and performance characteristics
[Documentation](https://jeffallan.github.io/claude-skills/skills/language/cpp-pro/)
@@ -1,534 +0,0 @@
# Build Systems and Tooling
## Modern CMake
```cmake
cmake_minimum_required(VERSION 3.20)
project(MyProject VERSION 1.0.0 LANGUAGES CXX)
# Set C++ standard
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Export compile commands for tools
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Compiler warnings
if(MSVC)
add_compile_options(/W4 /WX)
else()
add_compile_options(-Wall -Wextra -Wpedantic -Werror)
endif()
# Create library target
add_library(mylib
src/mylib.cpp
include/mylib.h
)
target_include_directories(mylib
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
)
target_compile_features(mylib PUBLIC cxx_std_20)
# Create executable
add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE mylib)
# Dependencies with FetchContent
include(FetchContent)
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 10.1.1
)
FetchContent_MakeAvailable(fmt)
target_link_libraries(mylib PUBLIC fmt::fmt)
# Testing
enable_testing()
add_subdirectory(tests)
# Install rules
include(GNUInstallDirs)
install(TARGETS mylib myapp
EXPORT MyProjectTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(DIRECTORY include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
```
## Sanitizers
```cmake
# AddressSanitizer (ASan) - memory errors
set(CMAKE_CXX_FLAGS_ASAN
"-g -O1 -fsanitize=address -fno-omit-frame-pointer"
CACHE STRING "Flags for ASan build"
)
# UndefinedBehaviorSanitizer (UBSan)
set(CMAKE_CXX_FLAGS_UBSAN
"-g -O1 -fsanitize=undefined -fno-omit-frame-pointer"
CACHE STRING "Flags for UBSan build"
)
# ThreadSanitizer (TSan) - data races
set(CMAKE_CXX_FLAGS_TSAN
"-g -O1 -fsanitize=thread -fno-omit-frame-pointer"
CACHE STRING "Flags for TSan build"
)
# MemorySanitizer (MSan) - uninitialized reads
set(CMAKE_CXX_FLAGS_MSAN
"-g -O1 -fsanitize=memory -fno-omit-frame-pointer"
CACHE STRING "Flags for MSan build"
)
# Usage: cmake -DCMAKE_BUILD_TYPE=ASAN ..
```
## Static Analysis
```yaml
# .clang-tidy configuration
---
Checks: >
-*,
bugprone-*,
cert-*,
clang-analyzer-*,
cppcoreguidelines-*,
misc-*,
performance-*
WarningsAsErrors: '*'
```
```bash
# Analyze production translation units from the compilation database.
# Deduplicate entries first when multiple targets compile the same source.
run-clang-tidy -p build -j 8 'src/.*\.cpp'
# Run cppcheck
cppcheck --enable=all --std=c++20 --suppress=missingInclude src/
# Run include-what-you-use
include-what-you-use -std=c++20 src/main.cpp
```
Prefer checks that identify correctness, security, undefined behavior, and
performance defects. Broad `readability-*` and `modernize-*` families often
create review churn and slow blocking gates; keep them opt-in or non-blocking
unless the project has adopted those rules explicitly.
Static analysis should consume the generated compilation database, analyze
each production translation unit once, and use bounded parallelism. Do not
scan headers independently or analyze test targets when the same headers and
sources are already covered through production translation units.
## Fast, High-Signal PR Gates
Measure each gate before removing checks. Optimize the critical path and cache
invalidation first:
- Provide one canonical PR command. CI should invoke it rather than repeat its
format, test, package, or smoke-test steps.
- Model independent checks as separate cacheable build derivations. Make the
umbrella PR gate depend on their outputs so the build system can run them in
parallel and reuse unchanged results.
- Give each derivation the smallest source closure it needs. Documentation,
benchmarks, tests, and agent instructions should not invalidate a production
package build.
- Build production with `BUILD_TESTING=OFF`. Use a dedicated test derivation
with `BUILD_TESTING=ON` to compile and execute tests.
- Keep runtime smoke tests with the installed package. This validates packaging
and dynamic linkage without rerunning the same command in CI.
- Avoid registering format or static-analysis commands in CTest when they
already have dedicated gates.
- Parallelize expensive per-translation-unit tools with a conservative worker
cap to avoid memory pressure and contention with concurrent builds.
Example Nix source filtering:
```nix
productionSource = lib.cleanSourceWith {
src = ./.;
filter = path: _type:
let
root = toString ./.;
relative = lib.removePrefix "${root}/" (toString path);
in
toString path == root
|| relative == "CMakeLists.txt"
|| relative == "cmake"
|| lib.hasPrefix "cmake/" relative
|| relative == "src"
|| lib.hasPrefix "src/" relative;
};
```
For a Nix-based CMake project, keep production and test configurations
separate:
```nix
cmakeFlags = [
"-DCMAKE_BUILD_TYPE=RelWithDebInfo"
"-DBUILD_TESTING=OFF"
];
testCheck = pkgs.runCommand "check-tests" {
src = testSource;
nativeBuildInputs = [ pkgs.cmake pkgs.ninja pkgs.stdenv.cc ];
} ''
cmake -S "$src" -B build -GNinja -DBUILD_TESTING=ON
cmake --build build
ctest --test-dir build --output-on-failure
touch "$out"
'';
```
## Testing with Catch2
```cpp
#include <catch2/catch_test_macros.hpp>
#include <catch2/benchmark/catch_benchmark.hpp>
#include "mylib.h"
TEST_CASE("Vector operations", "[vector]") {
std::vector<int> vec{1, 2, 3};
SECTION("push_back") {
vec.push_back(4);
REQUIRE(vec.size() == 4);
REQUIRE(vec.back() == 4);
}
SECTION("pop_back") {
vec.pop_back();
REQUIRE(vec.size() == 2);
REQUIRE(vec.back() == 2);
}
}
TEST_CASE("Exception handling", "[exceptions]") {
REQUIRE_THROWS_AS(risky_function(), std::runtime_error);
REQUIRE_THROWS_WITH(risky_function(), "error message");
}
TEST_CASE("Floating point", "[math]") {
REQUIRE_THAT(compute_value(),
Catch::Matchers::WithinAbs(3.14, 0.01));
}
BENCHMARK("Vector creation") {
return std::vector<int>(1000);
};
BENCHMARK("Vector fill") {
std::vector<int> vec(1000);
for (int i = 0; i < 1000; ++i) {
vec[i] = i;
}
return vec;
};
```
## Testing with GoogleTest
```cpp
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "calculator.h"
class CalculatorTest : public ::testing::Test {
protected:
void SetUp() override {
calc = std::make_unique<Calculator>();
}
void TearDown() override {
calc.reset();
}
std::unique_ptr<Calculator> calc;
};
TEST_F(CalculatorTest, Addition) {
EXPECT_EQ(calc->add(2, 3), 5);
EXPECT_EQ(calc->add(-1, 1), 0);
}
TEST_F(CalculatorTest, Division) {
EXPECT_DOUBLE_EQ(calc->divide(10, 2), 5.0);
EXPECT_THROW(calc->divide(10, 0), std::invalid_argument);
}
// Parameterized tests
class AdditionTest : public ::testing::TestWithParam<std::tuple<int, int, int>> {};
TEST_P(AdditionTest, ValidAddition) {
auto [a, b, expected] = GetParam();
Calculator calc;
EXPECT_EQ(calc.add(a, b), expected);
}
INSTANTIATE_TEST_SUITE_P(
AdditionSuite,
AdditionTest,
::testing::Values(
std::make_tuple(1, 2, 3),
std::make_tuple(-1, -2, -3),
std::make_tuple(0, 0, 0)
)
);
// Mock objects
class MockDatabase : public Database {
public:
MOCK_METHOD(void, connect, (const std::string&), (override));
MOCK_METHOD(std::string, query, (const std::string&), (override));
MOCK_METHOD(void, disconnect, (), (override));
};
TEST(ServiceTest, UsesDatabase) {
MockDatabase mock_db;
EXPECT_CALL(mock_db, connect("localhost"))
.Times(1);
EXPECT_CALL(mock_db, query("SELECT *"))
.WillOnce(::testing::Return("result"));
Service service(mock_db);
service.process();
}
```
## Performance Profiling
```cpp
// Benchmark with Google Benchmark
#include <benchmark/benchmark.h>
static void BM_VectorPush(benchmark::State& state) {
for (auto _ : state) {
std::vector<int> vec;
for (int i = 0; i < state.range(0); ++i) {
vec.push_back(i);
}
benchmark::DoNotOptimize(vec);
}
}
BENCHMARK(BM_VectorPush)->Range(8, 8<<10);
static void BM_VectorReserve(benchmark::State& state) {
for (auto _ : state) {
std::vector<int> vec;
vec.reserve(state.range(0));
for (int i = 0; i < state.range(0); ++i) {
vec.push_back(i);
}
benchmark::DoNotOptimize(vec);
}
}
BENCHMARK(BM_VectorReserve)->Range(8, 8<<10);
BENCHMARK_MAIN();
```
```bash
# Profile Strix Halo HIP kernels with the repository-pinned profiler.
nix develop -c rocprofv3 \
--output-directory /tmp/gufo-rocprof \
--output-format csv \
--kernel-trace \
--stats \
--summary \
--summary-units msec \
-- ./result/bin/gufo-server bench \
--model /path/to/model.gguf \
--n-prompt 512 \
--n-gen 0 \
--repetitions 1
# Add this when private-memory traffic or register pressure is suspected.
nix develop -c rocprofv3 \
--output-directory /tmp/gufo-rocprof-scratch \
--output-format csv \
--kernel-trace \
--scratch-memory-trace \
--stats \
--summary \
-- ./result/bin/gufo-server bench \
--model /path/to/model.gguf \
--n-prompt 512 \
--n-gen 0 \
--repetitions 1
# Profiling with perf (Linux)
perf record -g ./myapp
perf report
# Profiling with Instruments (macOS)
instruments -t "Time Profiler" ./myapp
# Valgrind callgrind
valgrind --tool=callgrind ./myapp
kcachegrind callgrind.out.*
# Memory profiling
valgrind --tool=massif ./myapp
ms_print massif.out.*
```
For Strix Halo work, use `nix develop -c rocprofv3`; do not depend on a
globally installed ROCm profiler. Keep traces bounded to one warmed repetition
and write raw CSV output under `/tmp` or an ignored artifact directory.
Profile to identify the dominant cost, then benchmark without profiler
instrumentation because tracing distorts wall time. Retain a HIP optimization
only after the relevant kernel/oracle tests, full-logit comparison, and
unprofiled end-to-end benchmark pass.
## Conan Package Manager
```python
# conanfile.txt
[requires]
fmt/10.1.1
spdlog/1.12.0
catch2/3.4.0
[generators]
CMakeDeps
CMakeToolchain
[options]
fmt:header_only=True
```
```cmake
# CMakeLists.txt with Conan
cmake_minimum_required(VERSION 3.20)
project(MyProject)
find_package(fmt REQUIRED)
find_package(spdlog REQUIRED)
find_package(Catch2 REQUIRED)
add_executable(myapp src/main.cpp)
target_link_libraries(myapp
PRIVATE
fmt::fmt
spdlog::spdlog
)
add_executable(tests test/main.cpp)
target_link_libraries(tests
PRIVATE
Catch2::Catch2WithMain
)
```
```bash
# Install dependencies
conan install . --output-folder=build --build=missing
cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake
cmake --build .
```
## CI/CD with GitHub Actions
```yaml
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
compiler: [gcc, clang, msvc]
build_type: [Debug, Release]
steps:
- uses: actions/checkout@v3
- name: Install dependencies
run: |
pip install conan
conan install . --output-folder=build --build=missing
- name: Configure
run: |
cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
- name: Build
run: cmake --build build --config ${{ matrix.build_type }}
- name: Test
run: ctest --test-dir build -C ${{ matrix.build_type }}
sanitizers:
runs-on: ubuntu-latest
strategy:
matrix:
sanitizer: [asan, ubsan, tsan]
steps:
- uses: actions/checkout@v3
- name: Build with sanitizer
run: |
cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.sanitizer }}
cmake --build build
- name: Run tests
run: ctest --test-dir build
static-analysis:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run clang-tidy
run: |
cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
clang-tidy src/*.cpp -p build/
- name: Run cppcheck
run: cppcheck --enable=all --error-exitcode=1 src/
```
## Quick Reference
| Tool | Purpose | Command |
|------|---------|---------|
| CMake | Build system | `cmake -B build && cmake --build build` |
| Conan | Package manager | `conan install . --build=missing` |
| ASan | Memory errors | `-fsanitize=address` |
| UBSan | Undefined behavior | `-fsanitize=undefined` |
| TSan | Data races | `-fsanitize=thread` |
| clang-tidy | Static analysis | `clang-tidy src/*.cpp` |
| cppcheck | Static analysis | `cppcheck --enable=all src/` |
| Catch2 | Unit testing | `TEST_CASE("name") { REQUIRE(...); }` |
| GoogleTest | Unit testing | `TEST(Suite, Name) { EXPECT_EQ(...); }` |
| Google Benchmark | Performance | `BENCHMARK(func)->Range(...)` |
| rocprofv3 | ROCm kernel and scratch profiling | `nix develop -c rocprofv3` |
| Valgrind | Memory profiler | `valgrind --tool=memcheck ./app` |
@@ -1,437 +0,0 @@
# Concurrency and Parallel Programming
## Atomics and Memory Ordering
```cpp
#include <atomic>
#include <thread>
// Basic atomics
std::atomic<int> counter{0};
std::atomic<bool> flag{false};
// Memory ordering
void producer(std::atomic<int>& data, std::atomic<bool>& ready) {
data.store(42, std::memory_order_relaxed);
ready.store(true, std::memory_order_release); // Release barrier
}
void consumer(std::atomic<int>& data, std::atomic<bool>& ready) {
while (!ready.load(std::memory_order_acquire)) { // Acquire barrier
std::this_thread::yield();
}
int value = data.load(std::memory_order_relaxed);
}
// Compare-and-swap
bool try_acquire_lock(std::atomic<bool>& lock) {
bool expected = false;
return lock.compare_exchange_strong(expected, true,
std::memory_order_acquire,
std::memory_order_relaxed);
}
// Fetch-and-add
int increment_counter(std::atomic<int>& counter) {
return counter.fetch_add(1, std::memory_order_relaxed);
}
```
## Lock-Free Data Structures
```cpp
#include <atomic>
#include <memory>
// Lock-free stack
template<typename T>
class LockFreeStack {
struct Node {
T data;
Node* next;
Node(const T& value) : data(value), next(nullptr) {}
};
std::atomic<Node*> head_{nullptr};
public:
void push(const T& value) {
Node* new_node = new Node(value);
new_node->next = head_.load(std::memory_order_relaxed);
while (!head_.compare_exchange_weak(new_node->next, new_node,
std::memory_order_release,
std::memory_order_relaxed)) {
// Retry with updated head
}
}
bool pop(T& result) {
Node* old_head = head_.load(std::memory_order_relaxed);
while (old_head &&
!head_.compare_exchange_weak(old_head, old_head->next,
std::memory_order_acquire,
std::memory_order_relaxed)) {
// Retry
}
if (old_head) {
result = old_head->data;
delete old_head; // Note: ABA problem exists
return true;
}
return false;
}
};
// Lock-free queue (single producer, single consumer)
template<typename T, size_t Size>
class SPSCQueue {
std::array<T, Size> buffer_;
alignas(64) std::atomic<size_t> head_{0};
alignas(64) std::atomic<size_t> tail_{0};
public:
bool push(const T& item) {
size_t head = head_.load(std::memory_order_relaxed);
size_t next_head = (head + 1) % Size;
if (next_head == tail_.load(std::memory_order_acquire)) {
return false; // Queue full
}
buffer_[head] = item;
head_.store(next_head, std::memory_order_release);
return true;
}
bool pop(T& item) {
size_t tail = tail_.load(std::memory_order_relaxed);
if (tail == head_.load(std::memory_order_acquire)) {
return false; // Queue empty
}
item = buffer_[tail];
tail_.store((tail + 1) % Size, std::memory_order_release);
return true;
}
};
```
## Thread Pool
```cpp
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <future>
class ThreadPool {
std::vector<std::thread> workers_;
std::queue<std::function<void()>> tasks_;
std::mutex queue_mutex_;
std::condition_variable condition_;
bool stop_ = false;
public:
ThreadPool(size_t num_threads) {
for (size_t i = 0; i < num_threads; ++i) {
workers_.emplace_back([this] {
while (true) {
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(queue_mutex_);
condition_.wait(lock, [this] {
return stop_ || !tasks_.empty();
});
if (stop_ && tasks_.empty()) {
return;
}
task = std::move(tasks_.front());
tasks_.pop();
}
task();
}
});
}
}
~ThreadPool() {
{
std::unique_lock<std::mutex> lock(queue_mutex_);
stop_ = true;
}
condition_.notify_all();
for (auto& worker : workers_) {
worker.join();
}
}
template<typename F, typename... Args>
auto enqueue(F&& f, Args&&... args)
-> std::future<typename std::invoke_result_t<F, Args...>> {
using return_type = typename std::invoke_result_t<F, Args...>;
auto task = std::make_shared<std::packaged_task<return_type()>>(
std::bind(std::forward<F>(f), std::forward<Args>(args)...)
);
std::future<return_type> result = task->get_future();
{
std::unique_lock<std::mutex> lock(queue_mutex_);
if (stop_) {
throw std::runtime_error("enqueue on stopped ThreadPool");
}
tasks_.emplace([task]() { (*task)(); });
}
condition_.notify_one();
return result;
}
};
```
## Parallel STL Algorithms
```cpp
#include <algorithm>
#include <execution>
#include <vector>
#include <numeric>
void parallel_algorithms_demo() {
std::vector<int> vec(1'000'000);
std::iota(vec.begin(), vec.end(), 0);
// Parallel sort
std::sort(std::execution::par, vec.begin(), vec.end());
// Parallel for_each
std::for_each(std::execution::par_unseq, vec.begin(), vec.end(),
[](int& x) { x *= 2; });
// Parallel transform
std::vector<int> result(vec.size());
std::transform(std::execution::par, vec.begin(), vec.end(),
result.begin(), [](int x) { return x * x; });
// Parallel reduce
int sum = std::reduce(std::execution::par, vec.begin(), vec.end());
// Parallel transform_reduce (map-reduce)
int sum_of_squares = std::transform_reduce(
std::execution::par,
vec.begin(), vec.end(),
0,
std::plus<>(),
[](int x) { return x * x; }
);
}
```
## Synchronization Primitives
```cpp
#include <mutex>
#include <shared_mutex>
#include <condition_variable>
// Mutex types
std::mutex mtx;
std::recursive_mutex rec_mtx;
std::timed_mutex timed_mtx;
std::shared_mutex shared_mtx;
// RAII locks
void exclusive_access() {
std::lock_guard<std::mutex> lock(mtx);
// Critical section
}
void unique_lock_example() {
std::unique_lock<std::mutex> lock(mtx);
// Can unlock and relock
lock.unlock();
// Do some work
lock.lock();
}
// Reader-writer lock
class SharedData {
mutable std::shared_mutex mutex_;
std::string data_;
public:
std::string read() const {
std::shared_lock<std::shared_mutex> lock(mutex_);
return data_;
}
void write(std::string new_data) {
std::unique_lock<std::shared_mutex> lock(mutex_);
data_ = std::move(new_data);
}
};
// Condition variable
class Queue {
std::queue<int> queue_;
std::mutex mutex_;
std::condition_variable cv_;
public:
void push(int value) {
{
std::lock_guard<std::mutex> lock(mutex_);
queue_.push(value);
}
cv_.notify_one();
}
int pop() {
std::unique_lock<std::mutex> lock(mutex_);
cv_.wait(lock, [this] { return !queue_.empty(); });
int value = queue_.front();
queue_.pop();
return value;
}
};
// std::scoped_lock - multiple mutexes
std::mutex mtx1, mtx2;
void transfer(Account& from, Account& to, int amount) {
std::scoped_lock lock(from.mutex, to.mutex); // Deadlock-free
from.balance -= amount;
to.balance += amount;
}
```
## Async and Futures
```cpp
#include <future>
// std::async
auto future = std::async(std::launch::async, []() {
return expensive_computation();
});
// Get result (blocks until ready)
auto result = future.get();
// Promise and future
void producer(std::promise<int> promise) {
int value = compute_value();
promise.set_value(value);
}
void consumer(std::future<int> future) {
int value = future.get();
}
std::promise<int> promise;
std::future<int> future = promise.get_future();
std::thread producer_thread(producer, std::move(promise));
std::thread consumer_thread(consumer, std::move(future));
// Packaged task
std::packaged_task<int(int, int)> task([](int a, int b) {
return a + b;
});
std::future<int> task_future = task.get_future();
std::thread task_thread(std::move(task), 5, 3);
int sum = task_future.get(); // 8
task_thread.join();
```
## Coroutine-Based Concurrency
```cpp
#include <coroutine>
#include <optional>
// Async task coroutine
template<typename T>
struct AsyncTask {
struct promise_type {
std::optional<T> value;
std::exception_ptr exception;
AsyncTask get_return_object() {
return AsyncTask{
std::coroutine_handle<promise_type>::from_promise(*this)
};
}
std::suspend_never initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void return_value(T v) {
value = std::move(v);
}
void unhandled_exception() {
exception = std::current_exception();
}
};
std::coroutine_handle<promise_type> handle;
AsyncTask(std::coroutine_handle<promise_type> h) : handle(h) {}
~AsyncTask() { if (handle) handle.destroy(); }
T get() {
if (!handle.done()) {
handle.resume();
}
if (handle.promise().exception) {
std::rethrow_exception(handle.promise().exception);
}
return *handle.promise().value;
}
};
// Usage
AsyncTask<int> async_compute() {
co_return 42;
}
```
## Quick Reference
| Primitive | Use Case | Performance |
|-----------|----------|-------------|
| std::atomic | Simple shared state | Lock-free |
| std::mutex | Exclusive access | Kernel call |
| std::shared_mutex | Read-heavy workload | Better than mutex |
| Lock-free structures | High contention | Best throughput |
| Thread pool | Task parallelism | Avoid thread overhead |
| Parallel STL | Data parallelism | Automatic scaling |
| std::async | Simple async tasks | Thread pool |
| Coroutines | Async I/O | Minimal overhead |
## Memory Ordering Guide
| Ordering | Guarantees | Use Case |
|----------|-----------|----------|
| relaxed | No synchronization | Counters |
| acquire | Load barrier | Consumer |
| release | Store barrier | Producer |
| acq_rel | Both | RMW operations |
| seq_cst | Total order | Default |
@@ -1,397 +0,0 @@
# Memory Management & Performance
## Smart Pointers
```cpp
#include <memory>
// unique_ptr - exclusive ownership
auto create_resource() {
return std::make_unique<Resource>("data");
}
// shared_ptr - reference counting
std::shared_ptr<Data> shared = std::make_shared<Data>(42);
std::weak_ptr<Data> weak = shared; // Non-owning reference
// Custom deleters
auto file_deleter = [](FILE* fp) { if (fp) fclose(fp); };
std::unique_ptr<FILE, decltype(file_deleter)> file(
fopen("data.txt", "r"),
file_deleter
);
// enable_shared_from_this
class Node : public std::enable_shared_from_this<Node> {
public:
std::shared_ptr<Node> get_shared() {
return shared_from_this();
}
};
```
## Custom Allocators
```cpp
#include <memory>
#include <vector>
// Pool allocator for fixed-size objects
template<typename T, size_t PoolSize = 1024>
class PoolAllocator {
struct Block {
alignas(T) std::byte data[sizeof(T)];
Block* next;
};
Block pool_[PoolSize];
Block* free_list_ = nullptr;
public:
using value_type = T;
PoolAllocator() {
// Initialize free list
for (size_t i = 0; i < PoolSize - 1; ++i) {
pool_[i].next = &pool_[i + 1];
}
pool_[PoolSize - 1].next = nullptr;
free_list_ = &pool_[0];
}
T* allocate(size_t n) {
if (n != 1 || !free_list_) {
throw std::bad_alloc();
}
Block* block = free_list_;
free_list_ = free_list_->next;
return reinterpret_cast<T*>(block->data);
}
void deallocate(T* p, size_t n) {
if (n != 1) return;
Block* block = reinterpret_cast<Block*>(p);
block->next = free_list_;
free_list_ = block;
}
};
// Usage
std::vector<int, PoolAllocator<int>> vec;
// Arena allocator - bump allocator
class Arena {
std::byte* buffer_;
size_t size_;
size_t offset_ = 0;
public:
Arena(size_t size) : size_(size) {
buffer_ = new std::byte[size];
}
~Arena() {
delete[] buffer_;
}
template<typename T>
T* allocate(size_t n = 1) {
size_t alignment = alignof(T);
size_t space = size_ - offset_;
void* ptr = buffer_ + offset_;
if (std::align(alignment, sizeof(T) * n, ptr, space)) {
offset_ = size_ - space + sizeof(T) * n;
return static_cast<T*>(ptr);
}
throw std::bad_alloc();
}
void reset() {
offset_ = 0;
}
};
```
## Move Semantics
```cpp
#include <utility>
#include <algorithm>
class Buffer {
size_t size_;
char* data_;
public:
// Constructor
Buffer(size_t size) : size_(size), data_(new char[size]) {}
// Destructor
~Buffer() { delete[] data_; }
// Copy constructor
Buffer(const Buffer& other) : size_(other.size_), data_(new char[size_]) {
std::copy(other.data_, other.data_ + size_, data_);
}
// Copy assignment
Buffer& operator=(const Buffer& other) {
if (this != &other) {
delete[] data_;
size_ = other.size_;
data_ = new char[size_];
std::copy(other.data_, other.data_ + size_, data_);
}
return *this;
}
// Move constructor
Buffer(Buffer&& other) noexcept
: size_(other.size_), data_(other.data_) {
other.size_ = 0;
other.data_ = nullptr;
}
// Move assignment
Buffer& operator=(Buffer&& other) noexcept {
if (this != &other) {
delete[] data_;
size_ = other.size_;
data_ = other.data_;
other.size_ = 0;
other.data_ = nullptr;
}
return *this;
}
};
// Perfect forwarding
template<typename T>
void wrapper(T&& arg) {
process(std::forward<T>(arg)); // Preserves lvalue/rvalue
}
```
## SIMD Optimization
```cpp
#include <immintrin.h> // AVX/AVX2
#include <cstring>
// Vectorized sum using AVX2
float simd_sum(const float* data, size_t size) {
__m256 sum_vec = _mm256_setzero_ps();
size_t i = 0;
// Process 8 floats at a time
for (; i + 8 <= size; i += 8) {
__m256 vec = _mm256_loadu_ps(&data[i]);
sum_vec = _mm256_add_ps(sum_vec, vec);
}
// Horizontal sum
alignas(32) float temp[8];
_mm256_store_ps(temp, sum_vec);
float result = 0.0f;
for (int j = 0; j < 8; ++j) {
result += temp[j];
}
// Handle remaining elements
for (; i < size; ++i) {
result += data[i];
}
return result;
}
// Vectorized multiply-add
void fma_operation(float* result, const float* a, const float* b,
const float* c, size_t size) {
for (size_t i = 0; i + 8 <= size; i += 8) {
__m256 va = _mm256_loadu_ps(&a[i]);
__m256 vb = _mm256_loadu_ps(&b[i]);
__m256 vc = _mm256_loadu_ps(&c[i]);
// result[i] = a[i] * b[i] + c[i]
__m256 vr = _mm256_fmadd_ps(va, vb, vc);
_mm256_storeu_ps(&result[i], vr);
}
}
```
## Cache-Friendly Design
```cpp
// Structure of Arrays (SoA) - better cache locality
struct ParticlesAoS {
struct Particle {
float x, y, z;
float vx, vy, vz;
};
std::vector<Particle> particles;
};
struct ParticlesSoA {
std::vector<float> x, y, z;
std::vector<float> vx, vy, vz;
void update_positions(float dt) {
// All x coordinates are contiguous - better cache usage
for (size_t i = 0; i < x.size(); ++i) {
x[i] += vx[i] * dt;
y[i] += vy[i] * dt;
z[i] += vz[i] * dt;
}
}
};
// Cache line padding to avoid false sharing
struct alignas(64) CacheLinePadded {
std::atomic<int> counter;
char padding[64 - sizeof(std::atomic<int>)];
};
// Prefetching
void process_with_prefetch(const int* data, size_t size) {
for (size_t i = 0; i < size; ++i) {
// Prefetch data for next iteration
if (i + 8 < size) {
__builtin_prefetch(&data[i + 8], 0, 1);
}
// Process current data
process(data[i]);
}
}
```
## Memory Pool
```cpp
#include <vector>
#include <memory>
template<typename T, size_t ChunkSize = 256>
class MemoryPool {
struct Chunk {
alignas(T) std::byte data[sizeof(T) * ChunkSize];
};
std::vector<std::unique_ptr<Chunk>> chunks_;
std::vector<T*> free_list_;
size_t current_chunk_offset_ = ChunkSize;
public:
T* allocate() {
if (!free_list_.empty()) {
T* ptr = free_list_.back();
free_list_.pop_back();
return ptr;
}
if (current_chunk_offset_ >= ChunkSize) {
chunks_.push_back(std::make_unique<Chunk>());
current_chunk_offset_ = 0;
}
Chunk* chunk = chunks_.back().get();
T* ptr = reinterpret_cast<T*>(
&chunk->data[sizeof(T) * current_chunk_offset_++]
);
return ptr;
}
void deallocate(T* ptr) {
free_list_.push_back(ptr);
}
template<typename... Args>
T* construct(Args&&... args) {
T* ptr = allocate();
new (ptr) T(std::forward<Args>(args)...);
return ptr;
}
void destroy(T* ptr) {
ptr->~T();
deallocate(ptr);
}
};
```
## Copy Elision and RVO
```cpp
// Return Value Optimization (RVO)
std::vector<int> create_vector() {
std::vector<int> vec{1, 2, 3, 4, 5};
return vec; // RVO applies, no copy/move
}
// Named Return Value Optimization (NRVO)
std::string build_string(bool condition) {
std::string result;
if (condition) {
result = "condition true";
} else {
result = "condition false";
}
return result; // NRVO may apply
}
// Guaranteed copy elision (C++17)
struct NonMovable {
NonMovable() = default;
NonMovable(const NonMovable&) = delete;
NonMovable(NonMovable&&) = delete;
};
NonMovable create() {
return NonMovable{}; // Guaranteed no copy/move in C++17
}
auto obj = create(); // OK in C++17
```
## Alignment and Memory Layout
```cpp
#include <cstddef>
// Control alignment
struct alignas(64) CacheAligned {
int data[16];
};
// Check alignment
static_assert(alignof(CacheAligned) == 64);
// Aligned allocation
void* aligned_alloc_wrapper(size_t alignment, size_t size) {
void* ptr = nullptr;
if (posix_memalign(&ptr, alignment, size) != 0) {
throw std::bad_alloc();
}
return ptr;
}
// Placement new with alignment
alignas(32) std::byte buffer[sizeof(Data)];
Data* obj = new (buffer) Data();
obj->~Data(); // Manual destruction needed
```
## Quick Reference
| Technique | Use Case | Benefit |
|-----------|----------|---------|
| Smart Pointers | Ownership management | Memory safety |
| Move Semantics | Avoid copies | Performance |
| Custom Allocators | Specialized allocation | Speed + control |
| SIMD | Parallel computation | 4-8x speedup |
| SoA Layout | Sequential access | Cache efficiency |
| Memory Pools | Frequent alloc/dealloc | Reduced fragmentation |
| Alignment | SIMD/cache optimization | Performance |
| RVO/NRVO | Return objects | Zero-copy |
@@ -1,304 +0,0 @@
# Modern C++20/23 Features
## Concepts and Constraints
```cpp
#include <concepts>
// Define custom concepts
template<typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;
template<typename T>
concept Hashable = requires(T a) {
{ std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;
};
template<typename T>
concept Container = requires(T c) {
typename T::value_type;
typename T::iterator;
{ c.begin() } -> std::same_as<typename T::iterator>;
{ c.end() } -> std::same_as<typename T::iterator>;
{ c.size() } -> std::convertible_to<std::size_t>;
};
// Use concepts for function constraints
template<Numeric T>
T add(T a, T b) {
return a + b;
}
// Concept-based overloading
template<std::integral T>
void process(T value) {
std::cout << "Processing integer: " << value << '\n';
}
template<std::floating_point T>
void process(T value) {
std::cout << "Processing float: " << value << '\n';
}
```
## Ranges and Views
```cpp
#include <ranges>
#include <vector>
#include <algorithm>
// Ranges-based algorithms
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// Filter, transform, take - all lazy evaluation
auto result = numbers
| std::views::filter([](int n) { return n % 2 == 0; })
| std::views::transform([](int n) { return n * n; })
| std::views::take(3);
// Copy to vector only when needed
std::vector<int> materialized(result.begin(), result.end());
// Custom range adaptor
auto is_even = [](int n) { return n % 2 == 0; };
auto square = [](int n) { return n * n; };
auto pipeline = std::views::filter(is_even)
| std::views::transform(square);
auto processed = numbers | pipeline;
```
## Coroutines
```cpp
#include <coroutine>
#include <iostream>
#include <memory>
// Generator coroutine
template<typename T>
struct Generator {
struct promise_type {
T current_value;
auto get_return_object() {
return Generator{std::coroutine_handle<promise_type>::from_promise(*this)};
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
std::suspend_always yield_value(T value) {
current_value = value;
return {};
}
void return_void() {}
void unhandled_exception() { std::terminate(); }
};
std::coroutine_handle<promise_type> handle;
Generator(std::coroutine_handle<promise_type> h) : handle(h) {}
~Generator() { if (handle) handle.destroy(); }
bool move_next() {
handle.resume();
return !handle.done();
}
T current_value() {
return handle.promise().current_value;
}
};
// Usage
Generator<int> fibonacci() {
int a = 0, b = 1;
while (true) {
co_yield a;
auto next = a + b;
a = b;
b = next;
}
}
// Async coroutine
#include <future>
struct Task {
struct promise_type {
Task get_return_object() {
return Task{std::coroutine_handle<promise_type>::from_promise(*this)};
}
std::suspend_never initial_suspend() { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void return_void() {}
void unhandled_exception() {}
};
std::coroutine_handle<promise_type> handle;
};
Task async_operation() {
std::cout << "Starting async work\n";
co_await std::suspend_always{};
std::cout << "Resuming async work\n";
}
```
## Three-Way Comparison (Spaceship)
```cpp
#include <compare>
struct Point {
int x, y;
// Auto-generate all comparison operators
auto operator<=>(const Point&) const = default;
};
// Custom spaceship operator
struct Version {
int major, minor, patch;
std::strong_ordering operator<=>(const Version& other) const {
if (auto cmp = major <=> other.major; cmp != 0) return cmp;
if (auto cmp = minor <=> other.minor; cmp != 0) return cmp;
return patch <=> other.patch;
}
bool operator==(const Version& other) const = default;
};
```
## Designated Initializers
```cpp
struct Config {
std::string host = "localhost";
int port = 8080;
bool ssl_enabled = false;
int timeout_ms = 5000;
};
// C++20 designated initializers
Config cfg {
.host = "example.com",
.port = 443,
.ssl_enabled = true
// timeout_ms uses default
};
```
## Modules (C++20)
```cpp
// math.cppm - module interface
export module math;
export namespace math {
template<typename T>
T add(T a, T b) {
return a + b;
}
class Calculator {
public:
int multiply(int a, int b);
};
}
// Implementation
module math;
int math::Calculator::multiply(int a, int b) {
return a * b;
}
// Usage in other files
import math;
int main() {
auto result = math::add(5, 3);
math::Calculator calc;
auto product = calc.multiply(4, 7);
}
```
## constexpr Enhancements
```cpp
#include <string>
#include <vector>
#include <algorithm>
// C++20: constexpr std::string and std::vector
constexpr auto compute_at_compile_time() {
std::vector<int> vec{1, 2, 3, 4, 5};
std::ranges::reverse(vec);
return vec[0]; // Returns 5
}
constexpr int value = compute_at_compile_time();
// constexpr virtual functions (C++20)
struct Base {
constexpr virtual int get_value() const { return 42; }
constexpr virtual ~Base() = default;
};
struct Derived : Base {
constexpr int get_value() const override { return 100; }
};
```
## std::format (C++20)
```cpp
#include <format>
#include <iostream>
int main() {
std::string msg = std::format("Hello, {}!", "World");
// Positional arguments
auto text = std::format("{1} {0}", "World", "Hello");
// Formatting options
double pi = 3.14159265;
auto formatted = std::format("Pi: {:.2f}", pi); // "Pi: 3.14"
// Custom types
struct Point { int x, y; };
}
// Custom formatter
template<>
struct std::formatter<Point> {
constexpr auto parse(format_parse_context& ctx) {
return ctx.begin();
}
auto format(const Point& p, format_context& ctx) const {
return std::format_to(ctx.out(), "({}, {})", p.x, p.y);
}
};
```
## Quick Reference
| Feature | C++17 | C++20 | C++23 |
|---------|-------|-------|-------|
| Concepts | - | ✓ | ✓ |
| Ranges | - | ✓ | ✓ |
| Coroutines | - | ✓ | ✓ |
| Modules | - | ✓ | ✓ |
| Spaceship | - | ✓ | ✓ |
| std::format | - | ✓ | ✓ |
| std::expected | - | - | ✓ |
| std::print | - | - | ✓ |
| Deducing this | - | - | ✓ |
@@ -1,357 +0,0 @@
# Template Metaprogramming
## Variadic Templates
```cpp
#include <iostream>
#include <utility>
// Fold expressions (C++17)
template<typename... Args>
auto sum(Args... args) {
return (args + ...); // Unary right fold
}
template<typename... Args>
void print(Args&&... args) {
((std::cout << args << ' '), ...); // Binary left fold
std::cout << '\n';
}
// Recursive variadic template
template<typename T>
void log(T&& value) {
std::cout << value << '\n';
}
template<typename T, typename... Args>
void log(T&& first, Args&&... rest) {
std::cout << first << ", ";
log(std::forward<Args>(rest)...);
}
// Parameter pack expansion
template<typename... Types>
struct TypeList {
static constexpr size_t size = sizeof...(Types);
};
template<typename... Args>
auto make_tuple_advanced(Args&&... args) {
return std::tuple<std::decay_t<Args>...>(std::forward<Args>(args)...);
}
```
## SFINAE and if constexpr
```cpp
#include <type_traits>
// SFINAE with std::enable_if (older style)
template<typename T>
std::enable_if_t<std::is_integral_v<T>, T>
double_value(T value) {
return value * 2;
}
template<typename T>
std::enable_if_t<std::is_floating_point_v<T>, T>
double_value(T value) {
return value * 2.0;
}
// Modern: if constexpr (C++17)
template<typename T>
auto process(T value) {
if constexpr (std::is_integral_v<T>) {
return value * 2;
} else if constexpr (std::is_floating_point_v<T>) {
return value * 2.0;
} else {
return value;
}
}
// Detection idiom
template<typename T, typename = void>
struct has_serialize : std::false_type {};
template<typename T>
struct has_serialize<T, std::void_t<decltype(std::declval<T>().serialize())>>
: std::true_type {};
template<typename T>
constexpr bool has_serialize_v = has_serialize<T>::value;
// Use with if constexpr
template<typename T>
void save(const T& obj) {
if constexpr (has_serialize_v<T>) {
obj.serialize();
} else {
// Default serialization
}
}
```
## Type Traits
```cpp
#include <type_traits>
// Custom type traits
template<typename T>
struct remove_all_pointers {
using type = T;
};
template<typename T>
struct remove_all_pointers<T*> {
using type = typename remove_all_pointers<T>::type;
};
template<typename T>
using remove_all_pointers_t = typename remove_all_pointers<T>::type;
// Conditional types
template<bool Condition, typename T, typename F>
struct conditional_type {
using type = T;
};
template<typename T, typename F>
struct conditional_type<false, T, F> {
using type = F;
};
// Compile-time type selection
template<size_t N>
struct best_integral_type {
using type = std::conditional_t<N <= 8, uint8_t,
std::conditional_t<N <= 16, uint16_t,
std::conditional_t<N <= 32, uint32_t, uint64_t>>>;
};
// Check for member functions
template<typename T, typename = void>
struct has_reserve : std::false_type {};
template<typename T>
struct has_reserve<T, std::void_t<decltype(std::declval<T>().reserve(size_t{}))>>
: std::true_type {};
```
## CRTP (Curiously Recurring Template Pattern)
```cpp
// Static polymorphism with CRTP
template<typename Derived>
class Shape {
public:
double area() const {
return static_cast<const Derived*>(this)->area_impl();
}
void draw() const {
static_cast<const Derived*>(this)->draw_impl();
}
};
class Circle : public Shape<Circle> {
double radius_;
public:
Circle(double r) : radius_(r) {}
double area_impl() const {
return 3.14159 * radius_ * radius_;
}
void draw_impl() const {
std::cout << "Drawing circle\n";
}
};
class Rectangle : public Shape<Rectangle> {
double width_, height_;
public:
Rectangle(double w, double h) : width_(w), height_(h) {}
double area_impl() const {
return width_ * height_;
}
void draw_impl() const {
std::cout << "Drawing rectangle\n";
}
};
// CRTP for mixin capabilities
template<typename Derived>
class Printable {
public:
void print() const {
std::cout << static_cast<const Derived*>(this)->to_string() << '\n';
}
};
class User : public Printable<User> {
std::string name_;
public:
User(std::string name) : name_(std::move(name)) {}
std::string to_string() const {
return "User: " + name_;
}
};
```
## Template Template Parameters
```cpp
#include <vector>
#include <list>
#include <deque>
// Template template parameter
template<typename T, template<typename, typename> class Container>
class Stack {
Container<T, std::allocator<T>> data_;
public:
void push(const T& value) {
data_.push_back(value);
}
T pop() {
T value = data_.back();
data_.pop_back();
return value;
}
size_t size() const {
return data_.size();
}
};
// Usage with different containers
Stack<int, std::vector> vector_stack;
Stack<int, std::deque> deque_stack;
Stack<int, std::list> list_stack;
```
## Compile-Time Computation
```cpp
#include <array>
// Compile-time factorial
constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
constexpr int fact_5 = factorial(5); // Computed at compile time
// Compile-time prime checking
constexpr bool is_prime(int n) {
if (n < 2) return false;
for (int i = 2; i * i <= n; ++i) {
if (n % i == 0) return false;
}
return true;
}
// Generate compile-time array of primes
template<size_t N>
constexpr auto generate_primes() {
std::array<int, N> primes{};
int count = 0;
int candidate = 2;
while (count < N) {
if (is_prime(candidate)) {
primes[count++] = candidate;
}
++candidate;
}
return primes;
}
constexpr auto first_10_primes = generate_primes<10>();
```
## Expression Templates
```cpp
// Lazy evaluation with expression templates
template<typename E>
class VecExpression {
public:
double operator[](size_t i) const {
return static_cast<const E&>(*this)[i];
}
size_t size() const {
return static_cast<const E&>(*this).size();
}
};
class Vec : public VecExpression<Vec> {
std::vector<double> data_;
public:
Vec(size_t n) : data_(n) {}
double operator[](size_t i) const { return data_[i]; }
double& operator[](size_t i) { return data_[i]; }
size_t size() const { return data_.size(); }
// Evaluate expression template
template<typename E>
Vec& operator=(const VecExpression<E>& expr) {
for (size_t i = 0; i < size(); ++i) {
data_[i] = expr[i];
}
return *this;
}
};
// Binary operation expression
template<typename E1, typename E2>
class VecSum : public VecExpression<VecSum<E1, E2>> {
const E1& lhs_;
const E2& rhs_;
public:
VecSum(const E1& lhs, const E2& rhs) : lhs_(lhs), rhs_(rhs) {}
double operator[](size_t i) const {
return lhs_[i] + rhs_[i];
}
size_t size() const { return lhs_.size(); }
};
// Operator overload
template<typename E1, typename E2>
VecSum<E1, E2> operator+(const VecExpression<E1>& lhs,
const VecExpression<E2>& rhs) {
return VecSum<E1, E2>(static_cast<const E1&>(lhs),
static_cast<const E2&>(rhs));
}
// Usage: a = b + c + d (no temporaries created!)
```
## Quick Reference
| Technique | Use Case | Performance |
|-----------|----------|-------------|
| Variadic Templates | Variable arguments | Zero overhead |
| SFINAE | Conditional compilation | Compile-time |
| if constexpr | Type-based branching | Zero overhead |
| CRTP | Static polymorphism | No vtable cost |
| Expression Templates | Lazy evaluation | Eliminates temps |
| Type Traits | Type introspection | Compile-time |
| Fold Expressions | Parameter pack ops | Optimal |
| Template Specialization | Type-specific impl | Zero overhead |
-325
View File
@@ -1,325 +0,0 @@
---
name: cpp-testing
description: Use only when writing/updating/fixing C++ tests, configuring GoogleTest/CTest, diagnosing failing or flaky tests, or adding coverage/sanitizers.
metadata:
origin: ECC
---
# C++ Testing (Agent Skill)
Agent-focused testing workflow for modern C++ (C++17/20) using GoogleTest/GoogleMock with CMake/CTest.
## When to Use
- Writing new C++ tests or fixing existing tests
- Designing unit/integration test coverage for C++ components
- Adding test coverage, CI gating, or regression protection
- Configuring CMake/CTest workflows for consistent execution
- Investigating test failures or flaky behavior
- Enabling sanitizers for memory/race diagnostics
### When NOT to Use
- Implementing new product features without test changes
- Large-scale refactors unrelated to test coverage or failures
- Performance tuning without test regressions to validate
- Non-C++ projects or non-test tasks
## Core Concepts
- **TDD loop**: red → green → refactor (tests first, minimal fix, then cleanups).
- **Isolation**: prefer dependency injection and fakes over global state.
- **Test layout**: `tests/unit`, `tests/integration`, `tests/testdata`.
- **Mocks vs fakes**: mock for interactions, fake for stateful behavior.
- **CTest discovery**: use `gtest_discover_tests()` for stable test discovery.
- **CI signal**: run subset first, then full suite with `--output-on-failure`.
## TDD Workflow
Follow the RED → GREEN → REFACTOR loop:
1. **RED**: write a failing test that captures the new behavior
2. **GREEN**: implement the smallest change to pass
3. **REFACTOR**: clean up while tests stay green
```cpp
// tests/add_test.cpp
#include <gtest/gtest.h>
int Add(int a, int b); // Provided by production code.
TEST(AddTest, AddsTwoNumbers) { // RED
EXPECT_EQ(Add(2, 3), 5);
}
// src/add.cpp
int Add(int a, int b) { // GREEN
return a + b;
}
// REFACTOR: simplify/rename once tests pass
```
## Code Examples
### Basic Unit Test (gtest)
```cpp
// tests/calculator_test.cpp
#include <gtest/gtest.h>
int Add(int a, int b); // Provided by production code.
TEST(CalculatorTest, AddsTwoNumbers) {
EXPECT_EQ(Add(2, 3), 5);
}
```
### Fixture (gtest)
```cpp
// tests/user_store_test.cpp
// Pseudocode stub: replace UserStore/User with project types.
#include <gtest/gtest.h>
#include <memory>
#include <optional>
#include <string>
struct User { std::string name; };
class UserStore {
public:
explicit UserStore(std::string /*path*/) {}
void Seed(std::initializer_list<User> /*users*/) {}
std::optional<User> Find(const std::string &/*name*/) { return User{"alice"}; }
};
class UserStoreTest : public ::testing::Test {
protected:
void SetUp() override {
store = std::make_unique<UserStore>(":memory:");
store->Seed({{"alice"}, {"bob"}});
}
std::unique_ptr<UserStore> store;
};
TEST_F(UserStoreTest, FindsExistingUser) {
auto user = store->Find("alice");
ASSERT_TRUE(user.has_value());
EXPECT_EQ(user->name, "alice");
}
```
### Mock (gmock)
```cpp
// tests/notifier_test.cpp
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <string>
class Notifier {
public:
virtual ~Notifier() = default;
virtual void Send(const std::string &message) = 0;
};
class MockNotifier : public Notifier {
public:
MOCK_METHOD(void, Send, (const std::string &message), (override));
};
class Service {
public:
explicit Service(Notifier &notifier) : notifier_(notifier) {}
void Publish(const std::string &message) { notifier_.Send(message); }
private:
Notifier &notifier_;
};
TEST(ServiceTest, SendsNotifications) {
MockNotifier notifier;
Service service(notifier);
EXPECT_CALL(notifier, Send("hello")).Times(1);
service.Publish("hello");
}
```
### CMake/CTest Quickstart
```cmake
# CMakeLists.txt (excerpt)
cmake_minimum_required(VERSION 3.20)
project(example LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(FetchContent)
# Prefer project-locked versions. If using a tag, use a pinned version per project policy.
set(GTEST_VERSION v1.17.0) # Adjust to project policy.
FetchContent_Declare(
googletest
# Google Test framework (official repository)
URL https://github.com/google/googletest/archive/refs/tags/${GTEST_VERSION}.zip
)
FetchContent_MakeAvailable(googletest)
add_executable(example_tests
tests/calculator_test.cpp
src/calculator.cpp
)
target_link_libraries(example_tests GTest::gtest GTest::gmock GTest::gtest_main)
enable_testing()
include(GoogleTest)
gtest_discover_tests(example_tests)
```
```bash
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build -j
ctest --test-dir build --output-on-failure
```
## Running Tests
```bash
ctest --test-dir build --output-on-failure
ctest --test-dir build -R ClampTest
ctest --test-dir build -R "UserStoreTest.*" --output-on-failure
```
```bash
./build/example_tests --gtest_filter=ClampTest.*
./build/example_tests --gtest_filter=UserStoreTest.FindsExistingUser
```
## Debugging Failures
1. Re-run the single failing test with gtest filter.
2. Add scoped logging around the failing assertion.
3. Re-run with sanitizers enabled.
4. Expand to full suite once the root cause is fixed.
## Coverage
Prefer target-level settings instead of global flags.
```cmake
option(ENABLE_COVERAGE "Enable coverage flags" OFF)
if(ENABLE_COVERAGE)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
target_compile_options(example_tests PRIVATE --coverage)
target_link_options(example_tests PRIVATE --coverage)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
target_compile_options(example_tests PRIVATE -fprofile-instr-generate -fcoverage-mapping)
target_link_options(example_tests PRIVATE -fprofile-instr-generate)
endif()
endif()
```
GCC + gcov + lcov:
```bash
cmake -S . -B build-cov -DENABLE_COVERAGE=ON
cmake --build build-cov -j
ctest --test-dir build-cov
lcov --capture --directory build-cov --output-file coverage.info
lcov --remove coverage.info '/usr/*' --output-file coverage.info
genhtml coverage.info --output-directory coverage
```
Clang + llvm-cov:
```bash
cmake -S . -B build-llvm -DENABLE_COVERAGE=ON -DCMAKE_CXX_COMPILER=clang++
cmake --build build-llvm -j
LLVM_PROFILE_FILE="build-llvm/default.profraw" ctest --test-dir build-llvm
llvm-profdata merge -sparse build-llvm/default.profraw -o build-llvm/default.profdata
llvm-cov report build-llvm/example_tests -instr-profile=build-llvm/default.profdata
```
## Sanitizers
```cmake
option(ENABLE_ASAN "Enable AddressSanitizer" OFF)
option(ENABLE_UBSAN "Enable UndefinedBehaviorSanitizer" OFF)
option(ENABLE_TSAN "Enable ThreadSanitizer" OFF)
if(ENABLE_ASAN)
add_compile_options(-fsanitize=address -fno-omit-frame-pointer)
add_link_options(-fsanitize=address)
endif()
if(ENABLE_UBSAN)
add_compile_options(-fsanitize=undefined -fno-omit-frame-pointer)
add_link_options(-fsanitize=undefined)
endif()
if(ENABLE_TSAN)
add_compile_options(-fsanitize=thread)
add_link_options(-fsanitize=thread)
endif()
```
## Flaky Tests Guardrails
- Never use `sleep` for synchronization; use condition variables or latches.
- Make temp directories unique per test and always clean them.
- Avoid real time, network, or filesystem dependencies in unit tests.
- Use deterministic seeds for randomized inputs.
## Best Practices
### DO
- Keep tests deterministic and isolated
- Prefer dependency injection over globals
- Use `ASSERT_*` for preconditions, `EXPECT_*` for multiple checks
- Separate unit vs integration tests in CTest labels or directories
- Run sanitizers in CI for memory and race detection
### DON'T
- Don't depend on real time or network in unit tests
- Don't use sleeps as synchronization when a condition variable can be used
- Don't over-mock simple value objects
- Don't use brittle string matching for non-critical logs
### Common Pitfalls
- **Using fixed temp paths** → Generate unique temp directories per test and clean them.
- **Relying on wall clock time** → Inject a clock or use fake time sources.
- **Flaky concurrency tests** → Use condition variables/latches and bounded waits.
- **Hidden global state** → Reset global state in fixtures or remove globals.
- **Over-mocking** → Prefer fakes for stateful behavior and only mock interactions.
- **Missing sanitizer runs** → Add ASan/UBSan/TSan builds in CI.
- **Coverage on debug-only builds** → Ensure coverage targets use consistent flags.
## Optional Appendix: Fuzzing / Property Testing
Only use if the project already supports LLVM/libFuzzer or a property-testing library.
- **libFuzzer**: best for pure functions with minimal I/O.
- **RapidCheck**: property-based tests to validate invariants.
Minimal libFuzzer harness (pseudocode: replace ParseConfig):
```cpp
#include <cstddef>
#include <cstdint>
#include <string>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
std::string input(reinterpret_cast<const char *>(data), size);
// ParseConfig(input); // project function
return 0;
}
```
## Alternatives to GoogleTest
- **Catch2**: header-only, expressive matchers
- **doctest**: lightweight, minimal compile overhead
+69 -174
View File
@@ -1,189 +1,84 @@
---
name: optimize-kernel
description: "Workflow for optimizing model inference kernels on Strix Halo gfx1151: assess a baseline, change one route behind a policy toggle, verify quality against the baseline, and retain or reject with evidence. Rejection with evidence is a valid card completion."
description: Optimize Gufo inference on Strix Halo gfx1151 using focused profiles, independent quality checks, and matched end-to-end measurements.
metadata:
origin: gufo
---
# Optimize Kernel (Agent Skill)
# Optimize gfx1151 kernels
Workflow for GPU kernel optimization cards (gh issue opt-*). Never promote a
fusion that degrades end-to-end layer time or spills scratch. Rejection with
evidence is a valid card completion.
Read the affected model's `docs/models/<model>/{BENCHMARKS,EVALUATION,EXPERIMENTS}.md`
and its launch code. Establish the timed scope and arithmetic contract before
changing dispatch. Follow the user's machine, time, and Git instructions.
## Card contract (read the issue first)
## Fast experiment loop
- The issue's `## Acceptance` checklist is the contract (e.g. #127 requires
"any active-layer degradation from bus contention rejects/disables
prefetch"). The `## Verify` snippet may name stale presets; the real ones
are `gpu-test` (build) and `gpu-full` (test).
- Register the equivalence test under the card's CTest label on the focused
Qwen HIP target that owns the kernel family in `CMakeLists.txt` (attention,
SSM, FFN, quant GEMV, dequant, graph/prefetch, module, or basic/BLAS). Add a
new focused target rather than growing an unrelated executable.
1. Keep baseline and candidate production binaries. Use `nix build`, or the
`release` CMake preset with the same compiler/dependency versions. Test
binaries keep assertions and are for correctness, not headline timings.
2. Start with one affected shape and one control. Use pp2048/tg128 for retained
end-to-end results; tiny prompts/outputs suffice to debug launches. For a
depth issue, try d16K/d32K first. Expand to d64K/d128K only when justified.
3. Profile separately with `tools/prof/prof.py`; compare stage totals, launch
counts, GPU-busy time and wall span. Inspect allocator, synchronization,
sampling and cache I/O when GPU time does not explain latency.
4. Change one mechanism, compile the affected target, then run its existing
analytic/operator check. Use `tools/bench/build.sh <name>` for standalone
HIP experiments; each result needs correctness alongside throughput.
5. Compare unprofiled baseline/candidate under identical model, prompt, depth,
quantization, sampling, cache history and concurrency. Repeat only to resolve
noise. A microbenchmark gain must survive the full inference path.
6. Keep the winning implementation as default. Delete rejected/dead routes;
do not add environment switches or duplicate tests to preserve experiments.
Record one concise retained/rejected row in `EXPERIMENTS.md`, current speeds
in `BENCHMARKS.md`, and actual quality evidence in `EVALUATION.md`.
## Task scratch file
## Techniques that worked here
Keep an evidence file at repo root (`task-on-going.md`); it is the only
artifact that survives context. Record baselines, profile findings, tool
gotchas, and ideas as they are discovered. DELETE it before finishing - it
only makes sense during the optimization.
- **Bandwidth and cache:** measure `tools/bench/gfx1151_peak.hip`, not a spec
sheet. Previous large-buffer controls reached roughly 240 GB/s. The 32 MiB
MALL cache can make repeatedly reused small matrices look unrealistically
fast: rotate weights beyond cache capacity or reproduce full-model traffic.
- **GEMV:** coalesce packed weight loads, stage shared activations in LDS,
fuse QKV or gate/up reads where useful, and preserve each dot product's
accumulation order. More fusion can increase VGPR pressure and spills.
- **Verification and concurrency:** reuse quantized weights across token and
request rows. Tune real ragged shapes, including 3–8 rows and partial tiles.
Batch the complete draft transformer body, not only the vocabulary head;
keep each request's KV, recurrence, acceptance and RNG state independent.
- **Wave mode and compiler:** selected quantized kernels need wave64 while
other paths use wave32. Match helper/caller wave modes per translation unit.
Iterative ILP scheduling helped selected 16-row Q4/Q5 kernels; applying it
globally was not a win. Inspect VGPR/LDS/private scratch and ISA with
`tools/prof/isa_mix.py`; occupancy alone is not the optimization objective.
- **Attention and selection:** exact partial top-k can avoid sorting the full
context. Preserve tie ordering and FP32 ranking. Pack existing KV bytes into
bounded scratch/head groups without changing persistent precision. Do not
change softmax reduction order without the model's numerical qualification.
- **Fusion and memory:** split repeated embedding/hidden projections instead
of concatenating duplicate inputs. Respect full-width HC normalization.
Reuse scratch, allocate rollback depth on demand, and avoid carrying entire
prefill chunks into draft catch-up. UMA still has placement costs: mapped
quantized weights and copied reusable audio/DiT weights behave differently.
- **Graphs:** verify replay actually launches every new operation. Side-stream
work during capture is not automatically included in the graph. Check the
relevant shallow/deep dispatch boundaries and unprofiled wall time.
## 1. Assess the baseline (before any change)
## Quality and reporting
1. Clean stage state: `git add .` (Nix sees only tracked files).
2. Probe hardware + record revision/fingerprint: `nix build && ./result/bin/gufo`.
3. Build:
- Release: `nix build` (produces `result/`). It builds fully-optimized
binaries and `./result/bin/gufo-server` runs significantly faster with
consistent performance.
- Fast incremental loop while editing:
`nix develop -c cmake --build --preset gpu-test`.
- Build after EVERY step that edits kernels or launchers; never batch
several uncommitted kernel edits before compiling this allows to iterate faster and safer.
4. Benchmark with `./result/bin/gufo-server bench`, single reps (do NOT pass
`--repetitions`; alternate baseline/candidate runs instead):
- Combined headline: `-p 2048 -n 128`
- Decode only: `--n-prompt 0 --n-gen 128`
- Prefill only: `--n-prompt 2048 --n-gen 0`
- Depth scaling: `--n-prompt 2048 --n-depth 0,4096,8192,16384`, plus
`--n-gen 128 --n-depth 0,4096,8192,16384` for tg. Read
`src/server/bench_cli.cpp` to interpret values before trusting them.
- Until the implementation works do NOT test with big numbers, focus on `--n-prompt 128 --n-gen 16`
5. Logits comparison (correctness before performance counts):
`--validate-prefill 1024 --n-prompt 1024 --n-gen 0`; record the envelope
(current model: rmse `0.02007260`, cosine `0.99997753`, top-1 `198`). A
candidate must not regress it. Comparing logits can be extremely slow, do it ONLY at the end to verify end to end correctness of the change.
6. Profiling (separate pass; tracing changes timing so never profile the
headline run):
```
nix develop -c rocprofv3 \
--kernel-trace --scratch-memory-trace --summary \
--output-directory /tmp/gufo-profile-<tag> \
-- ./result/bin/gufo-server bench --model "$MODEL" --n-prompt 0 --n-gen 128
```
Collect per-kernel launch count, elapsed time, memory traffic, and resource
table (VGPR, SGPR, LDS, scratch, occupancy, waves/SIMD). Headline latency
always comes from an unprofiled run.
Use an independent operator formula or pinned official teacher; two Gufo
paths agreeing does not prove upstream parity. Compare matched token histories
and find the first changed layer if logits drift. Never relax tolerances or
replace goldens to accept a speedup. Test finite outputs and awkward tails
(e.g. 1/8/9/32/33 rows), not only aligned shapes.
Record the baseline artifact in `task-on-going.md` (fingerprint, revision,
raw samples, register allocation, scratch, occupancy) before touching
code.
For state or speculative changes, cover greedy output, sampled p/q rejection
and residual correction, seeded replay, EOS, multi-turn continuation and
snapshot restore. Add images/cancellation when those paths change. Timing-based
controllers are for greedy decoding; sampled execution must retain seeded
replay. Full model sweeps are final qualification, not each iteration.
## 2. Introduce the change
- One fused kernel / one route at a time, behind a new policy toggle in
`src/core/hip/detail/qwen_attention_policy.hpp` (e.g.
`ShouldFuseSSMGateResidual`, `ShouldPrefetchNextLayer`), default OFF. The
unfused route stays wired as the independent reference (comparison and
revert); a rejected route remains runnable behind the toggle.
- Reuse existing kernel patterns in `src/core/hip/` (and llama.cpp CUDA
kernels in `llama.cpp/` as reference sources). Copy-paste the pattern into
that model's own implementation; do not modularize shared code. Only the
model being optimized may change.
- Kernel must sustain >= 4 waves per SIMD and no scratch spilling to be
retained; check `__launch_bounds__` and resource usage.
- When editing `__global__` kernels, re-balance braces before compiling:
count `{`/`}` per line skipping `//` and `#` lines. A stray `}` closing the
namespace early surfaces as `use of undeclared identifier '<KernelName>'` at
the launch site (secondary cascade); a missing `}` surfaces as `function
definition is not allowed here`. Fix the FIRST structural error and rebuild
before chasing later ones.
- Register the equivalence test under the card's CTest label.
- Graph-capture awareness (decode): if the change touches launch structure,
verify capture still succeeds with
`GUFO_DISPATCH_TELEMETRY=1 ./build/gpu-test/gufo-server bench -p 16 -n 16`
-> expect `hip_graph` `miss_captured` then `hit`. Cross-stream rules:
- Only the side-stream-records -> main-stream-waits direction is
capture-compatible; the reverse fails `hipStreamEndCapture`
(`miss_end_failed`).
- Work launched on a NON-captured side stream during capture is NOT part of
the captured graph (only event edges are baked in); on replay the side
kernels never run. Verify with rocprof that new work actually dispatches
per token, or it only fires during capture.
- Record new ideas in `task-on-going.md` as they are discovered (and the
model's `docs/models/<model>/BENCHMARKS.md` `# TODOs` dotted list at the end).
## 3. Verify against the baseline
1. Correctness gate first:
```
nix develop -c cmake --build --preset gpu-test
nix develop -c ctest --preset gpu-full --output-on-failure --no-tests=error -L 'opt-c0XX-...'
```
Fused output must match the unfused reference under the arithmetic contract
(CPU oracle vs GPU kernel).
2. Logits: same `--validate-prefill` as baseline; require finite logits,
identical top-1, no material envelope regression.
3. End-to-end interleaved A/B with release binaries. Keep
`result-base`/`result-cand` symlinks and alternate binaries:
```
val=$(./result-<tag>/bin/gufo-server bench --model "$MODEL" --n-prompt 0 --n-gen 128 2>/dev/null \
| rg '\| *tg128' | sed -E 's/.*\|\s*([0-9.]+) ±.*/\1/')
```
Depth rows are named `tg128@d4096` etc. Report medians, tail, and raw
samples. The host is noisy (background sidekiq/agy spikes produce pp2048
outliers ~150-300 tok/s), so a signal is real only if it reproduces across
interleaved samples outside the noise band. Include `pp2048` in the same
runs as an "untouched-path" sanity check.
4. Always A/B BOTH paths: decode silently switches to split-K (non-graph) at
context >= 4K, and a regression can hide in one path only. Test `tg128`
(graph) and `tg@depth 4K/8K/16K` (split-K); to force the non-graph path at
shallow depth run `GUFO_ENABLE_HIP_GRAPH=0`.
5. rocprofv3 with the same command as baseline; compare launch count,
eliminated memory traffic, layer latency, VGPR/LDS/scratch, occupancy,
waves/SIMD, and raw repetitions (sqlite query below).
6. Decision:
- Retain only if end-to-end evidence improves beyond noise under the
acceptance contract AND the kernel meets resources (>= 4 waves, no
scratch). If retained, flip the toggle on and consider making the route
default.
- Reject (valid completion) if neutral-to-regressive or any active-layer
degradation; leave the toggle OFF and the code + test behind it for
re-evaluation.
7. Full gates only on the final tree, right before wrap-up:
- clang-format edited files first:
`nix shell nixpkgs#clang-tools -c clang-format -i <edited .cpp/.hpp>`
- `nix build .#checks.x86_64-linux.pr`
- `nix flake check`
## 4. Wrap up (evidence-based decision)
1. Update `docs/models/<model>/BENCHMARKS.md` existing sections only: add a row to
the Experiment Summary table (retained/rejected) and append follow-up ideas
to the `# TODOs` dotted list.
2. Post the decision to the issue with `gh` and close it if done (rejection is
valid). Comment format that worked across cards:
- `## Result: REJECTED/...` header with the one-line conclusion
- Correctness (CTest label passing; validate-prefill envelope)
- rocprofv3 kernel table (VGPR/SGPR/LDS/scratch/wgs/grid + notes)
- Interleaved A/B raw samples + medians, memory placement
- Mechanism / why
- Decision sentence referencing the card contract
3. Commit locally: `jj commit -m "perf(hip): evaluate <change> (#<issue>)"`,
then move the `main` bookmark to the commit and to NOTHING else
(`jj bookmark move main --to <commit-id>`; the bookmark lives on the real
commit, never on the empty working copy). Do not push.
4. Delete `task-on-going.md` and squash the deletion into the card commit
(`jj squash --into <card-commit>`).
5. Re-run the full gates on the amended tree, then send the completion
notification if the user's convention uses one (e.g. ntfy).
## Platform knowledge (Strix Halo gfx1151, Qwen3.8-27B BF16)
- Weights are `mmap(PROT_READ, MAP_PRIVATE)` + `madvise(MADV_SEQUENTIAL)` then
`hipHostRegister(Mapped|ReadOnly)` via `MapRegisteredRegion` (mapped mode is
the default on the integrated APU). The shard is fully page-cached, so
steady-state decode is DRAM-bandwidth bound (~85 GB/s re-reading ~10-24 GiB
of weights per token). Page-touch prefetch only re-reads the same DRAM.
`GUFO_GPU_WEIGHT_MODE=copy` makes weights device-resident (slow H2D load).
- Decode hot kernels (tg128): `Wave32FusedSwiGLUGEMV_2Rows`,
`FastGEMVBlockKernel`, `Wave32GEMVKernel_1Row`,
`Wave32FusedSSMInputProjections`, `DeltaNetRecurrenceKernel`.
- Decode attention: online softmax below 4K; split-K (non-graph) at 4K+.
HIP-graph capture runs only when `!use_split_k_decode`.
- Reference: `llama.cpp/` contains llama-bench on the same model; the
remaining gap is ~1.07-1.09x decode and ~1.15-1.34x prefill at depth (see
`docs/models/qwen3.8-27b/BENCHMARKS.md`).
- Baseline envelope: pp2048 ~335-341, tg128 3.72-3.74,
validate rmse `0.02007260` / cosine `0.99997753` / top-1 `198`.
Measure C1 plus affected C2/C4/C6/C8. Keep prompt and cache state identical when
comparing CLI single-user and HTTP C1; report decode rate separately from whole
request throughput. For H3, prefer an analytic case or one block/forward pass;
do not generate full videos during routine kernel work.
@@ -1,212 +0,0 @@
---
name: refactoring-patterns
description: 'Apply named refactoring transformations to improve code structure without changing behavior. Use when the user mentions "refactor this", "code smells", "extract method", "replace conditional", "technical debt", "move method", "inline variable", "decompose conditional", or "clean up this messy code". Also trigger when cleaning up legacy code, preparing code for new features by restructuring, or identifying which transformation fits a specific code smell. Covers smell-driven refactoring, safe transformation sequences, and testing guards. For code-quality foundations, see clean-code. For managing complexity, see software-design-philosophy.'
license: MIT
metadata:
author: wondelai
version: "1.4.1"
---
# Refactoring Patterns Framework
A disciplined approach to improving the internal structure of existing code without changing its observable behavior. Every refactoring follows the same loop: verify tests pass, apply one small structural change, verify tests still pass.
## Core Principle
**Refactoring is not rewriting. It is a sequence of small, behavior-preserving transformations, each backed by tests.** You never change what the code does — only how it is organized. Big-bang rewrites fail because they combine structural change with behavioral change, making it impossible to know which broke things.
**The foundation:** Bad code is a natural consequence of delivering under time pressure, not a character flaw. Code smells are objective signals of degraded structure; the smell catalog tells you *where* to look, and the refactoring catalog tells you *what to do*.
## Scoring
**Goal: 10/10.** Score structural quality by how many of the eight [Quick Diagnostic](#quick-diagnostic) rows pass — `score = round(passed / 8 × 10)`, adjusting down when a single smell is severe. Bands:
- **9-10**: no obvious smells remain, each function does one thing, names reveal intent, duplication is eliminated, conditionals use polymorphism where apt, and tests cover the refactored paths.
- **5-6**: a few smells remain (a Long Method, some duplication) but structure is mostly sound.
- **≤3**: pervasive smells — tangled conditionals, God classes, duplication everywhere — or no tests to refactor safely.
Always state the current score, name the smells driving it down, and list the specific refactorings needed to reach 10/10.
## The Refactoring Patterns Framework
Six areas of focus for systematically improving code structure:
### 1. Code Smells as Triggers
**Core concept:** Code smells are surface indicators of deeper structural problems — not bugs, but signals that the design makes code harder to understand, extend, or maintain. Each smell maps to named refactorings that fix it.
**Why it works:** Named smells give teams objective criteria instead of subjective "I don't like this" — "This is Feature Envy" points directly at the fix.
**Key insights:**
- Smells cluster into five families: Bloaters, Object-Orientation Abusers, Change Preventers, Dispensables, Couplers
- Long Method is the most common smell; Duplicate Code is the most expensive
- A method that needs a comment to explain *what* it does is a smell — extract and name the block instead
- Shotgun Surgery (one change, many classes) and Divergent Change (one class, many reasons to change) are opposite signals of misplaced responsibilities
- Primitive Obsession — raw strings/ints instead of small domain objects — spreads errors and duplication
**Code applications:**
| Context | Pattern | Example |
|---------|---------|---------|
| Method > 10 lines | Extract Method | Pull loop body into `calculateLineTotal()` |
| One change touches many classes (Shotgun Surgery) | Move Method/Field | Gather the scattered behavior into one class |
| Same params in many methods | Introduce Parameter Object | `startDate, endDate` → `DateRange` |
| Copy-pasted logic | Extract Method + Pull Up Method | Share via common method or base class |
See [references/smell-catalog.md](references/smell-catalog.md) when you need to name a smell and its fix — all five families (Bloaters, OO Abusers, Change Preventers, Dispensables, Couplers) with detection heuristics and the refactoring each maps to.
### 2. Composing Methods
**Core concept:** Most refactoring starts here: break long methods into smaller, well-named pieces that read like prose — high-level steps delegating to clearly named helpers.
**Why it works:** Short methods with intention-revealing names eliminate comments, make bugs obvious at a glance, and enable reuse; a method call costs nothing to read when the name says everything.
**Key insights:**
- Extract Method is the single most important refactoring — master it first
- Urge to write a comment? Extract the block and use the comment as the method name
- Inline Method when the body is as clear as the name — indirection without value is noise
- Replace Temp with Query for computed values used in multiple places; Split Temporary Variable when one temp serves two purposes
- Replace Method with Method Object when locals are too tangled to extract — they become fields
**Code applications:**
| Context | Pattern | Example |
|---------|---------|---------|
| Block with a comment | Extract Method | `// check eligibility` → `isEligible()` |
| Temp used once | Inline Variable | Drop `const price = order.getPrice()` |
| Trivial delegating method | Inline Method | Inline `return deliveries > 5` if used once |
| Method with many tangled locals | Replace Method with Method Object | Locals become fields in a new class |
See [references/composing-methods.md](references/composing-methods.md) when applying any method-level transformation — step-by-step mechanics and before/after code for Extract/Inline Method, Extract/Inline Variable, Replace Temp with Query, Split Temporary Variable, and Replace Method with Method Object.
### 3. Moving Features Between Objects
**Core concept:** The key OO design decision is where responsibilities live. When Feature Envy, excessive coupling, or unbalanced class sizes show a method or field is in the wrong class, move it where it belongs.
**Why it works:** A method placed away from the data it uses creates invisible cross-class dependencies, so one logical change ripples across many files — Shotgun Surgery. Co-locating method and data confines the change to one class.
**Key insights:**
- Move Method when a method uses more of another class's features than its own; Move Field likewise
- Extract Class when one class does two things — split along the axis of change; Inline Class when one does too little
- Hide Delegate enforces the Law of Demeter; Remove Middle Man undoes it when forwarding becomes the whole class
- Resolve that tension case by case: hide the delegate when the chain is unstable, remove the middle man when it's pure forwarding
**Code applications:**
| Context | Pattern | Example |
|---------|---------|---------|
| Method envies another class | Move Method | `calculateShipping()` from `Order` to `ShippingPolicy` |
| God class 500+ lines | Extract Class | Pull `Address` fields/methods into own class |
| Client calls `a.getB().getC()` | Hide Delegate | Add `a.getCThroughB()` |
| Class only forwards calls | Remove Middle Man | Let client call the delegate directly |
See [references/moving-features.md](references/moving-features.md) when deciding where a responsibility belongs — mechanics for Move Method/Field, Extract/Inline Class, Hide Delegate, and Remove Middle Man.
### 4. Organizing Data
**Core concept:** Raw data — magic numbers, exposed fields, integer type codes — creates subtle bugs and scatters domain knowledge. Replace primitives with objects that encapsulate behavior and enforce invariants.
**Why it works:** An `int` amount has no rounding rules or currency code; a `Money` object encapsulates all of it, so business rules live in one place and the type system catches errors at compile time.
**Key insights:**
- Replace Magic Number with Symbolic Constant — the simplest data refactoring; it names intent
- Replace Data Value with Object cures Primitive Obsession (`EmailAddress`, `Money`, `Temperature`)
- Encapsulate Field and Encapsulate Collection — never expose raw fields or mutable internal lists
- Replace Type Code with Subclasses when the code affects behavior; with Strategy when subclassing is impractical
- Change Value to Reference when you need identity semantics (one shared `Customer`, not copies)
**Code applications:**
| Context | Pattern | Example |
|---------|---------|---------|
| `if (status == 2)` | Replace Magic Number | `if (status == ORDER_SHIPPED)` |
| `String email` passed everywhere | Replace Data Value with Object | `EmailAddress` class with validation |
| Getter returns mutable list | Encapsulate Collection | Return `Collections.unmodifiableList(items)` |
| `int typeCode` with switch | Replace Type Code with Subclasses | `Employee` → `Engineer`, `Manager` |
See [references/organizing-data.md](references/organizing-data.md) when replacing primitives with objects — mechanics for Replace Data Value with Object, Change Value to Reference, Replace Magic Number, Encapsulate Field/Collection, and the Replace Type Code variants.
### 5. Simplifying Conditional Logic
**Core concept:** Deeply nested if/else trees, long switches, and scattered null checks are the hardest code to read and the most bug-prone. Named refactorings decompose, consolidate, and replace conditionals with clearer structures.
**Why it works:** A six-branch conditional forces readers to simulate every path mentally; well-named extracted branches are self-documenting, and polymorphism eliminates whole categories of "forgot this case" bugs.
**Key insights:**
- Decompose Conditional: extract condition, then-branch, and else-branch into named methods
- Consolidate Conditional Expression: merge conditions with the same result into one named check
- Replace Nested Conditional with Guard Clauses: handle edge cases early and return, keeping the main path unindented
- Replace Conditional with Polymorphism is the gold standard for type-based conditionals
- Introduce Special Case (Null Object) eliminates scattered `if (x == null)` checks; Introduce Assertion makes assumptions fail fast
**Code applications:**
| Context | Pattern | Example |
|---------|---------|---------|
| Long `if` with complex condition | Decompose Conditional | Extract `isSummer(date)` and `summerCharge()` |
| Deeply nested `if/else` | Replace with Guard Clauses | Edge cases first, return early, flat main path |
| Switch on object type | Replace Conditional with Polymorphism | Each type implements its own `calculatePay()` |
| `if (customer == null)` everywhere | Introduce Special Case | `NullCustomer` with safe default behavior |
See [references/simplifying-conditionals.md](references/simplifying-conditionals.md) when untangling branches — before/after examples for Decompose/Consolidate Conditional, Guard Clauses, Replace Conditional with Polymorphism, Special Case, and Assertions.
### 6. Safe Refactoring Workflow
**Core concept:** Refactoring is only safe when wrapped in tests. The workflow is mechanical: run tests (green), apply one small transformation, run tests (green), commit. If tests go red, revert — don't debug a broken refactoring.
**Why it works:** Small steps make the failure obvious (it was the last thing you did) and reverting costs seconds; debugging a failed big-bang rewrite costs days.
**Key insights:**
- Rule of Three: tolerate duplication once, note it twice, refactor on the third occurrence
- Preparatory refactoring: restructure to make the feature easy *before* adding it; comprehension and litter-pickup refactoring keep code improving as you read and touch it
- When NOT to refactor: rewriting is easier, no tests and adding them isn't feasible, or the code will be deleted soon
- Refactor for clarity first, then profile and optimize the measured bottleneck — clear code is easier to tune
- Branch by Abstraction and Parallel Change enable large refactorings in production without long-lived branches
**Code applications:**
| Context | Pattern | Example |
|---------|---------|---------|
| About to add a feature | Preparatory Refactoring | Clean the insertion point first |
| Third copy of same logic | Rule of Three | Extract shared logic now |
| Large API change in production | Branch by Abstraction | Add abstraction layer, migrate callers, remove old path |
| Renaming a widely-used method | Parallel Change | Add new, deprecate old, migrate, remove |
See [references/refactoring-workflow.md](references/refactoring-workflow.md) before a large or risky refactoring — the full green-to-green cycle, when (not) to refactor, performance, Branch by Abstraction, and Parallel Change.
## Common Mistakes
| Mistake | Why It Fails | Fix |
|---------|-------------|-----|
| Refactoring without tests | No safety net to detect behavior change | Write characterization tests first |
| Big-bang rewrite | Mixes structural and behavioral change; undebuggable | Smallest possible steps, tests after each |
| Refactoring while adding features | Two hats at once — neither change verifiable | Refactor first (commit), then add feature (commit) |
| Renaming without updating callers | Broken build or dead code | Use IDE rename; search all references |
| Extracting too many tiny methods | Indirection without clarity when names are poor | Each name must remove the need to read the body |
| Ignoring the smell catalog | Reinvents fixes instead of applying proven recipes | Learn named smells; each maps to refactorings |
| Refactoring doomed code | Polish on condemned code is waste | Check the code's lifespan justifies the investment |
| Optimizing while refactoring | Conflates clarity with performance | Clarity first, then profile, then optimize hot path |
## Quick Diagnostic
| Question | If No | Action |
|----------|-------|--------|
| Do tests pass before you start? | No safety net | Write or fix tests first — never refactor red |
| Can you name the smell you're fixing? | Refactoring by instinct, not catalog | Identify the smell, apply its prescribed refactoring |
| Is each method under ~10 lines? | Long Methods likely | Extract Method into named steps |
| Does each class have one reason to change? | Divergent Change or Large Class | Extract Class to separate responsibilities |
| Are there duplicated code blocks? | The most expensive smell | Extract shared logic into common method/base class |
| Do conditionals use polymorphism where apt? | Switch Statements remain | Replace Conditional with Polymorphism |
| Are you committing after each step? | Risk losing work, mixing changes | Commit after every green-to-green transformation |
| Is the code easier to read after your change? | Refactoring added complexity | Revert and try a different approach |
## Further Reading
The definitive guides to improving existing code:
- [*"Refactoring: Improving the Design of Existing Code (2nd Edition)"*](https://www.amazon.com/Refactoring-Improving-Existing-Addison-Wesley-Signature/dp/0134757599?tag=wondelai00-20) by Martin Fowler
- [*"Working Effectively with Legacy Code"*](https://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052?tag=wondelai00-20) by Michael Feathers (companion for code without tests)
- [*"Clean Code: A Handbook of Agile Software Craftsmanship"*](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882?tag=wondelai00-20) by Robert C. Martin (complementary naming and style principles)
## About the Author
**Martin Fowler** is Chief Scientist at Thoughtworks, a signatory of the Agile Manifesto, and author of *Refactoring: Improving the Design of Existing Code* (1999; 2nd edition 2018), which introduced catalog-based, named refactorings to mainstream development. His catalog underpins the automated refactoring tools in every major IDE.
@@ -1,399 +0,0 @@
# Composing Methods
Detailed reference for the refactorings that break down long methods into well-named, cohesive pieces. These are the most frequently used refactorings and the foundation of all code improvement.
## Table of Contents
1. [Extract Method](#extract-method)
2. [Inline Method](#inline-method)
3. [Extract Variable](#extract-variable)
4. [Inline Variable](#inline-variable)
5. [Replace Temp with Query](#replace-temp-with-query)
6. [Split Temporary Variable](#split-temporary-variable)
7. [Remove Assignments to Parameters](#remove-assignments-to-parameters)
8. [Replace Method with Method Object](#replace-method-with-method-object)
9. [Decision Guide: Which Composing Refactoring to Use](#decision-guide-which-composing-refactoring-to-use)
---
## Extract Method
The single most important refactoring. Turn a code fragment into a method whose name explains the purpose of the fragment.
### Motivation
You have a code fragment that can be grouped together. The longer a method, the harder it is to understand. When you see a comment explaining what the next block does, that's a signal to extract. The comment becomes the method name.
### Mechanics
1. Create a new method and name it after the *intention* of the code (what it does, not how)
2. Copy the extracted code into the new method
3. Scan the extracted code for references to local variables -- these become parameters or return values
4. Declare any local-scope variables as local to the new method if they're only used within the extracted code
5. Pass any remaining local variables as parameters
6. If more than one value needs to be returned, consider returning an object or extracting further
7. Replace the original code with a call to the new method
8. Run tests
### Example
**Before:**
```javascript
function printOwing(invoice) {
let outstanding = 0;
// print banner
console.log("***********************");
console.log("**** Customer Owes ****");
console.log("***********************");
// calculate outstanding
for (const order of invoice.orders) {
outstanding += order.amount;
}
// print details
console.log(`name: ${invoice.customer}`);
console.log(`amount: ${outstanding}`);
console.log(`due: ${invoice.dueDate.toLocaleDateString()}`);
}
```
**After:**
```javascript
function printOwing(invoice) {
printBanner();
const outstanding = calculateOutstanding(invoice);
printDetails(invoice, outstanding);
}
function printBanner() {
console.log("***********************");
console.log("**** Customer Owes ****");
console.log("***********************");
}
function calculateOutstanding(invoice) {
let result = 0;
for (const order of invoice.orders) {
result += order.amount;
}
return result;
}
function printDetails(invoice, outstanding) {
console.log(`name: ${invoice.customer}`);
console.log(`amount: ${outstanding}`);
console.log(`due: ${invoice.dueDate.toLocaleDateString()}`);
}
```
### Naming Guidelines
| Bad Name | Good Name | Why |
|----------|-----------|-----|
| `doStuff()` | `calculateMonthlyTotal()` | Names the intent, not the vagueness |
| `process()` | `validateAndSaveOrder()` | Specific about what it does |
| `handleData()` | `parseCSVRow()` | Names the domain concept |
| `helper()` | `formatCurrencyForDisplay()` | Describes the transformation |
| `step2()` | `applyDiscountRules()` | Names the business concept |
**Rule of thumb:** If you can't find a good name, the extraction boundaries may be wrong. Try extracting a different fragment.
---
## Inline Method
The inverse of Extract Method. Replace a method call with the method's body when the body is as clear as the name, or when you need to regroup poorly factored code.
### Motivation
Sometimes a method body is as obvious as the method name. Indirection without value is noise. Also useful as an intermediate step: inline a badly decomposed method, then re-extract along better boundaries.
### Mechanics
1. Check that the method is not polymorphic (no subclass overrides it)
2. Find all callers
3. Replace each call with the method body
4. Delete the method
5. Run tests
### Example
**Before:**
```python
def get_rating(self):
return 2 if self.more_than_five_late_deliveries() else 1
def more_than_five_late_deliveries(self):
return self.late_deliveries > 5
```
**After:**
```python
def get_rating(self):
return 2 if self.late_deliveries > 5 else 1
```
### When NOT to Inline
- When the method name communicates domain meaning the code doesn't
- When the method is used in multiple places (DRY)
- When the method is overridden in subclasses
---
## Extract Variable
Introduce a local variable for a complex expression to make it self-documenting.
### Motivation
Expressions can become hard to read. A well-named variable for a sub-expression acts as inline documentation and makes debugging easier.
### Mechanics
1. Identify a complex expression or sub-expression
2. Declare a variable named for the intent of the expression
3. Replace the expression with the variable
4. Run tests
### Example
**Before:**
```javascript
return order.quantity * order.itemPrice -
Math.max(0, order.quantity - 500) * order.itemPrice * 0.05 +
Math.min(order.quantity * order.itemPrice * 0.1, 100);
```
**After:**
```javascript
const basePrice = order.quantity * order.itemPrice;
const quantityDiscount = Math.max(0, order.quantity - 500) * order.itemPrice * 0.05;
const shippingCap = Math.min(basePrice * 0.1, 100);
return basePrice - quantityDiscount + shippingCap;
```
---
## Inline Variable
The inverse of Extract Variable. Remove a variable when the expression is just as clear.
### When to Use
- The variable name adds no information beyond what the expression says
- The variable is assigned once and used once
- The variable is blocking another refactoring (e.g., you need to inline it to then Extract Method)
### Example
**Before:**
```python
base_price = order.base_price()
return base_price > 1000
```
**After:**
```python
return order.base_price() > 1000
```
---
## Replace Temp with Query
Turn a temporary variable into a method call so the computation is reusable and the original method becomes shorter.
### Motivation
Temporaries can only be seen within a single method. If the same computation is needed elsewhere, it gets duplicated. A query method is visible to the whole class (or can be extracted to another class).
### Mechanics
1. Check that the variable is assigned once and the expression has no side effects
2. Extract the right-hand side of the assignment into a new method
3. Replace all references to the temp with calls to the new method
4. Remove the temp declaration and assignment
5. Run tests
### Example
**Before:**
```javascript
class Order {
getPrice() {
const basePrice = this.quantity * this.itemPrice;
if (basePrice > 1000) {
return basePrice * 0.95;
} else {
return basePrice * 0.98;
}
}
}
```
**After:**
```javascript
class Order {
getPrice() {
if (this.basePrice() > 1000) {
return this.basePrice() * 0.95;
} else {
return this.basePrice() * 0.98;
}
}
basePrice() {
return this.quantity * this.itemPrice;
}
}
```
### Performance Note
Calling the method multiple times instead of caching in a temp may seem wasteful. In practice, the performance impact is negligible for most code. Profile before optimizing. Refactored code is easier to optimize later because the hot path is isolated.
---
## Split Temporary Variable
When a temporary variable is assigned more than once (and it's not a loop counter or collecting variable), it's doing two different jobs. Give each job its own variable.
### Motivation
A temp assigned twice for different purposes misleads the reader into thinking the assignments are related. Each role deserves its own variable with a descriptive name.
### Mechanics
1. Rename the first assignment to reflect its purpose
2. Declare it as `const`/`final` if possible
3. Find all uses that refer to the first assignment's value and make sure they use the new name
4. Repeat for each subsequent assignment with a different name
5. Run tests
### Example
**Before:**
```javascript
let temp = 2 * (height + width); // perimeter
console.log(temp);
temp = height * width; // area
console.log(temp);
```
**After:**
```javascript
const perimeter = 2 * (height + width);
console.log(perimeter);
const area = height * width;
console.log(area);
```
---
## Remove Assignments to Parameters
Never assign to a parameter inside a method body. It confuses readers about whether the change is visible to the caller (it isn't in pass-by-value languages; it is in pass-by-reference for object mutations).
### Mechanics
1. Create a new local variable for the parameter
2. Replace all assignments to the parameter with assignments to the new variable
3. Run tests
### Example
**Before:**
```python
def discount(input_val, quantity):
if quantity > 50:
input_val -= 2
if quantity > 100:
input_val -= 1
return input_val
```
**After:**
```python
def discount(input_val, quantity):
result = input_val
if quantity > 50:
result -= 2
if quantity > 100:
result -= 1
return result
```
---
## Replace Method with Method Object
When a method is too tangled with local variables to extract from, move the entire method into its own class where the local variables become fields. Then you can freely extract sub-methods.
### Motivation
Sometimes a long method has so many interrelated local variables that Extract Method is impossible (too many parameters would be needed). By turning the method into its own class, all locals become fields, accessible to any extracted method without parameters.
### Mechanics
1. Create a new class named after the method's purpose
2. Add a field for the original object and for every local variable and parameter
3. Create a constructor that takes the original object and all parameters
4. Copy the method body into a `compute()` (or similar) method
5. Replace the original method with: create the new object, call `compute()`
6. Now freely extract methods within the new class -- locals are fields, no parameter passing needed
7. Run tests
### Example
**Before:**
```python
class Account:
def gamma(self, input_val, quantity, year_to_date):
# 50 lines of tangled computation using all three params
# plus self.fields -- too intertwined to extract
...
```
**After:**
```python
class GammaCalculation:
def __init__(self, account, input_val, quantity, year_to_date):
self.account = account
self.input_val = input_val
self.quantity = quantity
self.year_to_date = year_to_date
def compute(self):
# Now extract freely -- all variables are fields
self._apply_quantity_adjustment()
self._apply_yearly_factor()
return self.input_val
def _apply_quantity_adjustment(self):
# can access self.quantity, self.input_val freely
...
def _apply_yearly_factor(self):
...
class Account:
def gamma(self, input_val, quantity, year_to_date):
return GammaCalculation(self, input_val, quantity, year_to_date).compute()
```
---
## Decision Guide: Which Composing Refactoring to Use
| Situation | Refactoring |
|-----------|-------------|
| Code block can be named by intent | Extract Method |
| Method body is trivial and name adds nothing | Inline Method |
| Complex expression needs explanation | Extract Variable |
| Variable adds no meaning beyond the expression | Inline Variable |
| Same computation needed in multiple methods | Replace Temp with Query |
| One variable serves two purposes | Split Temporary Variable |
| Parameter is reassigned inside method | Remove Assignments to Parameters |
| Long method with too many entangled locals | Replace Method with Method Object |
@@ -1,389 +0,0 @@
# Moving Features Between Objects
Detailed reference for refactorings that redistribute responsibilities between classes. The fundamental question of object-oriented design is: where should this behavior live? These refactorings provide the mechanical steps to move things to the right place.
## Table of Contents
1. [Move Method](#move-method)
2. [Move Field](#move-field)
3. [Extract Class](#extract-class)
4. [Inline Class](#inline-class)
5. [Hide Delegate](#hide-delegate)
6. [Remove Middle Man](#remove-middle-man)
7. [Introduce Foreign Method](#introduce-foreign-method)
8. [Introduce Local Extension](#introduce-local-extension)
9. [Decision Guide: Where Does This Behavior Belong?](#decision-guide-where-does-this-behavior-belong)
---
## Move Method
Move a method to the class it uses most. A method that accesses more features of another class than its own has Feature Envy and belongs somewhere else.
### Motivation
The most common reason for moving a method is Feature Envy -- when a method spends most of its time talking to another object. Moving the method reduces coupling: the method now lives where its data lives, so changes to that data don't ripple outward.
### Mechanics
1. Examine all features (fields and methods) used by the method. Determine which class has the most features used by the method.
2. Check for related methods in the source class. If other methods also use the same target class, consider moving them together.
3. Check superclasses and subclasses for overrides or related declarations.
4. Declare the method in the target class. Copy the body and adjust references -- `this` now refers to the target; the source object may need to be passed as a parameter.
5. Turn the source method into a delegating method (call the target).
6. Run tests.
7. Consider removing the delegating method if no other callers need it.
8. Run tests.
### Example
**Before:**
```javascript
class Account {
overdraftCharge() {
if (this.type.isPremium()) {
let result = 10;
if (this.daysOverdrawn > 7) {
result += (this.daysOverdrawn - 7) * 0.85;
}
return result;
} else {
return this.daysOverdrawn * 1.75;
}
}
}
```
The method depends heavily on `this.type` (an `AccountType` object). Move it there.
**After:**
```javascript
class AccountType {
overdraftCharge(daysOverdrawn) {
if (this.isPremium()) {
let result = 10;
if (daysOverdrawn > 7) {
result += (daysOverdrawn - 7) * 0.85;
}
return result;
} else {
return daysOverdrawn * 1.75;
}
}
}
class Account {
overdraftCharge() {
return this.type.overdraftCharge(this.daysOverdrawn);
}
}
```
### Decision Criteria
Move a method when:
- It uses more fields/methods of another class than its own
- The target class is likely to change in ways that affect this method
- Related methods already live in the target class
Don't move when:
- The method uses features from multiple classes equally (keep it in the most stable location)
- Polymorphism on the source class is needed
---
## Move Field
Move a field to the class that uses it more. Similar to Move Method but for data.
### Motivation
A field used more by another class signals that the data model is out of alignment with the behavior model. Moving the field keeps data and behavior together.
### Mechanics
1. If the field is public, encapsulate it first (Encapsulate Field)
2. Create the field in the target class with a getter and setter
3. Determine how to reference the target from the source (usually an existing association)
4. Update the source getter to delegate to the target
5. Run tests
6. Remove the field from the source class
7. Run tests
### Example
**Before:**
```python
class Customer:
def __init__(self):
self.discount_rate = 0.0
class Order:
def discounted_total(self):
return self.base_total() - (self.base_total() * self.customer.discount_rate)
```
`discount_rate` is only read by `Order` through `Customer`. If most logic involving `discount_rate` lives in the customer's pricing context, keep it in `Customer`. But if `Order` is the primary consumer and `discount_rate` is really about order pricing policy, consider moving it.
---
## Extract Class
Split a class that does two things into two classes that each do one thing.
### Motivation
A class with too many responsibilities grows too large and becomes hard to understand. If you can identify a coherent subset of fields and methods that relate to each other more than to the rest of the class, that subset deserves its own class.
### Mechanics
1. Identify the subset of responsibilities to split out
2. Create a new class named after the split-out responsibility
3. Add a link from the old class to the new class
4. Use Move Field for each field in the subset
5. Use Move Method for each method in the subset
6. Review the interfaces of both classes. Remove unneeded methods, rename as appropriate.
7. Decide whether to expose the new class or hide it behind the original
8. Run tests
### Example
**Before:**
```javascript
class Person {
constructor() {
this.name = '';
this.officeAreaCode = '';
this.officeNumber = '';
}
get telephoneNumber() {
return `(${this.officeAreaCode}) ${this.officeNumber}`;
}
}
```
**After:**
```javascript
class TelephoneNumber {
constructor() {
this.areaCode = '';
this.number = '';
}
toString() {
return `(${this.areaCode}) ${this.number}`;
}
}
class Person {
constructor() {
this.name = '';
this.telephoneNumber = new TelephoneNumber();
}
get telephone() {
return this.telephoneNumber.toString();
}
}
```
### Signals That Suggest Extraction
| Signal | What to Extract |
|--------|----------------|
| Field name prefix groups (e.g., `shippingStreet`, `shippingCity`) | `ShippingAddress` class |
| Methods that only use a subset of fields | The subset + its methods = new class |
| Subsets change at different rates | The faster-changing subset deserves its own class |
| Subsets have different collaborators | Each collaborator relationship = potential class boundary |
---
## Inline Class
The inverse of Extract Class. Merge a class that no longer carries its weight back into another class.
### Motivation
A class that does too little -- perhaps after previous refactorings moved its responsibilities elsewhere -- adds complexity without value. Fold it back into the class that uses it.
### Mechanics
1. For each public method and field of the source class, create a corresponding member in the target class
2. Change all references to the source class to use the target class instead
3. Run tests
4. Delete the source class
5. Run tests
### When to Use
- The class has only one or two trivial methods
- The class was created by Extract Class but subsequent refactorings emptied it
- The class adds indirection without any logic, validation, or behavior of its own
---
## Hide Delegate
Encapsulate the fact that one object delegates to another. Create a method on the server that hides the delegate from the client, enforcing the Law of Demeter.
### Motivation
When a client calls `person.getDepartment().getManager()`, the client knows about the `Department` class -- it's coupled to the navigation structure. If `Department` changes its interface, the client breaks. By adding `person.getManager()` (which internally calls `department.getManager()`), the client only knows about `Person`.
### Mechanics
1. For each method the client calls on the delegate, create a simple delegating method on the server
2. Change the client to call the server method instead
3. If no client needs the delegate accessor anymore, remove it
4. Run tests
### Example
**Before:**
```python
# Client code:
manager = person.department.manager
```
**After:**
```python
class Person:
@property
def manager(self):
return self.department.manager
# Client code:
manager = person.manager
```
### The Trade-Off
Hiding every delegate leads to the Middle Man smell -- a class that does nothing but forward calls. The right balance:
| Situation | Action |
|-----------|--------|
| Delegate's interface is unstable | Hide it (protect callers from change) |
| Client uses many delegate methods | Consider Hide Delegate for each |
| Server is becoming pure forwarding | Remove Middle Man |
| Chain is deep (a.b.c.d) | Definitely hide |
---
## Remove Middle Man
The inverse of Hide Delegate. When a class consists primarily of methods that delegate to another class, let the client call the delegate directly.
### Motivation
As a system evolves, more and more delegating methods accumulate until the "server" class adds no value -- it's just a pass-through. At that point, remove the indirection.
### Mechanics
1. Create a getter for the delegate on the server (if one doesn't exist)
2. For each delegating method that adds no value, redirect the client to call the delegate directly
3. Remove the delegating method from the server
4. Run tests
### Example
**Before:**
```javascript
class Person {
get manager() { return this.department.manager; }
get budget() { return this.department.budget; }
get headcount() { return this.department.headcount; }
get location() { return this.department.location; }
// ... 10 more forwarding methods
}
```
**After:**
```javascript
class Person {
get department() { return this._department; }
}
// Client:
const manager = person.department.manager;
```
---
## Introduce Foreign Method
When a server class needs an additional method but you can't modify it (third-party library, frozen module), create the method in the client class and pass the server object as the first argument.
### Motivation
A utility method that "should" be on the server class but can't be added there. The foreign method is a workaround -- mark it as such, so if the server class is ever opened for modification, the method can be moved.
### Example
```python
# Server class (third-party, can't modify):
# date = Date(year, month, day)
# Foreign method in client:
def next_day(date):
"""Foreign method -- should be on Date class."""
return Date(date.year, date.month, date.day + 1)
```
---
## Introduce Local Extension
When you need several foreign methods on a server class you can't modify, create a new class -- either a subclass or a wrapper -- that adds the missing methods.
### Subclass vs. Wrapper
| Approach | When to Use |
|----------|-------------|
| Subclass | When you can subclass the server; simplest approach |
| Wrapper (Decorator) | When you can't subclass (final class); forward all original methods |
### Example (Wrapper)
```javascript
class EnhancedDate {
constructor(date) {
this._original = date;
}
// Forward original methods
getYear() { return this._original.getYear(); }
getMonth() { return this._original.getMonth(); }
// New methods
nextDay() {
return new EnhancedDate(
new Date(this._original.getTime() + 86400000)
);
}
isWeekend() {
const day = this._original.getDay();
return day === 0 || day === 6;
}
}
```
---
## Decision Guide: Where Does This Behavior Belong?
Use these questions to decide whether and where to move code:
| Question | If Yes | Action |
|----------|--------|--------|
| Does this method use more of another class's features? | Feature Envy | Move Method to that class |
| Is this field used more by another class? | Misplaced data | Move Field to that class |
| Does this class have two groups of fields that don't interact? | Multiple responsibilities | Extract Class |
| Is this class just a thin wrapper with no logic? | Unnecessary indirection | Inline Class |
| Is the client navigating through an object chain? | Tight coupling | Hide Delegate |
| Is this class just forwarding calls? | Middle Man smell | Remove Middle Man |
| Need to add a method to a class you can't modify? | Missing feature | Introduce Foreign Method or Local Extension |
### The Responsibility Placement Heuristic
When unsure where to put a method, ask: **"If the data this method uses changes, which class should need to be updated?"** The method belongs in that class. This keeps data and behavior together, minimizing the ripple effect of change.
@@ -1,433 +0,0 @@
# Organizing Data
Detailed reference for refactorings that improve how data is represented. Raw primitives, magic numbers, exposed fields, and mutable collections create subtle bugs and scatter domain knowledge. These refactorings replace primitive representations with objects that encapsulate behavior and enforce invariants.
## Table of Contents
1. [Replace Data Value with Object](#replace-data-value-with-object)
2. [Change Value to Reference](#change-value-to-reference)
3. [Replace Array with Object](#replace-array-with-object)
4. [Replace Magic Number with Symbolic Constant](#replace-magic-number-with-symbolic-constant)
5. [Encapsulate Field](#encapsulate-field)
6. [Encapsulate Collection](#encapsulate-collection)
7. [Replace Type Code with Class](#replace-type-code-with-class)
8. [Decision Guide: Which Data Refactoring to Use](#decision-guide-which-data-refactoring-to-use)
---
## Replace Data Value with Object
Wrap a primitive data item in a class when it has behavior or validation associated with it. This is the cure for Primitive Obsession.
### Motivation
A data value starts life as a simple string or number. Then you add validation. Then formatting. Then comparison logic. Then the same validation appears in three places. At that point, the value deserves to be an object.
### Mechanics
1. Create a class for the value with a constructor that takes the primitive
2. Add validation in the constructor
3. Add any behavior methods (formatting, comparison, etc.)
4. Change the field type from primitive to the new class
5. Update all code that sets the field to create an instance of the new class
6. Update all code that reads the field to use the object's methods
7. Run tests
### Example
**Before:**
```javascript
class Order {
constructor(customer) {
this.customer = customer; // just a string name
}
}
// Scattered validation in multiple places:
if (order.customer === '') throw new Error('no customer');
if (otherOrder.customer === '') throw new Error('no customer');
```
**After:**
```javascript
class Customer {
constructor(name) {
if (!name || name.trim() === '') {
throw new Error('Customer name is required');
}
this._name = name.trim();
}
get name() { return this._name; }
equals(other) {
return other instanceof Customer && this._name === other._name;
}
}
class Order {
constructor(customer) {
this.customer = new Customer(customer);
}
}
```
### Common Primitive-to-Object Upgrades
| Primitive | Object | Behavior It Gains |
|-----------|--------|-------------------|
| `String email` | `EmailAddress` | Format validation, domain extraction |
| `number cents` | `Money` | Currency, rounding rules, arithmetic |
| `String phone` | `PhoneNumber` | Formatting, country code parsing |
| `number lat, number lng` | `Coordinates` | Distance calculation, validation |
| `String startDate, String endDate` | `DateRange` | Contains, overlaps, duration |
| `number celsius` | `Temperature` | Unit conversion, comparison |
| `String hex` | `Color` | Parsing, lightness, contrast |
| `number status` | `OrderStatus` | Valid transitions, display name |
---
## Change Value to Reference
Convert a value object into a reference object when you need identity semantics -- when changes to one instance should be visible everywhere that instance is used.
### Motivation
When you have multiple copies of the same customer, changing the phone number on one doesn't change it on the others. If business rules require a single shared instance, convert value to reference using a registry or repository.
### Mechanics
1. Determine or create a factory method for the object
2. Set up a registry (map, repository, or lookup service) to store instances
3. Change the factory to check the registry before creating new instances
4. Change client code to use the factory instead of the constructor
5. Run tests
### Example
```javascript
// Registry pattern:
class CustomerRepository {
constructor() {
this._customers = new Map();
}
get(id) {
if (!this._customers.has(id)) {
this._customers.set(id, new Customer(id));
}
return this._customers.get(id);
}
}
// All orders for customer #123 now share the same Customer object
const repo = new CustomerRepository();
const order1 = new Order(repo.get(123));
const order2 = new Order(repo.get(123));
// order1.customer === order2.customer // true (same reference)
```
### Value vs. Reference: Decision Guide
| Question | Value | Reference |
|----------|-------|-----------|
| Do you need identity (same object everywhere)? | No | Yes |
| Is the object immutable? | Typically | May be mutable |
| Do you compare by content? | Yes (`equals()`) | No (identity `===`) |
| Examples | Money, DateRange, Color | Customer, Account, Product |
---
## Replace Array with Object
Replace an array used as a record (where each position has a different meaning) with an object with named fields.
### Motivation
`row[0]` is the name, `row[1]` is the age, `row[2]` is the department. This is fragile, unreadable, and type-unsafe. Named fields make the structure self-documenting.
### Mechanics
1. Create a class with a field for each array position
2. Add getters and setters for each field
3. Replace array creation with object construction
4. Replace positional access with named access
5. Run tests
### Example
**Before:**
```python
performance = ["Liverpool", 15, 2]
name = performance[0]
wins = performance[1]
losses = performance[2]
```
**After:**
```python
class Performance:
def __init__(self, name, wins, losses):
self.name = name
self.wins = wins
self.losses = losses
performance = Performance("Liverpool", 15, 2)
name = performance.name
wins = performance.wins
losses = performance.losses
```
---
## Replace Magic Number with Symbolic Constant
Replace a literal number that has a particular meaning with a named constant.
### Motivation
`9.81` means nothing in code. `GRAVITATIONAL_ACCELERATION = 9.81` communicates intent, prevents typos (the constant name is checked by the compiler), and centralizes the value for easy change.
### Mechanics
1. Declare a constant and set it to the magic number
2. Find all occurrences of the magic number
3. Replace each occurrence with the constant (check that each occurrence represents the same concept -- the number `100` might mean "percentage" in one place and "max items" in another)
4. Run tests
### Common Magic Number Categories
| Category | Before | After |
|----------|--------|-------|
| Physics | `9.81` | `GRAVITATIONAL_ACCELERATION` |
| Business rules | `0.08` | `SALES_TAX_RATE` |
| Limits | `255` | `MAX_RGB_VALUE` |
| HTTP | `404` | `HTTP_NOT_FOUND` |
| Time | `86400` | `SECONDS_PER_DAY` |
| Retry | `3` | `MAX_RETRY_ATTEMPTS` |
| Thresholds | `100` | `FREE_SHIPPING_THRESHOLD` |
### When NOT to Replace
- `0` and `1` in arithmetic are usually fine as literals
- Loop counters (`for i in range(10)`) are obvious from context
- Array index `[0]` for "first element" is idiomatic
---
## Encapsulate Field
Replace direct access to a public field with getter and setter methods.
### Motivation
A public field gives you no control over reads and writes. You can't add validation, logging, lazy initialization, or computed values later without changing every caller. Encapsulation creates a seam for future change.
### Mechanics
1. Create getter and setter methods for the field
2. Find all references to the field and replace reads with the getter, writes with the setter
3. Make the field private
4. Run tests
### Example
**Before:**
```python
class Person:
def __init__(self, name):
self.name = name # public field
# Client:
person.name = " Bob " # no validation, no trimming
```
**After:**
```python
class Person:
def __init__(self, name):
self._name = None
self.name = name # uses the setter
@property
def name(self):
return self._name
@name.setter
def name(self, value):
if not value or not value.strip():
raise ValueError("Name cannot be empty")
self._name = value.strip()
```
---
## Encapsulate Collection
Don't return a raw mutable collection from a getter. Instead, return an unmodifiable view or a copy, and provide explicit add/remove methods.
### Motivation
When a getter returns a mutable list, callers can add, remove, or clear items without the owning object knowing. This breaks encapsulation -- the object can't enforce invariants, fire events, or validate changes.
### Mechanics
1. Add `addItem()` and `removeItem()` methods on the owning class
2. Change the getter to return an unmodifiable view (or a copy)
3. Find all callers that mutate the collection through the getter and change them to use the add/remove methods
4. Run tests
### Example
**Before:**
```javascript
class Course {}
class Person {
get courses() { return this._courses; }
set courses(list) { this._courses = list; }
}
// Client can mutate freely:
person.courses.push(newCourse); // bypasses Person
person.courses.splice(0, 1); // bypasses Person
person.courses = []; // replaces internal state
```
**After:**
```javascript
class Person {
get courses() {
return [...this._courses]; // return a copy
}
addCourse(course) {
this._courses.push(course);
}
removeCourse(course) {
const index = this._courses.indexOf(course);
if (index === -1) throw new RangeError('Course not found');
this._courses.splice(index, 1);
}
get numberOfCourses() {
return this._courses.length;
}
}
```
### Language-Specific Patterns
| Language | Unmodifiable Return |
|----------|-------------------|
| Java | `Collections.unmodifiableList(list)` |
| JavaScript | `[...this._items]` or `Object.freeze([...this._items])` |
| Python | `tuple(self._items)` or `list(self._items)` (return a copy) |
| C# | `items.AsReadOnly()` |
| Go | Return a slice copy: `append([]T{}, items...)` |
---
## Replace Type Code with Class
Replace a type code (integer or string constant) that does not affect behavior with a proper class. Use when the type code is used for categorization but doesn't drive conditional logic.
### When to Use Which
| Situation | Refactoring |
|-----------|-------------|
| Type code is informational only (no behavior change) | Replace Type Code with Class |
| Type code drives behavior via conditionals | Replace Type Code with Subclasses |
| Type code can change at runtime | Replace Type Code with Strategy/State |
| Type code has few values and language supports it | Use an Enum |
### Replace Type Code with Subclasses
Used when the type code determines behavior through conditionals.
**Before:**
```javascript
class Employee {
constructor(type) {
this._type = type; // 'engineer', 'manager', 'salesperson'
}
calculatePay() {
switch (this._type) {
case 'engineer': return this.basePay;
case 'manager': return this.basePay + this.bonus;
case 'salesperson': return this.basePay + this.commission;
}
}
canApproveExpenses() {
return this._type === 'manager';
}
}
```
**After:**
```javascript
class Employee {
calculatePay() { throw new Error('abstract'); }
canApproveExpenses() { return false; }
}
class Engineer extends Employee {
calculatePay() { return this.basePay; }
}
class Manager extends Employee {
calculatePay() { return this.basePay + this.bonus; }
canApproveExpenses() { return true; }
}
class Salesperson extends Employee {
calculatePay() { return this.basePay + this.commission; }
}
```
### Replace Type Code with Strategy/State
Used when the type code can change at runtime (an employee can be promoted from engineer to manager), so subclassing the employee itself is not possible.
**After (Strategy):**
```javascript
class Employee {
constructor(type) {
this._type = type; // EmployeeType strategy object
}
calculatePay() {
return this._type.calculatePay(this);
}
promoteToManager() {
this._type = new ManagerType();
}
}
class EngineerType {
calculatePay(employee) { return employee.basePay; }
}
class ManagerType {
calculatePay(employee) { return employee.basePay + employee.bonus; }
}
```
---
## Decision Guide: Which Data Refactoring to Use
| Situation | Refactoring |
|-----------|-------------|
| Primitive value has associated behavior | Replace Data Value with Object |
| Need one shared instance across the system | Change Value to Reference |
| Array positions have different meanings | Replace Array with Object |
| Literal number has domain meaning | Replace Magic Number with Symbolic Constant |
| Public field needs future flexibility | Encapsulate Field |
| Getter returns mutable collection | Encapsulate Collection |
| Type code is informational | Replace Type Code with Class / Enum |
| Type code drives behavior | Replace Type Code with Subclasses |
| Type code changes at runtime | Replace Type Code with Strategy |
@@ -1,373 +0,0 @@
# Refactoring Workflow
Detailed reference for when and how to refactor safely. The discipline of refactoring is as important as knowing the individual transformations. This reference covers the refactoring cycle, timing, safety techniques, and strategies for large-scale refactoring in production systems.
## Table of Contents
1. [The Refactoring Cycle](#the-refactoring-cycle)
2. [When to Refactor](#when-to-refactor)
3. [When NOT to Refactor](#when-not-to-refactor)
4. [Testing and Refactoring](#testing-and-refactoring)
5. [Refactoring and Performance](#refactoring-and-performance)
6. [Branch by Abstraction](#branch-by-abstraction)
7. [Parallel Change (Expand-Migrate-Contract)](#parallel-change-expand-migrate-contract)
8. [Large-Scale Refactoring Strategies](#large-scale-refactoring-strategies)
9. [Refactoring Checklist](#refactoring-checklist)
---
## The Refactoring Cycle
Every refactoring follows the same four-step loop:
```
1. Run tests → GREEN
2. Apply one small structural change
3. Run tests → GREEN
4. Commit
```
**Then repeat.**
### Why Small Steps Matter
| Approach | Risk | Recovery Time |
|----------|------|---------------|
| One refactoring at a time | Minimal -- if tests fail, the cause is obvious | Seconds (revert one change) |
| Several refactorings between tests | Medium -- must debug to find which one broke | Minutes |
| Big-bang rewrite | Maximum -- structural and behavioral changes mixed | Hours to days (or never) |
**Rule:** If a test fails after a refactoring step, **revert immediately**. Don't debug. The step was too big or wrong. Revert, think, try a smaller step.
### The Two Hats
Martin Fowler describes two distinct modes of work. You wear only one "hat" at a time:
| Hat | What You Do | What You Don't Do |
|-----|-------------|-------------------|
| **Refactoring** | Change structure, keep behavior identical | Add features, fix bugs, change tests |
| **Adding Function** | Add new behavior, write new tests | Change existing code structure |
**Switching hats:** You may alternate frequently, but never wear both at once. A typical sequence:
1. Refactoring hat: restructure to make the new feature easy to add. Commit.
2. Adding-function hat: add the feature and its tests. Commit.
3. Refactoring hat: clean up any mess the new feature introduced. Commit.
---
## When to Refactor
### Preparatory Refactoring (Refactor to Make the Change Easy)
**Trigger:** You're about to add a feature, and the code isn't structured to accommodate it easily.
**Example:** You need to add a new payment method. The payment logic is in a long if/else chain. Before adding the new branch, refactor to Replace Conditional with Polymorphism. Now adding the new payment method means creating one new class.
**Kent Beck's quote:** "Make the change easy (warning: this may be hard), then make the easy change."
**The payoff:** The feature is faster to add, less likely to contain bugs, and the refactoring improves the code for all future changes, not just this one.
### Comprehension Refactoring (Refactor to Understand)
**Trigger:** You're reading code and struggling to understand it. Rename variables, extract methods, and reorganize to make the code express its intent.
**Example:** You encounter a function called `calc` with variables named `a`, `b`, and `temp`. As you figure out what each does, rename them: `calculateMonthlyPayment`, `principal`, `interestRate`, `monthlyAmount`. The understanding you gain is encoded in the code itself.
**Ward Cunningham's insight:** "By refactoring, I move the understanding from my head into the code itself."
### Litter-Pickup Refactoring (Boy Scout Rule)
**Trigger:** You touch a file for any reason and notice a small improvement you can make. Do it.
**Examples:**
- Rename a misleading variable name
- Extract a method from a long function
- Remove dead code
- Add a missing guard clause
**The rule:** Leave the code cleaner than you found it. Each small improvement compounds over time. A codebase that is consistently cleaned by every developer who touches it stays healthy.
### Rule of Three
**Trigger:** The third time you see duplicated code or a repeated pattern.
**The progression:**
1. First time: Write it
2. Second time: Wince at the duplication but tolerate it
3. Third time: Refactor -- extract the common pattern
**Why three, not two:** Premature abstraction is as dangerous as duplication. Two occurrences might be coincidental. Three confirms the pattern.
### Long-Term Refactoring
**Trigger:** A large structural problem that can't be fixed in one session.
**Examples:**
- Replacing a library or framework
- Splitting a monolith into modules
- Changing a pervasive data representation
**Approach:** The team agrees on a target architecture. Everyone makes small changes toward it during regular work. No one stops feature development for a "refactoring sprint."
---
## When NOT to Refactor
Not every piece of code deserves refactoring. Save your effort for code that justifies it.
### Code You Should Leave Alone
| Situation | Why |
|-----------|-----|
| The code works and nobody needs to modify it | If it's behind a clean interface, its internal messiness costs nothing |
| It's easier to rewrite from scratch | If the code is small and the rewrite is straightforward, don't polish what you'll replace |
| There are no tests and adding them is impractical | Refactoring without tests is too risky; consider characterization tests first |
| The code will be deleted soon | Don't beautify code with a known end-of-life |
| You're exploring or prototyping | Throwaway code benefits from speed, not structure |
### The "Messy Middle" Trap
Some teams swing between two extremes:
- **Never refactor:** Technical debt accumulates until development grinds to a halt
- **Always refactor:** Gold-plating code that doesn't need it, shipping features slowly
The right balance: **Refactor code that you're about to change, or code that's actively hurting velocity.** Don't refactor code just because it's not beautiful.
---
## Testing and Refactoring
### The Safety Net
Tests are not optional for refactoring. Without them, you cannot verify that behavior is preserved.
| Test Type | Role in Refactoring |
|-----------|-------------------|
| Unit tests | Fast feedback on individual method behavior |
| Integration tests | Verify behavior across collaborating objects |
| Characterization tests | Capture existing behavior of legacy code (the starting point) |
| Regression tests | Ensure the entire system still works after changes |
### Characterization Tests
When you encounter code without tests that you need to refactor:
1. Run the code with known inputs
2. Observe the actual outputs (even if you think they're "wrong")
3. Write tests that assert the actual current behavior
4. Now you have a safety net -- refactor freely
**Example:**
```python
def test_weird_edge_case():
# This behavior may be "wrong" but it's what exists.
# Capture it so refactoring doesn't accidentally change it.
result = calculate_shipping(weight=0, distance=100)
assert result == 5.99 # Captures existing behavior
```
### Test-Driven Refactoring Steps
1. **Before starting:** Run all tests. If any fail, fix them first.
2. **After each refactoring step:** Run tests. All must pass.
3. **If a test fails:** Revert immediately. Don't debug.
4. **After completing a logical group of refactorings:** Commit.
5. **If you discover a bug during refactoring:** Stop refactoring. Fix the bug (adding-function hat). Then resume refactoring.
---
## Refactoring and Performance
### The Common Fear
"Won't all these small methods and indirection make the code slower?"
### The Reality
1. Most performance concerns about refactored code are unfounded. Modern compilers and runtimes inline small methods.
2. Performance bottlenecks are almost never where you think they are. Profile first.
3. Well-structured code is **easier** to optimize because the hot path is isolated.
### The Three-Step Performance Strategy
1. **Write clear code first.** Don't optimize during refactoring.
2. **Profile the running system.** Find the actual bottleneck (usually 10% of the code causes 90% of the performance issue).
3. **Optimize only the measured hot path.** Well-refactored code makes this easy because the hot path is in a small, isolated method.
### When Refactoring Genuinely Hurts Performance
| Refactoring | Potential Cost | Mitigation |
|-------------|---------------|------------|
| Replace Temp with Query | Method called multiple times instead of cached once | Cache if profiling shows impact |
| Extract Method | Additional method call overhead | Usually inlined by the compiler/JIT |
| Replace Conditional with Polymorphism | Virtual dispatch instead of branch | Negligible in most cases; profile if in doubt |
| Introduce Parameter Object | Object allocation for each call | Often optimized away; pool if necessary |
**Key insight:** Optimization and refactoring are separate concerns. Refactor first for clarity, then optimize the measured bottleneck.
---
## Branch by Abstraction
A technique for making large-scale changes to a widely-used component without creating a long-lived feature branch.
### When to Use
- Replacing a framework, library, or major internal component
- The replacement will take weeks or months
- You need to keep shipping features during the transition
- Feature branches would become stale and cause merge conflicts
### How It Works
```
Step 1: Identify the component to replace (OldComponent)
Step 2: Create an abstraction layer (interface) that wraps OldComponent
Step 3: Change all callers to use the abstraction (deploy incrementally)
Step 4: Create NewComponent that implements the same abstraction
Step 5: Switch the abstraction to point to NewComponent (one change, deploy)
Step 6: Remove OldComponent and the abstraction layer (clean up)
```
### Example
**Step 1-2:** Introduce the abstraction
```python
# Before: callers use OldPaymentGateway directly
class OldPaymentGateway:
def charge(self, amount, card): ...
# After: introduce abstraction
class PaymentGateway(ABC):
@abstractmethod
def charge(self, amount, card): ...
class OldPaymentGateway(PaymentGateway):
def charge(self, amount, card): ... # existing implementation
```
**Step 3:** Migrate callers to use `PaymentGateway` (the abstraction). Deploy.
**Step 4:** Build `NewPaymentGateway(PaymentGateway)`. Test thoroughly.
**Step 5:** Switch the wiring:
```python
# In configuration:
# gateway = OldPaymentGateway() # old
gateway = NewPaymentGateway() # new
```
**Step 6:** Delete `OldPaymentGateway`. Optionally inline the abstraction if only one implementation remains.
---
## Parallel Change (Expand-Migrate-Contract)
A technique for making breaking API changes safely by running old and new versions side by side.
### When to Use
- Renaming a widely-used method or changing its signature
- Changing a data format while consumers still read the old format
- Migrating from one API to another when you can't update all consumers at once
### The Three Phases
**1. Expand:** Add the new version alongside the old one.
```python
class User:
def get_full_name(self): # new name
return f"{self.first} {self.last}"
def getFullName(self): # old name, still works
return self.get_full_name() # delegates to new
```
**2. Migrate:** Update all callers to use the new version. This can happen incrementally across multiple deployments.
**3. Contract:** Remove the old version once all callers have migrated.
```python
class User:
def get_full_name(self): # only the new version remains
return f"{self.first} {self.last}"
```
### Parallel Change for Data
```
1. Expand: Write to both old and new columns/formats
2. Migrate: Update all readers to use the new format
3. Contract: Stop writing the old format, remove old column
```
---
## Large-Scale Refactoring Strategies
### The Strangler Fig Pattern
Gradually replace a legacy system by building new functionality around it, routing more and more traffic to the new system until the old one can be decommissioned.
| Phase | Action |
|-------|--------|
| 1. Intercept | Place a routing layer in front of the legacy system |
| 2. Build new | Implement new components behind the router |
| 3. Redirect | Route requests to new components as they're ready |
| 4. Retire | Decommission old components once no traffic reaches them |
### Mikado Method
For complex refactorings with many interdependencies:
1. Try the refactoring you want to make
2. If it breaks, note what needs to change first (the prerequisites)
3. Revert your change
4. Recursively fix the prerequisites (each may have its own prerequisites)
5. Build a dependency graph (the "Mikado Graph")
6. Solve the graph from the leaves (no-dependency tasks) toward the root
### Feature Toggles During Refactoring
Use feature flags to gradually roll out a refactored component:
```python
if feature_flag('new_pricing_engine'):
return new_pricing_engine.calculate(order)
else:
return old_pricing_engine.calculate(order)
```
This allows:
- Incremental rollout (10% of traffic, then 50%, then 100%)
- Instant rollback by toggling the flag
- A/B comparison of old vs. new behavior
---
## Refactoring Checklist
Use this checklist before, during, and after refactoring sessions:
### Before Starting
- [ ] All existing tests pass (green)
- [ ] You've identified the specific smell or improvement target
- [ ] You can name the refactoring(s) you'll apply
- [ ] The code has test coverage for the area you'll change (add characterization tests if not)
### During Refactoring
- [ ] Each step is the smallest possible transformation
- [ ] Tests run after every step
- [ ] You revert immediately if tests fail (don't debug)
- [ ] You're wearing only the refactoring hat (no new features)
- [ ] You commit after each logical group of steps
### After Completing
- [ ] All tests still pass
- [ ] The code is easier to read than before
- [ ] Variable and method names reveal intent
- [ ] No unnecessary comments remain (the code explains itself)
- [ ] No new smells were introduced
- [ ] Changes are committed with a clear message describing the refactoring
@@ -1,385 +0,0 @@
# Simplifying Conditional Logic
Detailed reference for refactorings that tame complex conditional structures. Conditionals are the hardest code to read and the most likely to harbor bugs. These refactorings decompose, consolidate, and replace conditionals with clearer alternatives.
## Table of Contents
1. [Decompose Conditional](#decompose-conditional)
2. [Consolidate Conditional Expression](#consolidate-conditional-expression)
3. [Replace Nested Conditional with Guard Clauses](#replace-nested-conditional-with-guard-clauses)
4. [Replace Conditional with Polymorphism](#replace-conditional-with-polymorphism)
5. [Introduce Special Case (Null Object)](#introduce-special-case-null-object)
6. [Introduce Assertion](#introduce-assertion)
7. [Decision Guide: Which Conditional Refactoring to Use](#decision-guide-which-conditional-refactoring-to-use)
---
## Decompose Conditional
Extract the condition, the then-branch, and the else-branch of a complex conditional into well-named methods.
### Motivation
A long `if` statement with a compound condition and multi-line branches forces the reader to simulate every path mentally. By naming each part, you turn the conditional into readable prose.
### Mechanics
1. Extract the condition into a method whose name describes the meaning (not the mechanics)
2. Extract the then-body into a method whose name describes what happens
3. Extract the else-body into a method whose name describes what happens
4. Run tests
### Example
**Before:**
```javascript
function calculateCharge(date, quantity, plan) {
let charge;
if (date.getMonth() >= 6 && date.getMonth() <= 8) {
charge = quantity * plan.summerRate;
} else {
charge = quantity * plan.regularRate + plan.regularServiceCharge;
}
return charge;
}
```
**After:**
```javascript
function calculateCharge(date, quantity, plan) {
if (isSummer(date)) {
return summerCharge(quantity, plan);
} else {
return regularCharge(quantity, plan);
}
}
function isSummer(date) {
return date.getMonth() >= 6 && date.getMonth() <= 8;
}
function summerCharge(quantity, plan) {
return quantity * plan.summerRate;
}
function regularCharge(quantity, plan) {
return quantity * plan.regularRate + plan.regularServiceCharge;
}
```
### Naming the Condition
| Condition Expression | Good Name |
|---------------------|-----------|
| `date.getMonth() >= 6 && date.getMonth() <= 8` | `isSummer(date)` |
| `user.age >= 18 && user.hasConsent` | `isEligible(user)` |
| `cart.total > 100 && !cart.hasPromo` | `qualifiesForDiscount(cart)` |
| `retries < MAX && !response.ok` | `shouldRetry(retries, response)` |
| `file.size > 0 && file.ext === '.csv'` | `isValidUpload(file)` |
The condition name should answer a yes/no question using the domain vocabulary.
---
## Consolidate Conditional Expression
Combine a series of conditional checks that all lead to the same result into a single conditional with a descriptive name.
### Motivation
When multiple conditions return the same value, combining them into one named check makes the logic clearer: "All of these mean the same thing -- this situation is X."
### Mechanics
1. Verify that all the conditionals have no side effects
2. Combine using logical operators (`&&`, `||`)
3. Extract the combined condition into a named method
4. Run tests
### Example
**Before:**
```python
def disability_amount(employee):
if employee.seniority < 2:
return 0
if employee.months_disabled > 12:
return 0
if employee.is_part_time:
return 0
# compute disability amount...
return base_amount * 1.5
```
**After:**
```python
def disability_amount(employee):
if is_not_eligible_for_disability(employee):
return 0
return base_amount * 1.5
def is_not_eligible_for_disability(employee):
return (employee.seniority < 2
or employee.months_disabled > 12
or employee.is_part_time)
```
### When to Consolidate vs. Keep Separate
| Situation | Action |
|-----------|--------|
| All conditions mean the same business concept | Consolidate into one named check |
| Conditions are independent with different reasons | Keep separate (each deserves its own name) |
| Conditions should be evaluated in sequence for performance | Keep separate for short-circuit clarity |
---
## Replace Nested Conditional with Guard Clauses
Handle special cases and edge conditions at the top of the method and return early, leaving the main path of execution flat and unindented.
### Motivation
Deeply nested `if/else` structures obscure the normal path. Guard clauses make it clear: "These are the edge cases. Now here's the main logic." The main path runs at the lowest indentation level.
### Mechanics
1. Identify each edge case or special condition
2. Move it to the top of the method as an `if (condition) return earlyValue;`
3. Remove the corresponding `else` and reduce indentation
4. Run tests
### Example
**Before:**
```javascript
function payAmount(employee) {
let result;
if (employee.isSeparated) {
result = { amount: 0, reasonCode: 'SEP' };
} else {
if (employee.isRetired) {
result = { amount: 0, reasonCode: 'RET' };
} else {
// main calculation
result = {
amount: employee.salary * employee.rate,
reasonCode: 'REG'
};
}
}
return result;
}
```
**After:**
```javascript
function payAmount(employee) {
if (employee.isSeparated) return { amount: 0, reasonCode: 'SEP' };
if (employee.isRetired) return { amount: 0, reasonCode: 'RET' };
return {
amount: employee.salary * employee.rate,
reasonCode: 'REG'
};
}
```
### Guard Clause Patterns
| Pattern | Example |
|---------|---------|
| Null check | `if (input == null) return defaultValue;` |
| Empty check | `if (items.length === 0) return [];` |
| Permission check | `if (!user.canEdit) throw new ForbiddenError();` |
| Boundary check | `if (index < 0 \|\| index >= size) throw new RangeError();` |
| Status check | `if (order.isCancelled) return zeroPay();` |
### "One return" vs. Guard Clauses
Some coding standards mandate a single return statement per method. This leads to deeply nested conditionals and temporary result variables. Guard clauses with early returns produce clearer, flatter code. Fowler explicitly recommends guard clauses over single-return for methods with special cases.
---
## Replace Conditional with Polymorphism
Replace a conditional that checks a type, status, or category and branches to different behavior with polymorphic classes where each type provides its own implementation.
### Motivation
This is the gold standard for eliminating type-based conditionals. Instead of one function that knows about every type, each type knows about itself. Adding a new type means adding a new class -- not editing existing conditionals in multiple places (Open/Closed Principle).
### Mechanics
1. If the conditional is based on a type code, apply Replace Type Code with Subclasses first
2. Create a base method (possibly abstract) in the superclass
3. Copy each branch of the conditional into the corresponding subclass as an override
4. Remove the conditional from the superclass (or make it the default case)
5. Run tests
### Example
**Before:**
```python
class Bird:
def __init__(self, bird_type, voltage=0, coconut_count=0):
self.type = bird_type
self.voltage = voltage
self.coconut_count = coconut_count
def speed(self):
if self.type == 'european':
return 35 - (self.voltage / 10)
elif self.type == 'african':
return 40 - (2 * self.coconut_count)
elif self.type == 'norwegian_blue':
return 0 if self.voltage > 100 else 10 + (self.voltage / 10)
else:
raise ValueError(f"Unknown bird type: {self.type}")
```
**After:**
```python
class Bird:
def speed(self):
raise NotImplementedError
class EuropeanSwallow(Bird):
def speed(self):
return 35 - (self.voltage / 10)
class AfricanSwallow(Bird):
def speed(self):
return 40 - (2 * self.coconut_count)
class NorwegianBlueParrot(Bird):
def speed(self):
return 0 if self.voltage > 100 else 10 + (self.voltage / 10)
```
### When to Use Polymorphism vs. Keep the Conditional
| Situation | Recommendation |
|-----------|---------------|
| Conditional appears in multiple methods | Polymorphism -- types know their own behavior |
| Only one method has the conditional | May be overkill -- Decompose Conditional may suffice |
| New types are added frequently | Polymorphism -- Open/Closed Principle |
| The set of types is fixed and small (e.g., 2-3) | Conditional may be simpler |
| Behavior varies by a code that changes at runtime | Use Strategy pattern instead of inheritance |
---
## Introduce Special Case (Null Object)
Instead of checking for a special case (usually null) in every caller, create a class that encapsulates the special-case behavior.
### Motivation
`if (customer == null)` checks scattered through the codebase add noise and are easy to forget. A `NullCustomer` or `UnknownCustomer` object responds to all the same methods with safe default behavior.
### Mechanics
1. Create a subclass or separate class for the special case
2. Add a method to the superclass or factory that creates the special case (e.g., `Customer.unknown()`)
3. Implement each method in the special case with the default behavior that callers currently use after their null checks
4. Change callers to use the special case object instead of null
5. Remove the null checks from callers
6. Run tests
### Example
**Before:**
```javascript
// Scattered throughout the codebase:
const customerName = (customer !== null) ? customer.name : 'Occupant';
const billingPlan = (customer !== null) ? customer.billingPlan : BillingPlan.basic();
const paymentHistory = (customer !== null) ? customer.paymentHistory : new NullPaymentHistory();
```
**After:**
```javascript
class UnknownCustomer {
get name() { return 'Occupant'; }
get billingPlan() { return BillingPlan.basic(); }
get paymentHistory() { return new NullPaymentHistory(); }
get isUnknown() { return true; }
}
class Customer {
get isUnknown() { return false; }
// ... normal implementation
}
// Callers (no more null checks):
const customerName = customer.name;
const billingPlan = customer.billingPlan;
```
### Common Special Cases
| Domain | Special Case Object | Default Behavior |
|--------|-------------------|------------------|
| Customer | `UnknownCustomer` | Returns "Occupant", basic plan |
| Currency | `NullMoney` | Zero amount, no currency |
| Logger | `NullLogger` | Silently discards all messages |
| Permission | `DeniedPermission` | Returns false for all checks |
| Config | `DefaultConfig` | Returns sensible defaults |
| User | `AnonymousUser` | Read-only, no privileges |
---
## Introduce Assertion
Make an assumption explicit by inserting an assertion that will fail fast if the assumption is violated.
### Motivation
Assertions document what the code expects to be true. They are executable documentation that catches bugs during development. Unlike comments, assertions are verified by the runtime.
### Mechanics
1. Identify an assumption in the code (a condition that should always be true)
2. Insert an assertion at the point where the assumption is made
3. Ensure the assertion does not have side effects
4. Run tests (they should still pass -- if an assertion fails, you found a bug)
### Example
**Before:**
```python
def apply_discount(product, discount_rate):
# discount should be between 0 and 1
price = product.base_price * (1 - discount_rate)
return price
```
**After:**
```python
def apply_discount(product, discount_rate):
assert 0 <= discount_rate <= 1, f"Discount rate must be 0-1, got {discount_rate}"
price = product.base_price * (1 - discount_rate)
return price
```
### Assertion Guidelines
| Guideline | Rationale |
|-----------|-----------|
| Never use assertions for input validation | Assertions can be disabled in production; use exceptions for untrusted input |
| Use assertions for programmer errors | Conditions that should never occur if the code is correct |
| Keep assertion messages descriptive | Include the actual value and the expected constraint |
| Don't put side effects in assertions | `assert items.remove(x)` breaks when assertions are disabled |
---
## Decision Guide: Which Conditional Refactoring to Use
| Situation | Refactoring |
|-----------|-------------|
| Long, complex condition expression | Decompose Conditional |
| Multiple conditions lead to same result | Consolidate Conditional Expression |
| Nested if/else with special cases | Replace Nested Conditional with Guard Clauses |
| Switch/if on type code in multiple places | Replace Conditional with Polymorphism |
| Null checks scattered everywhere | Introduce Special Case (Null Object) |
| Hidden assumption in code logic | Introduce Assertion |
| Condition appears once, set of types is small | Keep the conditional, but Decompose it |
| Condition varies at runtime | Use Strategy pattern |
@@ -1,347 +0,0 @@
# Code Smell Catalog
A comprehensive catalog of code smells organized by family. Each smell includes a description, detection heuristics, and the named refactorings that fix it.
## Table of Contents
1. [What Is a Code Smell?](#what-is-a-code-smell)
2. [Family 1: Bloaters](#family-1-bloaters)
3. [Family 2: Object-Orientation Abusers](#family-2-object-orientation-abusers)
4. [Family 3: Change Preventers](#family-3-change-preventers)
5. [Family 4: Dispensables](#family-4-dispensables)
6. [Family 5: Couplers](#family-5-couplers)
7. [Smell-to-Refactoring Quick Reference](#smell-to-refactoring-quick-reference)
---
## What Is a Code Smell?
A code smell is a surface indication that usually corresponds to a deeper structural problem. Smells are not bugs -- the code works correctly -- but they make the code harder to understand, extend, and maintain. The term was coined by Kent Beck and popularized by Martin Fowler.
**Key principles:**
- Smells are heuristics, not rules -- use judgment
- A smell that causes no real problem in context can be left alone
- Smells cluster: fixing one often reveals others nearby
- The "smell → refactoring" mapping is many-to-many; one smell may require several refactorings
---
## Family 1: Bloaters
Smells where code grows too large to work with effectively.
### Long Method
**Description:** A method that tries to do too much. The longer a method is, the harder it is to understand, test, and reuse.
**Detection heuristics:**
- More than 10-15 lines of executable code
- Multiple levels of indentation
- Comments separating "sections" within the method
- Multiple responsibilities visible in one scan
- Difficulty naming the method because it does several things
**Typical fixes:**
- Extract Method -- pull each logical section into a named method
- Replace Temp with Query -- eliminate temporaries that block extraction
- Replace Method with Method Object -- when extraction is blocked by tangled local variables
- Decompose Conditional -- when the length comes from complex branching
**Example smell:**
```
function processOrder(order) {
// validate order
if (!order.items || order.items.length === 0) { ... }
if (!order.customer) { ... }
// calculate totals
let subtotal = 0;
for (const item of order.items) { subtotal += item.price * item.qty; }
let tax = subtotal * 0.08;
let shipping = subtotal > 100 ? 0 : 9.99;
// apply discounts
if (order.customer.isPremium) { ... }
// save to database
db.save({ ...order, subtotal, tax, shipping });
// send confirmation
emailService.send(order.customer.email, ...);
}
```
Each comment block should be its own method: `validateOrder()`, `calculateTotals()`, `applyDiscounts()`, `saveOrder()`, `sendConfirmation()`.
### Large Class
**Description:** A class that has too many fields, too many methods, or too many responsibilities. Often called a "God Class" or "Blob."
**Detection heuristics:**
- More than 200-300 lines
- More than 10-15 fields
- Fields that cluster into subgroups (e.g., address fields, billing fields)
- Methods that only use a subset of the fields
- The class name is vague (`Manager`, `Handler`, `Processor`, `Utils`)
**Typical fixes:**
- Extract Class -- split along the axis of change
- Extract Subclass -- when behavior varies by type
- Replace Data Value with Object -- when field clusters represent a concept
### Long Parameter List
**Description:** A method that takes more than three or four parameters, making calls confusing and error-prone.
**Detection heuristics:**
- More than 3-4 parameters
- Boolean parameters that switch behavior
- Parameters that always travel together
- Callers passing `null` for unused parameters
**Typical fixes:**
- Introduce Parameter Object -- group related params into `DateRange`, `Address`, `Options`
- Preserve Whole Object -- pass the object instead of extracting its fields
- Replace Parameter with Method -- have the method fetch data it needs
### Data Clumps
**Description:** Groups of variables that appear together in multiple places -- method parameters, field declarations, or local variables.
**Detection heuristics:**
- Same three or more fields appear together in multiple classes
- Same group of parameters appears in multiple method signatures
- Deleting one member of the group would make no sense without the others
**Typical fixes:**
- Extract Class -- create a new class for the clump (`Address`, `DateRange`, `Coordinates`)
- Introduce Parameter Object -- replace parameter groups with the new class
- Preserve Whole Object -- pass the object instead of its decomposed fields
### Primitive Obsession
**Description:** Using primitive types (strings, ints, arrays) to represent domain concepts instead of small objects.
**Detection heuristics:**
- Constants or magic numbers used to represent types (`int ADMIN = 1`)
- Strings used for structured data (phone numbers, zip codes, currencies)
- Arrays or tuples used instead of named structures
- Validation logic for the same type scattered across multiple locations
**Typical fixes:**
- Replace Data Value with Object -- `String email` becomes `EmailAddress`
- Replace Type Code with Subclasses -- when the type code drives behavior
- Replace Type Code with Strategy -- when subclassing is impractical
- Replace Magic Number with Symbolic Constant -- names instead of numbers
---
## Family 2: Object-Orientation Abusers
Smells where object-oriented features are used incorrectly or not at all.
### Switch Statements
**Description:** The same switch/case or if/else chain on a type code appears in multiple places. When a new type is added, every switch must be updated.
**Detection heuristics:**
- `switch` on a type or status field that appears in more than one place
- `if/else if` chain checking `instanceof` or type strings
- Adding a new type requires editing multiple files
**Typical fixes:**
- Replace Conditional with Polymorphism -- each type implements its own behavior
- Replace Type Code with Subclasses + Replace Conditional with Polymorphism
- Replace Type Code with Strategy when the type can change at runtime
**Example:**
```
// SMELL: same switch in calculatePay(), generateReport(), getPermissions()
switch (employee.type) {
case 'engineer': return basePay;
case 'manager': return basePay + bonus;
case 'salesperson': return basePay + commission;
}
// FIX: polymorphism
class Engineer extends Employee {
calculatePay() { return this.basePay; }
}
class Manager extends Employee {
calculatePay() { return this.basePay + this.bonus; }
}
```
### Refused Bequest
**Description:** A subclass inherits methods or data it does not want. It overrides parent methods to do nothing or throws "not supported" exceptions.
**Detection heuristics:**
- Subclass overrides a method to do nothing or throw
- Subclass uses only a small fraction of inherited methods
- The "is-a" relationship feels forced
**Typical fixes:**
- Push Down Method / Push Down Field -- move unwanted members to the sibling that actually uses them
- Replace Inheritance with Delegation -- the child holds a reference to the parent instead of extending it
### Alternative Classes with Different Interfaces
**Description:** Two classes do essentially the same job but have different method names and signatures, preventing interchangeability.
**Detection heuristics:**
- Two classes with similar purpose but different method names
- Callers choose between them but can't treat them polymorphically
- Duplication of logic because no shared interface exists
**Typical fixes:**
- Rename Method -- align names across both classes
- Extract Superclass or Extract Interface -- define a shared contract
- Move Method -- equalize what each class offers
---
## Family 3: Change Preventers
Smells that make changes expensive by scattering related logic.
### Divergent Change
**Description:** One class changes for multiple unrelated reasons. It is the opposite of the Single Responsibility Principle.
**Detection heuristics:**
- You edit the same class for different kinds of changes (new database, new report format, new business rule)
- The class has methods that cluster into groups with no interaction between them
- Different team members edit the same file for different features
**Typical fixes:**
- Extract Class -- split the class along its axes of change
- Each resulting class should change for exactly one reason
### Shotgun Surgery
**Description:** A single logical change requires edits in many different classes. It is the opposite of Divergent Change.
**Detection heuristics:**
- A small functional change touches 5+ files
- A new field must be added to multiple classes
- A format change requires edits in scattered locations
**Typical fixes:**
- Move Method / Move Field -- consolidate related logic into one class
- Inline Class -- if scattered pieces are too small, merge them into the class that should own the responsibility
---
## Family 4: Dispensables
Smells where something exists but shouldn't.
### Lazy Class
**Description:** A class that does too little to justify its existence. Each class costs complexity; if it doesn't carry its weight, merge it.
**Typical fixes:** Inline Class, Collapse Hierarchy
### Dead Code
**Description:** Code that is never executed -- unreachable branches, unused variables, unneeded parameters, methods no one calls.
**Typical fixes:** Delete it. Version control remembers.
### Speculative Generality
**Description:** Abstractions, parameters, hooks, or classes created "in case we need them someday." YAGNI -- You Aren't Gonna Need It.
**Detection heuristics:**
- Abstract classes with only one subclass
- Parameters that are always passed the same value
- Methods that are only called by tests
- Framework infrastructure with no current use
**Typical fixes:**
- Collapse Hierarchy -- remove unneeded abstract class
- Remove Parameter -- delete unused params
- Inline Class / Inline Method -- collapse unneeded indirection
### Duplicate Code
**Description:** The same or nearly identical code structure appears in more than one place. The most common and most expensive smell.
**Detection heuristics:**
- Copy-pasted blocks with minor variations
- Methods in different classes that do the same thing
- Conditional branches with identical bodies
**Typical fixes:**
- Extract Method -- share the common code
- Pull Up Method -- move shared method to a common base class
- Extract Superclass / Extract Class -- when duplication spans classes
- Form Template Method -- when method structure is identical but details differ
---
## Family 5: Couplers
Smells where classes are too tightly bound to each other.
### Feature Envy
**Description:** A method that uses more features (fields and methods) of another class than its own. It "envies" the other class's data.
**Detection heuristics:**
- A method that calls 3+ getters on one foreign object
- A method that could be moved to the other class and would need fewer parameters
**Typical fixes:**
- Move Method -- relocate the method to the class it envies
- Extract Method + Move Method -- extract the envious part, then move it
### Inappropriate Intimacy
**Description:** Two classes are overly entangled -- accessing each other's private details, forming a bidirectional dependency.
**Typical fixes:**
- Move Method / Move Field to reduce the cross-boundary traffic
- Extract Class to put the shared concern in a neutral place
- Replace Inheritance with Delegation when subclass accesses too many parent internals
### Message Chains
**Description:** A client asks object A for B, then asks B for C, then asks C for D: `a.getB().getC().getD()`. The client is coupled to the entire navigation structure.
**Typical fixes:**
- Hide Delegate -- have A provide the answer directly
- Extract Method + Move Method -- push the chain into the object that should know the answer
### Middle Man
**Description:** A class whose methods do nothing but delegate to another class. It adds indirection without value.
**Detection heuristics:**
- More than half of a class's methods are one-line delegations
- The class has no logic of its own
**Typical fixes:**
- Remove Middle Man -- let the client call the delegate directly
- Inline Method -- merge the trivial forwarding methods into the caller
---
## Smell-to-Refactoring Quick Reference
| Smell | Primary Refactoring | Secondary Refactoring |
|-------|--------------------|-----------------------|
| Long Method | Extract Method | Replace Temp with Query |
| Large Class | Extract Class | Extract Subclass |
| Long Parameter List | Introduce Parameter Object | Preserve Whole Object |
| Data Clumps | Extract Class | Introduce Parameter Object |
| Primitive Obsession | Replace Data Value with Object | Replace Type Code with Subclasses |
| Switch Statements | Replace Conditional with Polymorphism | Replace Type Code with Strategy |
| Refused Bequest | Replace Inheritance with Delegation | Push Down Method |
| Divergent Change | Extract Class | -- |
| Shotgun Surgery | Move Method / Move Field | Inline Class |
| Lazy Class | Inline Class | Collapse Hierarchy |
| Dead Code | Delete it | -- |
| Speculative Generality | Collapse Hierarchy | Inline Class |
| Duplicate Code | Extract Method | Pull Up Method |
| Feature Envy | Move Method | Extract Method + Move Method |
| Inappropriate Intimacy | Move Method / Move Field | Extract Class |
| Message Chains | Hide Delegate | Extract Method |
| Middle Man | Remove Middle Man | Inline Method |
+11 -66
View File
@@ -12,12 +12,7 @@
openssl,
ffmpeg-headless,
rocmPackages,
config,
version,
# Overridable feature flags
rocmSupport ? config.rocmSupport or false,
rocmGpuTargets ? (lib.optionals rocmSupport rocmPackages.clr.gpuTargets),
}:
let
@@ -33,7 +28,9 @@ let
relativePath = lib.removePrefix "${root}/" pathString;
in
pathString == root
|| relativePath == "CMakeLists.txt"
|| builtins.elem relativePath [ "CMakeLists.txt" "LICENSE" "NOTICE" "THIRD_PARTY_NOTICES.md" ]
|| relativePath == "licenses"
|| lib.hasPrefix "licenses/" relativePath
|| relativePath == "cmake"
|| lib.hasPrefix "cmake/" relativePath
|| relativePath == "src"
@@ -44,16 +41,12 @@ let
|| relativePath == "tests/models/deepseek_v4_flash/fixtures"
|| relativePath == "tests/models/deepseek_v4_flash/fixtures/antirez-ds4.json"
|| relativePath == "tools"
|| relativePath == "tools/bench"
|| relativePath == "tools/bench/tune_hipblaslt.cpp"
|| relativePath == "tools/bench/benchmark_ssm_replay.cpp"
|| relativePath == "tools/bench/wmma_layout_test.hip"
|| relativePath == "tools/gufo"
|| relativePath == "tools/gufo/compile_h3_attention.py"
|| relativePath == "tools/gufo/h3_attention_kernel.py";
};
in
stdenv.mkDerivation (finalAttrs: {
stdenv.mkDerivation {
pname = "gufo";
inherit version;
src = productionSource;
@@ -62,8 +55,6 @@ stdenv.mkDerivation (finalAttrs: {
cmake
ninja
pkg-config
]
++ lib.optionals rocmSupport [
rocmPackages.clr
h3TritonCompiler
];
@@ -75,8 +66,6 @@ stdenv.mkDerivation (finalAttrs: {
libjpeg
openssl
ffmpeg-headless
]
++ lib.optionals rocmSupport [
rocmPackages.clr
rocmPackages.hipblas
rocmPackages.hipblaslt
@@ -87,7 +76,6 @@ stdenv.mkDerivation (finalAttrs: {
rocmPackages.rocblas
rocmPackages.composable_kernel
rocmPackages.aotriton
rocmPackages.rocprofiler-sdk
];
cmakeFlags = [
@@ -96,48 +84,8 @@ stdenv.mkDerivation (finalAttrs: {
"-DGUFO_VERSION=${version}"
"-DGUFO_FFMPEG_EXECUTABLE=${ffmpeg-headless}/bin/ffmpeg"
"-DGUFO_FFPROBE_EXECUTABLE=${ffmpeg-headless}/bin/ffprobe"
]
++ lib.optional rocmSupport "-DENGINE_ENABLE_HIP=ON"
++ lib.optional rocmSupport "-DCMAKE_HIP_COMPILER=${rocmPackages.llvm.clang}/bin/clang"
++ lib.optional rocmSupport "-DGPU_TARGETS=${lib.concatStringsSep ";" rocmGpuTargets}"
++ lib.optional rocmSupport "-DHIPCUB_INCLUDE_DIR=${rocmPackages.hipcub}/include"
++ lib.optional rocmSupport "-DROCPRIM_INCLUDE_DIR=${rocmPackages.rocprim}/include"
++ lib.optional rocmSupport "-DROCWMMA_INCLUDE_DIR=${rocmPackages.rocwmma}/include";
env = lib.optionalAttrs rocmSupport {
ROCM_PATH = "${rocmPackages.clr}";
GUFO_HIPCUB_ROOT = "${rocmPackages.hipcub}";
GUFO_ROCPRIM_ROOT = "${rocmPackages.rocprim}";
GUFO_ROCWMMA_ROOT = "${rocmPackages.rocwmma}";
};
installPhase = ''
runHook preInstall
mkdir -p $out/bin
cp gufo $out/bin/gufo
ln -sf gufo $out/bin/gufo-server
mkdir -p $out/share/gufo/models/qwen3_tts
cp $src/src/models/qwen3_tts/reference/run_official.py \
$out/share/gufo/models/qwen3_tts/
mkdir -p $out/share/gufo/models/minimax_h3
cp $src/src/models/minimax_h3/MINIMAX_H3_FL2VA_BF16.source-manifest.json \
$out/share/gufo/models/minimax_h3/
mkdir -p $out/share/gufo/eval
cp $src/tests/models/deepseek_v4_flash/fixtures/antirez-ds4.json $out/share/gufo/eval/
if [ -f gufo-kernel-bench ]; then
cp gufo-kernel-bench $out/bin/gufo-kernel-bench
fi
if [ -f tune_hipblaslt ]; then
cp tune_hipblaslt $out/bin/tune_hipblaslt
fi
if [ -f benchmark_ssm_replay ]; then
cp benchmark_ssm_replay $out/bin/benchmark_ssm_replay
fi
chmod +x $out/bin/*
runHook postInstall
'';
"-DCMAKE_HIP_COMPILER=${rocmPackages.llvm.clang}/bin/clang"
];
doInstallCheck = true;
installCheckPhase = ''
@@ -147,12 +95,9 @@ stdenv.mkDerivation (finalAttrs: {
$out/bin/gufo --help >/dev/null
$out/bin/gufo-server --version
$out/bin/gufo-server --help >/dev/null
if [ -x $out/bin/gufo-kernel-bench ]; then
$out/bin/gufo-kernel-bench --help >/dev/null
fi
if [ -x $out/bin/tune_hipblaslt ]; then
$out/bin/tune_hipblaslt --help >/dev/null
fi
test ! -e $out/bin/gufo-kernel-bench
test -f $out/share/licenses/gufo/LICENSE
test -f $out/share/licenses/gufo/third-party/LICENSE.ds4
runHook postInstallCheck
'';
@@ -170,9 +115,9 @@ stdenv.mkDerivation (finalAttrs: {
meta = with lib; {
description = "Gufo Engine — local inference runtime for AMD Strix Halo (gfx1151 GPU)";
homepage = "https://github.com/";
homepage = "https://github.com/gufo-org/gufo";
license = licenses.mit;
platforms = [ "x86_64-linux" ];
mainProgram = "gufo";
};
})
}
+8 -3
View File
@@ -6,10 +6,15 @@ on:
pull_request:
branches: [ main ]
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build-and-test:
name: Build & Quality Gates
name: CPU contracts & repository checks
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
@@ -47,7 +52,7 @@ jobs:
use-flakehub: false
use-gha-cache: true
- name: Run Canonical PR Test Suite
- name: Run hosted PR checks
run: |
nix build .#checks.x86_64-linux.pr --print-build-logs
nix build .#checks.x86_64-linux.pr --print-build-logs --cores 2 --max-jobs 2
cat ./result/pr-summary.txt
+1
View File
@@ -87,6 +87,7 @@ test-results/
*.gguf
*.ckpt
pytorch_model*.bin
/.venv/
__pycache__/
*.pyc
+46 -35
View File
@@ -1,48 +1,59 @@
# AGENTS.md
## Supported Platform
## Platform and build
Linux x86-64 on AMD Strix Halo (`gfx1151` GPU) is the only
supported production target.
## Build & Test (Nix)
Build and test with Nix only. Direct host builds and Makefiles are unsupported.
Production targets Linux x86-64 AMD Strix Halo (`gfx1151`) only. CMake owns
compiler flags, dependencies and installation; Nix supplies the pinned toolchain.
Linux source-build prerequisites are in [README.md](README.md#build-from-source).
```sh
nix build # build default package (gfx1151)
./result/bin/gufo diagnose # run hardware probe & diagnostics
./result/bin/gufo serve # run server
nix build .#checks.x86_64-linux.pr # canonical PR test command (all gates)
nix develop # dev shell
nix build # production package, no tests/tools
./result/bin/gufo diagnose
nix build .#checks.x86_64-linux.pr # bounded hosted CPU/repository checks
nix develop # GPU development and reference tools
# Same production build without Nix, with the documented dependencies installed
cmake --preset release
cmake --build --preset release --parallel 4
```
- `git add` before `nix build` — Nix sees only tracked files.
- Use binaries under `build/gpu-test` for focused correctness and debugging
only; GPU test presets use `RelWithDebInfo`, matching the release build type,
with test assertions enabled. Measure performance with binaries produced
by `nix build` under
`result/bin`. Example:
`./result/bin/gufo bench --model <model.gguf> -p 128 -n 16 --validate-prefill 128`.
Stage only task-owned paths before Nix builds; flakes include tracked files.
Measure performance with `result/bin/gufo` or `build/release/gufo`. Preserve
compiler/dependency versions when comparing results.
## Profiling and kernel work
## Focused tests
Performance tooling lives in `tools/` and is documented in
[docs/PERFORMANCE.md](docs/PERFORMANCE.md) under "Commands":
Run the smallest check covering the change. `gpu-test` is RelWithDebInfo with
assertions enabled. Build only the affected target during iteration:
- `tools/bench/build.sh` builds the standalone microbenchmarks in `tools/bench/`
with `hipcc` (seconds, not a full `nix build`); each carries an ablation
harness that reports correctness next to throughput.
- `tools/bench/gfx1151_peak.hip` measures the roofline ceilings every kernel is
scored against. Do not use spec-sheet numbers.
- `tools/prof/prof.py` wraps `rocprofv3` with a pipeline-stage rollup, GPU-busy versus
wall-span, and an A/B `diff` mode.
- `tools/prof/isa_mix.py` summarizes one kernel's instruction mix from an assembly
listing.
```sh
nix develop -c cmake --preset gpu-test
nix develop -c cmake --build --preset gpu-test --target <test-target>
nix develop -c ctest --preset gpu-full -R '^<test-name>$' --output-on-failure
```
The same CMake/CTest commands work outside Nix. `cmake --build --preset pr`
runs the hosted contract suite after configuring `cpu-test`. Full CPU checks,
sanitisers, GPU/operator/model checks and H3 quality tools remain available
locally; see [docs/TESTING.md](docs/TESTING.md). Do not run full model sweeps,
video generation or duplicate suites for routine edits. A missing-model skip
is not a quality pass. Broaden checks when shared behavior changes or failures
expose risk.
## Profiling and kernels
Apply [.agents/skills/optimize-kernel/SKILL.md](.agents/skills/optimize-kernel/SKILL.md).
Use `tools/bench/build.sh` for standalone HIP experiments,
`tools/bench/gfx1151_peak.hip` for measured hardware ceilings,
`tools/prof/prof.py` for pipeline/wall-time profiles, and
`tools/prof/isa_mix.py` for instruction analysis. Production paths must retain
quality; successful optimizations become the default, without extra switches.
## Development
- Use one canonical long option and backend name for each behavior. Avoid extra aliases.
- Use `gh` CLI to retrieve and update issues content. Verify if installed and configured with `gh auth status`, and use it unless the user explicitly says otherwise.
- Follow Conventional Commits format with single-line commit messages (e.g., `feat(scope): summary (#issue)`, `fix(scope): summary (#issue)`).
- Prefer Jujutsu (`jj`) over Git when possible. Verify that it is available by running `jj version`; fall back to Git if it is unavailable or if the user explicitly requires Git.
- Keep model code, tests, tools and numerical contracts with their model.
- Use one canonical long option and backend name per behavior; avoid aliases.
- Use `gh` for GitHub operations after checking `gh auth status`.
- Follow Conventional Commits with a single-line message.
- Prefer `jj` when available (`jj version`); otherwise use Git.
- Follow the user's remote workflow and preserve unrelated work.
+45 -56
View File
@@ -1,5 +1,14 @@
cmake_minimum_required(VERSION 3.21)
project(gufo LANGUAGES C CXX)
if (NOT CMAKE_SYSTEM_NAME STREQUAL "Linux" OR NOT CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64)$")
message(FATAL_ERROR "Gufo requires Linux x86-64 with an AMD gfx1151 GPU")
endif()
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Build type" FORCE)
endif()
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
option(BUILD_TESTING "Build correctness tests" OFF)
option(GUFO_BUILD_TOOLS "Build development benchmarks and tuning tools" OFF)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS ON)
@@ -19,27 +28,15 @@ set(GUFO_FFMPEG_EXECUTABLE "ffmpeg" CACHE STRING
set(GUFO_FFPROBE_EXECUTABLE "ffprobe" CACHE STRING
"FFprobe executable used to validate generated media")
# ROCm/HIP GPU backend — compiled natively for the Strix Halo gfx1151 target.
option(ENGINE_ENABLE_HIP "Build gufo with ROCm/HIP GPU backend" OFF)
# ROCm/HIP GPU discovery — compiled natively for the Strix Halo gfx1151 target.
# Dependencies are supplied by the Nix environment via $ROCM_PATH.
# Host ambient /usr fallbacks are rejected.
if (NOT DEFINED ENV{ROCM_PATH})
if (EXISTS /opt/rocm)
set(ENV{ROCM_PATH} /opt/rocm)
endif()
# CPU-only builds exist for host checks; production targets gfx1151 only.
option(ENGINE_ENABLE_HIP "Build the gfx1151 HIP backend" ON)
set(CMAKE_HIP_ARCHITECTURES gfx1151 CACHE STRING "HIP architecture")
if (ENGINE_ENABLE_HIP AND NOT CMAKE_HIP_ARCHITECTURES STREQUAL "gfx1151")
message(FATAL_ERROR "Gufo kernels require CMAKE_HIP_ARCHITECTURES=gfx1151")
endif()
if (DEFINED ENV{ROCM_PATH})
list(APPEND CMAKE_PREFIX_PATH $ENV{ROCM_PATH})
list(APPEND CMAKE_PREFIX_PATH "$ENV{ROCM_PATH}/lib64/cmake")
endif()
# Forward GPU_TARGETS (set by the nix package) to CMAKE_HIP_ARCHITECTURES.
# e.g. -DGPU_TARGETS=gfx1151
if (GPU_TARGETS AND NOT CMAKE_HIP_ARCHITECTURES)
set(CMAKE_HIP_ARCHITECTURES "${GPU_TARGETS}" CACHE STRING "HIP architectures")
# Standard ROCm installations; custom prefixes use CMAKE_PREFIX_PATH.
if (EXISTS /opt/rocm)
list(APPEND CMAKE_PREFIX_PATH /opt/rocm)
endif()
if (ENGINE_ENABLE_HIP)
@@ -51,21 +48,16 @@ if (ENGINE_ENABLE_HIP)
find_package(rocblas REQUIRED)
find_package(miopen REQUIRED)
find_package(aotriton CONFIG REQUIRED)
find_path(HIPCUB_INCLUDE_DIR hipcub/hipcub.hpp
HINTS "$ENV{GUFO_HIPCUB_ROOT}/include"
REQUIRED)
find_path(ROCPRIM_INCLUDE_DIR rocprim/rocprim.hpp
HINTS "$ENV{GUFO_ROCPRIM_ROOT}/include"
REQUIRED)
find_path(ROCWMMA_INCLUDE_DIR rocwmma/rocwmma.hpp
HINTS "$ENV{GUFO_ROCWMMA_ROOT}/include"
REQUIRED)
find_path(ROCTX_INCLUDE_DIR rocprofiler-sdk-roctx/roctx.h REQUIRED)
find_library(ROCTX_LIBRARY NAMES rocprofiler-sdk-roctx REQUIRED)
find_path(HIPCUB_INCLUDE_DIR hipcub/hipcub.hpp REQUIRED)
find_path(ROCPRIM_INCLUDE_DIR rocprim/rocprim.hpp REQUIRED)
find_path(ROCWMMA_INCLUDE_DIR rocwmma/rocwmma.hpp REQUIRED)
find_path(COMPOSABLE_KERNEL_INCLUDE_DIR ck/ck.hpp REQUIRED)
message(STATUS "gufo: ROCm/HIP enabled, architectures=${CMAKE_HIP_ARCHITECTURES}")
message(STATUS "gufo: ROCM_PATH=$ENV{ROCM_PATH}")
execute_process(COMMAND "${Python3_EXECUTABLE}" -c "import triton"
RESULT_VARIABLE GUFO_TRITON_IMPORT OUTPUT_QUIET ERROR_QUIET)
if (NOT GUFO_TRITON_IMPORT EQUAL 0)
message(FATAL_ERROR "The build needs Python Triton to compile H3 attention; see README.md")
endif()
message(STATUS "gufo: HIP target ${CMAKE_HIP_ARCHITECTURES}")
set(GUFO_H3_ATTENTION_GENERATED_DIR
"${CMAKE_CURRENT_BINARY_DIR}/generated")
@@ -254,9 +246,7 @@ target_compile_definitions(gufo PRIVATE
GUFO_EVAL_SOURCE_DATA="${CMAKE_CURRENT_SOURCE_DIR}/tests/models/deepseek_v4_flash/fixtures/antirez-ds4.json"
GUFO_EVAL_INSTALLED_DATA="${CMAKE_INSTALL_PREFIX}/share/gufo/eval/antirez-ds4.json"
GUFO_H3_SOURCE_MANIFEST="${CMAKE_CURRENT_SOURCE_DIR}/src/models/minimax_h3/MINIMAX_H3_FL2VA_BF16.source-manifest.json"
GUFO_H3_INSTALLED_SOURCE_MANIFEST="${CMAKE_INSTALL_PREFIX}/share/gufo/models/minimax_h3/MINIMAX_H3_FL2VA_BF16.source-manifest.json"
GUFO_QWEN3_TTS_REFERENCE_RUNNER="${CMAKE_CURRENT_SOURCE_DIR}/src/models/qwen3_tts/reference/run_official.py"
GUFO_QWEN3_TTS_INSTALLED_REFERENCE_RUNNER="${CMAKE_INSTALL_PREFIX}/share/gufo/models/qwen3_tts/run_official.py")
GUFO_H3_INSTALLED_SOURCE_MANIFEST="${CMAKE_INSTALL_PREFIX}/share/gufo/models/minimax_h3/MINIMAX_H3_FL2VA_BF16.source-manifest.json")
if (ENGINE_ENABLE_HIP)
target_link_libraries(gufo PRIVATE gufo_deepseek_v4_flash)
@@ -267,12 +257,15 @@ add_custom_target(gufo-server ALL
COMMAND ${CMAKE_COMMAND} -E create_symlink $<TARGET_FILE_NAME:gufo> ${CMAKE_CURRENT_BINARY_DIR}/gufo-server
DEPENDS gufo
)
add_library(gufo_kernel_bench_support STATIC
src/cli/bench/kernel_bench.cpp
)
target_include_directories(gufo_kernel_bench_support PUBLIC ${CMAKE_SOURCE_DIR})
if (BUILD_TESTING OR GUFO_BUILD_TOOLS)
add_library(gufo_kernel_bench_support STATIC
src/cli/bench/kernel_bench.cpp)
target_include_directories(gufo_kernel_bench_support PUBLIC ${CMAKE_SOURCE_DIR})
endif()
if (ENGINE_ENABLE_HIP)
if (ENGINE_ENABLE_HIP AND GUFO_BUILD_TOOLS)
find_path(ROCTX_INCLUDE_DIR rocprofiler-sdk-roctx/roctx.h REQUIRED)
find_library(ROCTX_LIBRARY NAMES rocprofiler-sdk-roctx REQUIRED)
add_executable(gufo-kernel-bench
src/cli/bench/kernel_bench_main.cpp
)
@@ -289,28 +282,22 @@ if (ENGINE_ENABLE_HIP)
add_executable(tune_hipblaslt tools/bench/tune_hipblaslt.cpp)
target_link_libraries(tune_hipblaslt PRIVATE gufo_core hip::host)
option(GUFO_BUILD_DIAG_TOOLS "Build standalone diagnostic tools" OFF)
if(GUFO_BUILD_DIAG_TOOLS)
add_executable(wmma_layout_test tools/bench/wmma_layout_test.hip)
target_link_libraries(wmma_layout_test PRIVATE gufo_core hip::device)
endif()
add_executable(wmma_layout_test tools/bench/wmma_layout_test.hip)
target_link_libraries(wmma_layout_test PRIVATE gufo_core hip::device)
endif()
install(TARGETS gufo DESTINATION bin)
install(FILES
src/models/minimax_h3/MINIMAX_H3_FL2VA_BF16.source-manifest.json
DESTINATION share/gufo/models/minimax_h3)
install(FILES
src/models/qwen3_tts/reference/run_official.py
DESTINATION share/gufo/models/qwen3_tts)
install(FILES
src/models/qwen3_asr/reference/run_official.py
src/models/qwen3_asr/tools/benchmark.py
DESTINATION share/gufo/models/qwen3_asr)
install(FILES
tests/models/deepseek_v4_flash/fixtures/antirez-ds4.json
DESTINATION share/gufo/eval)
if (ENGINE_ENABLE_HIP)
install(FILES LICENSE NOTICE THIRD_PARTY_NOTICES.md DESTINATION share/licenses/gufo)
install(DIRECTORY licenses/ DESTINATION share/licenses/gufo/third-party)
install(FILES src/models/deepseek_v4_flash/LICENSE.ds4 DESTINATION share/licenses/gufo/third-party)
install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink gufo \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/gufo-server\")")
if (ENGINE_ENABLE_HIP AND GUFO_BUILD_TOOLS)
install(TARGETS gufo-kernel-bench tune_hipblaslt
DESTINATION bin)
endif()
@@ -935,4 +922,6 @@ if (BUILD_TESTING)
endif()
endif()
# Formatting and static analysis are owned by the canonical Nix PR checks.
if (BUILD_TESTING)
include(cmake/Checks.cmake)
endif()
+12 -29
View File
@@ -16,26 +16,15 @@
"GUFO_ENABLE_WARNINGS": "ON"
}
},
{
"name": "development",
"displayName": "Development",
"description": "CPU-only development build with debug information and warnings enabled",
"inherits": "base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"BUILD_TESTING": "ON",
"ENGINE_ENABLE_HIP": "OFF"
}
},
{
"name": "release",
"displayName": "Release",
"description": "CPU-only optimized release build",
"description": "Production gfx1151 build; same optimization as the Nix package",
"inherits": "base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_BUILD_TYPE": "RelWithDebInfo",
"BUILD_TESTING": "OFF",
"ENGINE_ENABLE_HIP": "OFF"
"ENGINE_ENABLE_HIP": "ON"
}
},
{
@@ -56,7 +45,7 @@
"description": "CPU-only build for running unit and smoke tests with CTest",
"inherits": "base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_BUILD_TYPE": "RelWithDebInfo",
"BUILD_TESTING": "ON",
"ENGINE_ENABLE_HIP": "OFF"
}
@@ -70,16 +59,11 @@
"CMAKE_BUILD_TYPE": "RelWithDebInfo",
"BUILD_TESTING": "ON",
"ENGINE_ENABLE_HIP": "ON",
"GPU_TARGETS": "gfx1151"
"GUFO_BUILD_TOOLS": "ON"
}
}
],
"buildPresets": [
{
"name": "development",
"displayName": "Development",
"configurePreset": "development"
},
{
"name": "release",
"displayName": "Release",
@@ -99,17 +83,16 @@
"name": "gpu-test",
"displayName": "gfx1151 GPU Tests",
"configurePreset": "gpu-test"
},
{
"name": "pr",
"configurePreset": "cpu-test",
"targets": [
"check-pr"
]
}
],
"testPresets": [
{
"name": "development",
"displayName": "Development Tests",
"configurePreset": "development",
"output": {
"outputOnFailure": true
}
},
{
"name": "cpu-sanitizer",
"displayName": "CPU ASan and UBSan Tests",
+23 -43
View File
@@ -1,48 +1,28 @@
gufo
Copyright (c) 2026 gufo contributors
Gufo
Copyright (c) 2026 Gufo contributors
This product includes software developed by the gufo project and
third-party open source projects.
Original Gufo code is licensed under the MIT License in LICENSE.
Third-party code retains its original copyright and license terms.
See THIRD_PARTY_NOTICES.md for provenance, versions and distribution boundaries;
full imported notices are installed alongside it in third-party/.
The original gufo source code is licensed under the MIT License.
See the LICENSE file in the root of this distribution for the full license text.
Adapted code includes:
- llama.cpp / ggml: Copyright (c) 2023-2026 The ggml authors (MIT).
- DS4: Copyright (c) 2026 The ds4.c authors (MIT).
- DS4 GB10/GX10 changes: Copyright (c) 2026 Marco Palaferri (MIT).
- Qwen reference chat templates: Alibaba Cloud / Qwen (Apache-2.0); Gufo
implements a separate bounded renderer with local validation and API wiring.
- h3.c: Copyright (c) 2026 Salvatore Sanfilippo (MIT).
- ccv ancestry through h3.c: Copyright (c) 2010 Liu Liu (BSD-3-Clause).
- ROCm kernels/headers: AMD and the contributors identified in their notices.
- hipCUB: Duane Merrill, NVIDIA CORPORATION and AMD (BSD-3-Clause).
For detailed third-party dependency provenance, component breakdown, SPDX identifiers,
licenses, and corresponding source locations, see THIRD_PARTY_NOTICES.md.
This software is based in part on the work of the Independent JPEG Group.
================================================================================
THIRD-PARTY NOTICES
================================================================================
Gufo invokes FFmpeg/ffprobe as separate executables. Their license and source
obligations, and those of any other bundled dependency, remain applicable to
redistributed packages. Gufo's MIT license does not replace those obligations.
--------------------------------------------------------------------------------
1. FFmpeg - Separate Runtime Executable
--------------------------------------------------------------------------------
Version: 8.1.2 from pinned Nixpkgs
Upstream Repository: https://github.com/FFmpeg/FFmpeg
MiniMax H3 media output invokes the headless ffmpeg and ffprobe executables as
separate processes. No FFmpeg library is linked into gufo. The
pinned Nix closure supplies FFmpeg's LGPL/GPL license texts and corresponding
source metadata. See THIRD_PARTY_NOTICES.md for the exact relationship and
enabled-license inventory.
--------------------------------------------------------------------------------
2. curl / libcurl - OpenAI-Compatible Evaluation Client
--------------------------------------------------------------------------------
Version: 8.21.0 from pinned Nixpkgs
Upstream Repository: https://github.com/curl/curl
The gufo eval command links libcurl for HTTP and HTTPS model discovery and
chat-completion requests. The pinned Nix closure supplies curl's license text
and corresponding-source metadata. See THIRD_PARTY_NOTICES.md for the exact
relationship.
--------------------------------------------------------------------------------
3. Evaluation-Only Python Quality Toolchain
--------------------------------------------------------------------------------
Pinned development environments include PyTorch ROCm 2.12.0, Torchvision
0.27.0, and LPIPS 0.1.4 for independent teacher execution and perceptual
quality evaluation. These BSD-licensed packages are not linked into or
distributed with the production Gufo binaries. Their upstream license texts
and source metadata are supplied by the pinned Nix packages; exact
relationships are recorded in THIRD_PARTY_NOTICES.md.
Model weights are not included. Operators acquire them separately under the
publisher's terms. Python reference/evaluation tools and their models belong
to the development environment, not the production Gufo installation.
+67 -12
View File
@@ -70,29 +70,84 @@ curl http://localhost:8080/v1/chat/completions \
The server also exposes `/v1/completions`, `/v1/responses`, `/v1/models`, and
`/health`. Any OpenAI-compatible client can point at `http://localhost:8080`.
## Supported Platform
## Build from source
Linux x86-64 on AMD Strix Halo (`gfx1151` GPU) is the only
planned production platform. Windows, macOS, and CUDA are out of scope.
Linux x86-64 on AMD Strix Halo (`gfx1151`) is the supported target. CMake owns
one production configuration for both Nix and ordinary Linux builds. Tests,
profilers, tuning executables and Python reference runners are not installed
with the production package. No `build.sh` wrapper is needed.
Build with Nix only; direct host builds are unsupported:
### With Nix
```sh
nix build # build default package (gfx1151)
./result/bin/gufo diagnose # run hardware probe & diagnostics
./result/bin/gufo serve # run server
nix build .#checks.x86_64-linux.pr # canonical PR test command (all gates)
nix build
./result/bin/gufo diagnose
./result/bin/gufo serve llm --model /path/to/model.gguf
```
For non-Nix users, [toolboxes](https://github.com/gufo-org/toolboxes) for Docker and Podman are available:
[flake.lock](flake.lock) pins the dependencies. `nix develop` adds profiling,
model-download and independent evaluation tools; these are not runtime
requirements. See [testing](docs/TESTING.md) for the small hosted CI suite and
explicit local quality checks.
### Without Nix
Install a C++20 compiler, CMake 3.21+, Ninja, pkg-config, Python 3.10+ and the
following development libraries. The currently qualified toolchain is GCC
15.3, ROCm 7.2.3, AOTriton 0.11.1b and Triton 3.7.0.
| Dependency | Used for |
| --- | --- |
| ROCm HIP compiler/runtime, hipBLAS, hipBLASLt, rocBLAS | GPU execution and matrix multiplication |
| hipCUB, rocPRIM, rocWMMA, Composable Kernel headers | Compiled GPU kernels |
| MIOpen | ASR audio encoder convolutions |
| AOTriton, including **gfx1151 kernel images** | H3 attention |
| ICU, libcurl, OpenSSL, libpng, libjpeg | Tokenization, HTTPS, hashing and images |
| FFmpeg and ffprobe | Video/audio output; invoked as separate executables |
| Python Triton 3.7.0 | Build-time H3 kernel compilation; not needed to run Gufo |
Install ROCm using [AMD's Linux instructions](https://rocm.docs.amd.com/projects/install-on-linux/en/latest/).
Use the development packages for the libraries above. AOTriton's Python package
alone is insufficient: CMake needs its headers, `aotriton-config.cmake`, shared
library and gfx1151 images. If building it from
[the upstream source](https://github.com/ROCm/aotriton/tree/0.11.1b), select
`-DAOTRITON_TARGET_ARCH=gfx1151 -DAOTRITON_USE_TORCH=OFF` and follow its install
instructions. ROCm normally installs under `/opt/rocm`.
For example, on Debian/Ubuntu the ordinary system libraries are:
```sh
podman pull ghcr.io/gufo-org/toolboxes/gufo-runtime:latest
sudo apt install build-essential cmake ninja-build pkg-config python3-venv \
libicu-dev libcurl4-openssl-dev libssl-dev libpng-dev libjpeg-dev ffmpeg
python3 -m venv .venv
. .venv/bin/activate
python -m pip install triton==3.7.0
cmake --preset release -DCMAKE_INSTALL_PREFIX="$HOME/.local"
cmake --build --preset release --parallel 4
./build/release/gufo diagnose
./build/release/gufo serve llm --model /path/to/model.gguf
```
### Why Nix?
Install the ROCm/AOTriton dependencies from the table before configuring.
For nonstandard installations, pass ordinary CMake paths, for example
`cmake --preset release -DCMAKE_PREFIX_PATH="/opt/rocm;/opt/aotriton"`.
If compiler discovery picks a system Clang, also pass
`-DCMAKE_HIP_COMPILER=/opt/rocm/llvm/bin/clang++`.
Use `cmake --install build/release` to install Gufo,
its runtime data and license notices. The GPU driver must allow your user to
access `/dev/kfd` and `/dev/dri`; model weights are acquired separately.
Nix provides a reproducible environment by having all the dependencies in a single file, the [flake.nix](./flake.nix). Our primary goal is to provide an engine that performs better on the Strix Halo without sacrificing accuracy. Nix helps us develop this project by removing all those variables that may make experiments not reproducible across several machines (different driver versions, different environment variables, etc.).
The same source, compiler flags and install rules serve both builds. Nix pins
the complete toolchain for reproducible comparisons; changing the compiler or
math libraries requires the affected model's quality checks.
## License
Gufo's original code is [MIT licensed](LICENSE). Adapted code and dependencies
retain their own notices in [NOTICE](NOTICE), [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)
and `licenses/`, installed under `share/licenses/gufo`. Model weights are not
bundled and retain their publishers' terms.
## Reference Projects
+74 -261
View File
@@ -1,211 +1,90 @@
# Third-Party Notices and Inventory
# Third-party notices
This document records the complete inventory of third-party software, libraries, drivers,
and system components used, linked, or required by **gufo**.
Gufo's original source is MIT licensed; this does not relicense dependencies or
model weights. This inventory covers direct dependencies and adapted code.
Transitive system/toolchain dependencies retain their upstream licenses.
`flake.lock` pins Nixpkgs revision `2fcb964de67fcf60b43471c55d5d99e61a9ccb5a`;
its package expressions record source revisions, patches and build options.
Non-Nix builds must retain the notices of the versions they actually distribute.
The engine license is in [LICENSE](LICENSE); model-specific acquisition and
usage records are linked from [the model guide](docs/models/README.md).
---
## Inventory Summary
## Runtime and compiled code
| Component Name | Relationship | License (SPDX) | Pinned Revision / Version | Upstream Source / Location |
| --- | --- | --- | --- | --- |
| **ROCm / HIP** | Linked / Toolchain | `MIT OR Apache-2.0 WITH LLVM-exception` | Nixpkgs `2fcb964de67fcf60b43471c55d5d99e61a9ccb5a` | [ROCm/clr](https://github.com/ROCm/clr) |
| **hipBLAS** | Linked | `MIT` | Nixpkgs `2fcb964de67fcf60b43471c55d5d99e61a9ccb5a` | [ROCm/hipBLAS](https://github.com/ROCm/hipBLAS) |
| **hipBLASLt** | Linked | `MIT` | Nixpkgs `2fcb964de67fcf60b43471c55d5d99e61a9ccb5a` | [ROCm/hipBLASLt](https://github.com/ROCm/hipBLASLt) |
| **rocBLAS** | Linked | `MIT` | Nixpkgs `2fcb964de67fcf60b43471c55d5d99e61a9ccb5a` | [ROCm/rocBLAS](https://github.com/ROCm/rocBLAS) |
| **Composable Kernel** | Header / compiled kernels | `MIT` | Nixpkgs `2fcb964de67fcf60b43471c55d5d99e61a9ccb5a` | [ROCm/composable_kernel](https://github.com/ROCm/composable_kernel) |
| **ROCprofiler SDK / ROCTx** | Benchmark marker library / profiling tool | `MIT` | Nixpkgs `2fcb964de67fcf60b43471c55d5d99e61a9ccb5a` | [ROCm/rocprofiler-sdk](https://github.com/ROCm/rocprofiler-sdk) |
| **DS4** | Vendored model engine and ROCm kernels | `MIT` | `84cc882352757baf628a1776badf7cc54d584e28` | [antirez/ds4](https://github.com/antirez/ds4) |
| **h3.c** | Pinned implementation reference; selected code may be adapted model-privately | `MIT` | `8974cc055ea9c02fcd14cc27dfda3e1027c05153` | [antirez/h3.c](https://github.com/antirez/h3.c) |
| **ccv TensorOps matmul ancestry** | Algorithm/source ancestry identified by h3.c | `BSD-3-Clause` | Notice pinned through h3.c commit `8974cc055ea9c02fcd14cc27dfda3e1027c05153` | [libccv/ccv](https://github.com/liuliu/ccv) |
| **llama.cpp** | Source-derived algorithm | `MIT` | `e9fa0781f1c25fc4fe8c86be1edc6970661ad6f0` | [ggml-org/llama.cpp](https://github.com/ggml-org/llama.cpp) |
| **ICU** | Linked | `Unicode-3.0` | Nixpkgs `2fcb964de67fcf60b43471c55d5d99e61a9ccb5a` | [unicode-org/icu](https://github.com/unicode-org/icu) |
| **curl / libcurl** | Linked HTTP client | `curl` | `8.21.0`, Nixpkgs `2fcb964de67fcf60b43471c55d5d99e61a9ccb5a` | [curl/curl](https://github.com/curl/curl) |
| **FFmpeg** | Spawned runtime executable | `LGPL-2.1-or-later AND GPL-2.0-or-later` (enabled components may also be `LGPL-3.0-or-later` / `GPL-3.0-or-later`) | `8.1.2`, Nixpkgs `2fcb964de67fcf60b43471c55d5d99e61a9ccb5a` | [FFmpeg/FFmpeg](https://github.com/FFmpeg/FFmpeg) |
| **PyTorch ROCm** | Evaluation/offline-teacher tool only; not shipped | `BSD-3-Clause` | `2.12.0`, Nixpkgs `2fcb964de67fcf60b43471c55d5d99e61a9ccb5a` | [pytorch/pytorch](https://github.com/pytorch/pytorch) |
| **Torchvision** | Evaluation/LPIPS tool only; not shipped | `BSD-3-Clause` | `0.27.0`, Nixpkgs `2fcb964de67fcf60b43471c55d5d99e61a9ccb5a` | [pytorch/vision](https://github.com/pytorch/vision) |
| **LPIPS** | Perceptual-quality evaluation tool only; not shipped | `BSD-2-Clause` | `0.1.4`, Nixpkgs `2fcb964de67fcf60b43471c55d5d99e61a9ccb5a` | [richzhang/PerceptualSimilarity](https://github.com/richzhang/PerceptualSimilarity) |
| **Torchvision AlexNet weights** | Evaluation model data only; not shipped in the production package | `NOASSERTION` | SHA-256 `7be5be791159472b1fbf3c69796f7cb30dca7ad8466c2df70058c37116cdee02` | [PyTorch model distribution](https://download.pytorch.org/models/alexnet-owt-7be5be79.pth) |
| **MiniMax H3 FL2VA checkpoint** | External operator-supplied model; not distributed | `LicenseRef-MiniMax-H3-Community-2026-08-02` or operator-specific authorization | `42ed227ee7df40d41602854ae760620d6eb651fe` | [MiniMaxAI/MiniMax-H3](https://huggingface.co/MiniMaxAI/MiniMax-H3) |
| **Qwen3-VL-32B encoder weights used by H3** | External operator-supplied model component; not distributed | `Apache-2.0` | Included by the pinned H3 FL2VA checkpoint | [QwenLM/Qwen3-VL](https://github.com/QwenLM/Qwen3-VL) |
| ROCm / HIP | Linked runtime | `MIT` | ROCm 7.2.3; flake.lock | [ROCm/clr](https://github.com/ROCm/clr) |
| hipBLAS | Linked | `MIT` | ROCm 7.2.3; flake.lock | [ROCm/hipBLAS](https://github.com/ROCm/hipBLAS) |
| hipBLASLt | Linked | `MIT` | ROCm 7.2.3; flake.lock | [ROCm/hipBLASLt](https://github.com/ROCm/hipBLASLt) |
| rocBLAS | Linked | `MIT` | ROCm 7.2.3; flake.lock | [ROCm/rocBLAS](https://github.com/ROCm/rocBLAS) |
| MIOpen | Linked; ASR convolutions | `MIT` | ROCm 7.2.3; flake.lock | [ROCm/rocm-libraries](https://github.com/ROCm/rocm-libraries) |
| AOTriton | Linked; H3 attention images | `MIT` | 0.11.1b; flake.lock | [ROCm/aotriton](https://github.com/ROCm/aotriton) |
| hipCUB | Headers compiled into kernels | `BSD-3-Clause` | ROCm 7.2.3; flake.lock | [ROCm/hipCUB](https://github.com/ROCm/hipCUB) |
| rocPRIM | Headers compiled into kernels | `MIT` | ROCm 7.2.3; flake.lock | [ROCm/rocm-libraries](https://github.com/ROCm/rocm-libraries) |
| rocWMMA | Headers compiled into kernels | `MIT` | ROCm 7.2.3; flake.lock | [ROCm/rocWMMA](https://github.com/ROCm/rocWMMA) |
| Composable Kernel | Headers compiled into attention kernels | `MIT` | ROCm 7.2.3; flake.lock | [ROCm/composable_kernel](https://github.com/ROCm/composable_kernel) |
| ICU | Linked; Unicode normalization/tokenization | `Unicode-3.0` | 78.3; flake.lock | [unicode-org/icu](https://github.com/unicode-org/icu) |
| curl / libcurl | Linked; image HTTPS and evaluation client | `curl` | 8.21.0; flake.lock | [curl/curl](https://github.com/curl/curl) |
| OpenSSL | Linked; cryptographic hashes and HTTPS dependency | `Apache-2.0` | 3.6.3; flake.lock | [openssl/openssl](https://github.com/openssl/openssl) |
| libpng | Linked; image decoding | `libpng-2.0` | 1.6.58; flake.lock | [pnggroup/libpng](https://github.com/pnggroup/libpng) |
| libjpeg-turbo | Linked; JPEG decoding | `IJG AND BSD-3-Clause AND Zlib` | 3.1.4.1; flake.lock | [libjpeg-turbo](https://github.com/libjpeg-turbo/libjpeg-turbo) |
| FFmpeg | Separate ffmpeg/ffprobe executables for media | `GPL-3.0-or-later` (Nix build with GPL/version3 components) | 8.1.2; flake.lock | [FFmpeg](https://github.com/FFmpeg/FFmpeg) |
| GNU C/C++/OpenMP runtimes | System runtime libraries; no Gufo source import | `LGPL-2.1-or-later AND (GPL-3.0-or-later WITH GCC-exception-3.1)` | glibc/GCC packages in flake.lock | [GNU](https://www.gnu.org/software/) |
| llama.cpp / ggml | Adapted quantization, attention and model-private HIP kernels | `MIT` | `5c0e9468378eba6bf3cc1989ff5d62fbbe4d9e3a`; attention `e9fa0781f1c25fc4fe8c86be1edc6970661ad6f0` | [llama.cpp](https://github.com/ggml-org/llama.cpp) |
| Qwen chat templates | Reference Jinja and adapted renderer behavior | `Apache-2.0` | `1d4bf0f2ff6012fd82039f2fa52739d0dd7c60c0` (27B), `de4b8e4d43b917e7706784d8bb445c9af86a3540` (Flash-Next) | [Qwen](https://huggingface.co/Qwen/Qwen3.8-27B) |
| DS4 | Adapted loader, tokenizer, sessions and HIP kernels | `MIT` | `84cc882352757baf628a1776badf7cc54d584e28` | [antirez/ds4](https://github.com/antirez/ds4) |
| DS4 GB10/GX10 fork | Adapted paired MoE launch code, now HIP | `MIT` | `910501e` | [xangel82/DS4](https://github.com/xangel82/DS4-GB10-GX10-DSpark-CUDA) |
| h3.c | Adapted H3 geometry, scheduling and sampler; implementation reference | `MIT` | `8974cc055ea9c02fcd14cc27dfda3e1027c05153` | [antirez/h3.c](https://github.com/antirez/h3.c) |
| ccv TensorOps ancestry | Attribution retained through h3.c | `BSD-3-Clause` | h3.c pin above | [liuliu/ccv](https://github.com/liuliu/ccv) |
---
Upstream license texts are retained in `licenses/` and
[src/models/deepseek_v4_flash/LICENSE.ds4](src/models/deepseek_v4_flash/LICENSE.ds4).
CMake installs these with `LICENSE`, `NOTICE` and this inventory under
`share/licenses/gufo`. Preserve embedded copyright notices when modifying
adapted code. Model-private changes and import boundaries are recorded in the
[DS4 provenance](src/models/deepseek_v4_flash/UPSTREAM.md),
[DS4 HIP import](src/models/deepseek_v4_flash/kernels/rocm/mmq/VENDOR.md),
[Flash-Next HIP import](src/models/qwen38_flash_next/kernels/rocm/mmq/VENDOR.md)
and [H3 evaluation record](docs/models/minimax-h3/EVALUATION.md).
## Distributed Userspace Dependencies
This software is based in part on the work of the Independent JPEG Group.
### ROCm / HIP (AMD ROCm Compute Language Runtime)
FFmpeg is invoked as a separate process, not linked into Gufo. The pinned Nix
build enables GPL/version3 components; a system FFmpeg build can have different
terms. Redistributors bundling FFmpeg or other copyleft dependencies must also
provide their applicable license texts and corresponding source/build materials,
not just this MIT license. The pinned Nixpkgs expressions and `flake.lock`
identify those sources and build recipes; a source URL alone is not a substitute
for fulfilling the applicable distribution terms.
- **Component Name**: ROCm / HIP (AMD ROCm Compute Language Runtime)
- **Upstream URL**: https://github.com/ROCm/clr
- **Pinned Revision**: Nixpkgs `nixos-unstable` lock revision `2fcb964de67fcf60b43471c55d5d99e61a9ccb5a` (`rocmPackages.clr` / `rocmPackages.llvm.clang`)
- **Component Used**: HIP runtime, headers, host/device headers, and Clang compiler for `gfx1151`
- **SPDX License Identifier**: `MIT OR Apache-2.0 WITH LLVM-exception`
- **Copyright / Notice Source**: Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
- **Relationship**: Linked / Toolchain (Build toolchain and runtime library dependency)
- **Corresponding-Source Location**: https://github.com/ROCm/clr (via Nix derivation `rocmPackages.clr`)
The official 0731 DeepSeek continuations in
`tests/models/deepseek_v4_flash/fixtures/official-0731.json` come from DS4 revision
`6289c516273979173abbc062209a81dd3706b804`; the fixture retains source hashes and
its upstream MIT notice. External model files are never part of the binary package.
### hipBLAS
## Build and development only
- **Component Name**: hipBLAS (AMD ROCm BLAS Marshalling Library)
- **Upstream URL**: https://github.com/ROCm/hipBLAS
- **Pinned Revision**: Nixpkgs `nixos-unstable` lock revision `2fcb964de67fcf60b43471c55d5d99e61a9ccb5a` (`rocmPackages.hipblas`)
- **Component Used**: Dynamic BLAS abstraction library and interface headers
- **SPDX License Identifier**: `MIT`
- **Copyright / Notice Source**: Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
- **Relationship**: Linked (Dynamic library dependency)
- **Corresponding-Source Location**: https://github.com/ROCm/hipBLAS (via Nix derivation `rocmPackages.hipblas`)
| Component Name | Relationship | License (SPDX) | Pinned Revision / Version | Upstream Source / Location |
| --- | --- | --- | --- | --- |
| LLVM/Clang | HIP build toolchain | `Apache-2.0 WITH LLVM-exception` | ROCm LLVM from flake.lock | [ROCm/llvm-project](https://github.com/ROCm/llvm-project) |
| Triton | Build-time compilation of retained H3 kernels | `MIT` | 3.7.0; flake.lock | [triton-lang/triton](https://github.com/triton-lang/triton) |
| ROCprofiler SDK / ROCTx | Development profiling and benchmark markers only | `MIT` | ROCm 7.2.3; flake.lock | [ROCm/rocprofiler-sdk](https://github.com/ROCm/rocprofiler-sdk) |
| PyTorch ROCm | Independent evaluation; not shipped | `BSD-3-Clause` | 2.12.0; flake.lock | [pytorch/pytorch](https://github.com/pytorch/pytorch) |
| Torchvision | Evaluation; not shipped | `BSD-3-Clause` | 0.27.0; flake.lock | [pytorch/vision](https://github.com/pytorch/vision) |
| LPIPS | Evaluation; not shipped | `BSD-2-Clause` | 0.1.4; flake.lock | [PerceptualSimilarity](https://github.com/richzhang/PerceptualSimilarity) |
| Transformers / Accelerate / Safetensors / Hugging Face Hub | Evaluation, artifact inspection and model acquisition | `Apache-2.0` | flake.lock | [Hugging Face](https://github.com/huggingface) |
| NumPy / SciPy | Evaluation arrays and signal analysis | `BSD-3-Clause` | flake.lock | [NumPy](https://github.com/numpy/numpy), [SciPy](https://github.com/scipy/scipy) |
| Requests | Evaluation HTTP client | `Apache-2.0` | flake.lock | [Requests](https://github.com/psf/requests) |
| SoX | Development audio utility | `GPL-2.0-or-later` | flake.lock | [SoX](https://sourceforge.net/projects/sox/) |
| Torchvision AlexNet weights | Evaluation data only; not shipped | `NOASSERTION` | SHA-256 `7be5be791159472b1fbf3c69796f7cb30dca7ad8466c2df70058c37116cdee02` | [PyTorch model distribution](https://download.pytorch.org/models/alexnet-owt-7be5be79.pth) |
### hipBLASLt
Python/PyTorch and reference scripts are not installed with Gufo. Triton is
needed while building H3's native code, not during inference. Kernel/tuning
executables are built only with `GUFO_BUILD_TOOLS=ON`; ROCprofiler is supplied
by the development shell. Ordinary build tools (CMake, Ninja, pkg-config,
Python and the host compiler) retain their own licenses and are not Gufo code.
- **Component Name**: hipBLASLt (AMD ROCm Tunable BLAS Library)
- **Upstream URL**: https://github.com/ROCm/hipBLASLt
- **Pinned Revision**: Nixpkgs `nixos-unstable` lock revision `2fcb964de67fcf60b43471c55d5d99e61a9ccb5a` (`rocmPackages.hipblaslt`)
- **Component Used**: Tuned BF16 matrix multiplication kernels and algorithm-selection interface for prompt processing
- **SPDX License Identifier**: `MIT`
- **Copyright / Notice Source**: Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
- **Relationship**: Linked (Dynamic library dependency)
- **Corresponding-Source Location**: https://github.com/ROCm/hipBLASLt (via Nix derivation `rocmPackages.hipblaslt`)
### rocBLAS
- **Component Name**: rocBLAS (AMD ROCm Basic Linear Algebra Subprograms)
- **Upstream URL**: https://github.com/ROCm/rocBLAS
- **Pinned Revision**: Nixpkgs `nixos-unstable` lock revision `2fcb964de67fcf60b43471c55d5d99e61a9ccb5a` (`rocmPackages.rocblas`)
- **Component Used**: GPU BLAS execution kernels optimized for `gfx1151`
- **SPDX License Identifier**: `MIT`
- **Copyright / Notice Source**: Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
- **Relationship**: Linked (Dynamic library dependency)
- **Corresponding-Source Location**: https://github.com/ROCm/rocBLAS (via Nix derivation `rocmPackages.rocblas`)
### Composable Kernel
- **Component Name**: Composable Kernel (ROCm GPU kernel library)
- **Upstream URL**: https://github.com/ROCm/composable_kernel
- **Pinned Revision**: Nixpkgs `nixos-unstable` lock revision `2fcb964de67fcf60b43471c55d5d99e61a9ccb5a` (`rocmPackages.composable_kernel`)
- **Component Used**: Header-instantiated causal grouped-query attention kernel compiled into the `gfx1151` HIP backend
- **SPDX License Identifier**: `MIT`
- **Copyright / Notice Source**: Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
- **Relationship**: Header / compiled kernels
- **Corresponding-Source Location**: https://github.com/ROCm/composable_kernel (via Nix derivation `rocmPackages.composable_kernel`)
### ROCprofiler SDK / ROCTx
- **Component Name**: ROCprofiler SDK / ROCTx
- **Upstream URL**: https://github.com/ROCm/rocprofiler-sdk
- **Pinned Revision**: Nixpkgs `nixos-unstable` lock revision `2fcb964de67fcf60b43471c55d5d99e61a9ccb5a` (`rocmPackages.rocprofiler-sdk`)
- **Component Used**: The `rocprofv3` diagnostic profiler and the lightweight ROCTx marker library linked only by `gufo-kernel-bench`
- **SPDX License Identifier**: `MIT`
- **Copyright / Notice Source**: Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
- **Relationship**: Benchmark marker library / profiling tool; the inference server does not link the profiler SDK
- **Corresponding-Source Location**: https://github.com/ROCm/rocprofiler-sdk (via Nix derivation `rocmPackages.rocprofiler-sdk`)
### ICU
- **Component Name**: ICU (International Components for Unicode)
- **Upstream URL**: https://github.com/unicode-org/icu
- **Pinned Revision**: Nixpkgs `nixos-unstable` lock revision
`2fcb964de67fcf60b43471c55d5d99e61a9ccb5a` (`icu`)
- **Component Used**: Unicode NFC normalization and Unicode general-category /
whitespace classification for the model-private MiniMax H3 tokenizer
- **SPDX License Identifier**: `Unicode-3.0`
- **Relationship**: Linked dynamic runtime dependency
- **Corresponding-Source Location**: https://github.com/unicode-org/icu
(via Nix derivation `icu`)
### FFmpeg
- **Component Name**: FFmpeg
- **Upstream URL**: https://github.com/FFmpeg/FFmpeg
- **Pinned Version**: `8.1.2` from Nixpkgs lock revision
`2fcb964de67fcf60b43471c55d5d99e61a9ccb5a`
- **Component Used**: Headless `ffmpeg` and `ffprobe` executables for
concurrent RGB24/F32 PCM input, H.264/AAC MP4 encoding, and output metadata
validation
- **SPDX License Identifiers**: `LGPL-2.1-or-later` and
`GPL-2.0-or-later`; the pinned Nix derivation also declares
`LGPL-3.0-or-later` and `GPL-3.0-or-later` for enabled optional components
- **Relationship**: Spawned as a separate runtime process through pipes.
Gufo does not link FFmpeg libraries or copy FFmpeg source into the engine.
- **Corresponding-Source Location**: https://github.com/FFmpeg/FFmpeg
(via Nix derivation `ffmpeg-headless`)
The Nix closure retains FFmpeg's complete license and corresponding-source
metadata. H.264/AAC availability and any patent obligations are deployment
considerations separate from the MiniMax model license and the engine's MIT
license.
### llama.cpp
- **Component Name**: llama.cpp
- **Upstream URL**: https://github.com/ggml-org/llama.cpp
- **Pinned Revision**: Commit `e9fa0781f1c25fc4fe8c86be1edc6970661ad6f0`
- **Component Used**: Causal tiled-attention scheduling and online-softmax algorithm adapted into the native Qwen3.8 `gfx1151` HIP kernel
- **SPDX License Identifier**: `MIT`
- **Copyright / Notice Source**: Copyright (c) 2023-2026 The ggml authors
- **Relationship**: Source-derived algorithm; no llama.cpp runtime code or library is linked
- **Corresponding-Source Location**: https://github.com/ggml-org/llama.cpp/tree/e9fa0781f1c25fc4fe8c86be1edc6970661ad6f0/ggml/src/ggml-cuda
### DS4
- **Component Name**: DS4
- **Upstream URL**: https://github.com/antirez/ds4
- **Pinned Revision**: Commit `84cc882352757baf628a1776badf7cc54d584e28`
- **Component Used**: DeepSeek V4 Flash GGUF loader, tokenizer, request-session
graph, and ROCm numerical kernels
- **SPDX License Identifier**: `MIT`
- **Copyright / Notice Source**: Copyright (c) 2026 Salvatore Sanfilippo
and DS4 contributors; the upstream license is retained under
`src/models/deepseek_v4_flash/vendor/antirez/LICENSE`.
- **Relationship**: Vendored and adapted into a model-private ROCm backend.
Gufo does not import the upstream command-line interface, HTTP server,
agent, evaluator, or disk-cache frontend.
- **Corresponding-Source Location**:
https://github.com/antirez/ds4/tree/84cc882352757baf628a1776badf7cc54d584e28
- **Official continuation fixtures**: The matching 0731 hosted-model
continuations and five smoke prompts are imported separately from revision
`6289c516273979173abbc062209a81dd3706b804`. The fixture
`tests/models/deepseek_v4_flash/fixtures/official-0731.json` retains source
hashes and the upstream MIT notice (the ds4.c authors and ggml authors).
### h3.c
- **Component Name**: h3.c
- **Upstream URL**: https://github.com/antirez/h3.c
- **Pinned Revision**: `8974cc055ea9c02fcd14cc27dfda3e1027c05153`
- **Component Used**: Native MiniMax H3 architecture, tensor naming, packed
multimodal layout, scheduler, sampler, VAE, tokenizer, fixtures, and
performance reference for the model-private ROCm/HIP port
- **SPDX License Identifier**: `MIT`
- **Copyright / Notice Source**: Copyright (c) 2026 Salvatore Sanfilippo
- **Relationship**: Pinned implementation reference. Selected compatible code
may be copied or adapted into `src/models/minimax_h3/` with provenance and
modifications recorded; Metal and Objective-C execution are not imported.
- **Corresponding-Source Location**:
https://github.com/antirez/h3.c/tree/8974cc055ea9c02fcd14cc27dfda3e1027c05153
The upstream `THIRD_PARTY_NOTICES.md` identifies the rectangular Morton
decoder and dynamic INT8/TensorOps scheduling design in `h3_shaders.metal` as
adapted from ccv's `NAMatMulKernel` and `NAInt8MatMulKernel`, licensed
BSD-3-Clause with copyright (c) 2010, Liu Liu. Any adapted expression or design
retains that notice. See
[the MiniMax H3 provenance record](docs/models/minimax-h3/EVALUATION.md).
### curl / libcurl
- **Component Name**: curl / libcurl
- **Upstream URL**: https://github.com/curl/curl
- **Pinned Revision**: Version `8.21.0` from Nixpkgs lock revision
`2fcb964de67fcf60b43471c55d5d99e61a9ccb5a`
- **Component Used**: HTTPS-capable client library used only by `gufo eval`
for OpenAI-compatible model discovery and chat-completion requests
- **SPDX License Identifier**: `curl`
- **Relationship**: Linked dynamic runtime dependency
- **Corresponding-Source Location**: https://github.com/curl/curl
---
The LPIPS evaluator checks the AlexNet digest above and calibration digest
`df73285e35b22355a2df87cdb6b70b343713b667eddbda73e1977e0c860835c0`, records the
loaded module hash and library versions, and requires locally available weights.
## External Model Artifacts (Non-Distributed)
@@ -255,69 +134,3 @@ determination about a downstream operator.
See [the MiniMax H3 guide](docs/models/minimax-h3/README.md) for the acquisition, release, and
runtime boundary.
---
## Evaluation-Only Quality Toolchain (Non-Shipped)
The following packages are present only in the pinned Nix development shell.
They are not linked into, copied into, or distributed with the production
`gufo` package.
### PyTorch ROCm
- **Component Name**: PyTorch with ROCm support
- **Upstream URL**: https://github.com/pytorch/pytorch
- **Pinned Version**: `2.12.0` from Nixpkgs lock revision
`2fcb964de67fcf60b43471c55d5d99e61a9ccb5a`
- **Component Used**: Independent MiniMax H3 teacher execution and tensor
inspection on the supported ROCm host
- **SPDX License Identifier**: `BSD-3-Clause`
- **Relationship**: Evaluation and offline tooling only; absent from the
production package closure
### Torchvision
- **Component Name**: Torchvision
- **Upstream URL**: https://github.com/pytorch/vision
- **Pinned Version**: `0.27.0` from Nixpkgs lock revision
`2fcb964de67fcf60b43471c55d5d99e61a9ccb5a`
- **Component Used**: AlexNet feature network used by the LPIPS evaluator
- **SPDX License Identifier**: `BSD-3-Clause`
- **Relationship**: Evaluation-only direct dependency of the pinned Python
toolchain; absent from the production package closure
### LPIPS
- **Component Name**: Learned Perceptual Image Patch Similarity (LPIPS)
- **Upstream URL**: https://github.com/richzhang/PerceptualSimilarity
- **Pinned Version**: `0.1.4` from Nixpkgs lock revision
`2fcb964de67fcf60b43471c55d5d99e61a9ccb5a`
- **Component Used**: AlexNet-based perceptual comparison of delivered
MiniMax H3 video frames
- **SPDX License Identifier**: `BSD-2-Clause`
- **Relationship**: Evaluation-only tool; absent from the production package
closure
- **Model Boundary**: Feature-network parameters are not committed or
redistributed by gufo. Each promoted quality report records a
canonical SHA-256 of the loaded module state and the exact
LPIPS/PyTorch/Torchvision versions.
### Torchvision AlexNet evaluation weights
- **Component Name**: Torchvision AlexNet ImageNet weights
- **Upstream URL**:
https://download.pytorch.org/models/alexnet-owt-7be5be79.pth
- **Pinned Digest**:
`7be5be791159472b1fbf3c69796f7cb30dca7ad8466c2df70058c37116cdee02`
- **Component Used**: Frozen AlexNet feature trunk for LPIPS evaluation
- **SPDX License Identifier**: `NOASSERTION`
- **Relationship**: Nix-fetched evaluation model data only; absent from the
production package closure and not committed to the repository
The evaluator verifies this digest and the bundled LPIPS v0.1 AlexNet
calibration digest
`df73285e35b22355a2df87cdb6b70b343713b667eddbda73e1977e0c860835c0`
before constructing the metric. It fails closed when either file is absent or
changed and therefore does not rely on an ambient Torch hub cache or a runtime
download.
+28
View File
@@ -0,0 +1,28 @@
# Hosted CPU regression suite. Other tests remain available through CTest and
# explicit targets; adding a model oracle must not make every PR expensive.
set(gufo_pr_targets
arg_parser_test json_test gguf_reader_test gguf_identity_test
logit_sampler_test ggml_dequant_test
openai_chat_test http_server_test text_generation_scheduler_test
text_model_runner_test continuation_disk_store_test
bench_cli_test prompt_cli_test eval_http_client_test
qwen_tokenizer_test qwen_chat_template_test
qwen38_flash_next_config_test qwen38_flash_next_mtp_sampling_test
ds4_sampling_test ds4_chat_template_test ds4_cli_test
qwen3_asr_config_test qwen3_asr_audio_api_test
qwen3_tts_config_test qwen3_tts_audio_api_test
video_api_test minimax_h3_sampling_test minimax_h3_runtime_test)
set(gufo_pr_tests ${gufo_pr_targets})
list(REMOVE_ITEM gufo_pr_tests
qwen38_flash_next_config_test qwen38_flash_next_mtp_sampling_test
ds4_sampling_test ds4_chat_template_test ds4_cli_test)
list(APPEND gufo_pr_tests
"qwen38_flash_next\\.config" "qwen38_flash_next\\.mtp_sampling"
"ds4\\.sampling" "ds4\\.template" "ds4\\.cli"
gufo_version gufo_help serve_cli_test eval_http_test)
list(JOIN gufo_pr_tests "|" gufo_pr_pattern)
add_custom_target(check-pr
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure --no-tests=error
--timeout 60 -R "^(${gufo_pr_pattern})$"
DEPENDS gufo ${gufo_pr_targets}
USES_TERMINAL VERBATIM)
+6 -2
View File
@@ -63,12 +63,16 @@ answers through the OpenAI-compatible server.
## Focused kernel measurements
Build development tools with `cmake --preset gpu-test` and
`cmake --build --preset gpu-test --target gufo-kernel-bench tune_hipblaslt`.
They are omitted from the production package.
`gufo-kernel-bench` exercises production HIP entry points without loading a
model. Correctness sentinels run outside the timed interval:
```sh
./result/bin/gufo-kernel-bench --list
./result/bin/gufo-kernel-bench \
./build/gpu-test/gufo-kernel-bench --help
./build/gpu-test/gufo-kernel-bench \
--case decode-attention --context 4096,8192,12288,16384 \
--warmup 5 --repetitions 30 --json
```
+8 -3
View File
@@ -1,6 +1,7 @@
# Development
Use Nix on Linux x86-64 gfx1151. Direct host builds are unsupported.
Build on Linux x86-64 gfx1151 with CMake. Nix supplies the pinned development
toolchain; [the README](../README.md#build-from-source) also covers system dependencies.
| Directory | Ownership |
| --- | --- |
@@ -14,7 +15,8 @@ Use Nix on Linux x86-64 gfx1151. Direct host builds are unsupported.
| `.devops/nix` | Production package and server configuration |
Stage new files before Nix builds; the flake includes tracked source only.
Production performance comes from `nix build` / `result/bin`, not test binaries.
Production performance comes from `nix build` / `result/bin` or the `release`
CMake preset, with matching compiler and dependencies. Test builds keep assertions.
```sh
nix build
@@ -23,8 +25,11 @@ nix develop -c cmake --build --preset gpu-test --target <affected-target>
nix develop -c ctest --preset gpu-fast -R <affected-check> --output-on-failure
```
`gpu-test` uses RelWithDebInfo with assertions. `cpu-test` provides host checks;
`release` builds only production inference. `GUFO_BUILD_TOOLS=ON` adds kernel
benchmarks and tuning tools; `gpu-test` enables them and uses RelWithDebInfo with assertions. `cpu-test` provides host checks;
`cpu-sanitizer` enables ASan/UBSan. `gpu-full` selects the complete GPU tree.
Commands inside `nix develop -c` also work directly with system dependencies.
`cmake --preset cpu-test && cmake --build --preset pr` runs hosted contracts.
Keep slow external-model checks explicit. See [testing](TESTING.md),
[performance tooling](PERFORMANCE.md) and [model quality contracts](models/README.md).
+7 -5
View File
@@ -253,9 +253,11 @@ Qwen3.8-27B loader registers mapped GGUF shards directly with HIP; see
List cases, then run the smallest relevant matrix:
```sh
./result/bin/gufo-kernel-bench --list
nix develop -c cmake --preset gpu-test
nix develop -c cmake --build --preset gpu-test --target gufo-kernel-bench tune_hipblaslt
./build/gpu-test/gufo-kernel-bench --help
./result/bin/gufo-kernel-bench \
./build/gpu-test/gufo-kernel-bench \
--case decode-attention \
--context 4096,8192,12288,16384 \
--warmup 5 \
@@ -363,7 +365,7 @@ nix develop -c rocprofv3 \
--stats \
--summary \
--output-directory /tmp/gufo-profile \
-- ./result/bin/gufo-kernel-bench \
-- ./build/gpu-test/gufo-kernel-bench \
--case decode-attention \
--context 16384 \
--warmup 1 \
@@ -395,7 +397,7 @@ microbenchmark before acting on the mix.
### Offline tuning and replay
```sh
./result/bin/tune_hipblaslt \
./build/gpu-test/tune_hipblaslt \
--out /tmp/gufo-hipblaslt-plans.bin \
--warmup 3 \
--repetitions 10
@@ -420,7 +422,7 @@ Generated profiler, plan, and replay artifacts stay outside the repository.
### Dispatch telemetry
```sh
GUFO_DISPATCH_TELEMETRY=1 ./result/bin/gufo-kernel-bench ...
GUFO_DISPATCH_TELEMETRY=1 ./build/gpu-test/gufo-kernel-bench ...
```
Telemetry is diagnostic JSONL. It records semantic dispatch decisions and
+33 -3
View File
@@ -2,7 +2,7 @@
Run the smallest check that covers the change. Correctness and model-owned
quality limits pass before performance results count. Production targets
Linux gfx1151; all builds and tests use Nix.
Linux gfx1151. CMake/CTest work with either system dependencies or `nix develop`.
## Commands
@@ -10,7 +10,7 @@ Linux gfx1151; all builds and tests use Nix.
# Documentation changes
nix build .#checks.x86_64-linux.docs
# Canonical PR gates
# Hosted CPU/repository checks
nix build .#checks.x86_64-linux.pr
# Focused test (replace the target and expression)
@@ -26,7 +26,8 @@ nix develop -c ctest --preset cpu-sanitizer --output-on-failure
See [development](DEVELOPMENT.md) for all build presets.
`gpu-test` uses optimized `RelWithDebInfo` with assertions.
Performance measurements use `nix build` binaries under `result/bin`.
Performance measurements use `nix build` binaries under `result/bin` or the
`release` preset with the same compiler and dependencies.
Add new files to Git before invoking Nix.
`gpu-fast` excludes `slow` and `external-model` tests. The
@@ -35,6 +36,35 @@ model/device suites; `gpu-full` covers the complete hardware tree.
External-model tests require their documented local artifacts. A skip due to
an absent model or device is not a quality pass.
## Hosted versus local checks
GitHub runs formatting, documentation, dependency-inventory and server-command
checks plus `cmake/Checks.cmake`: parsing, GGUF safety, sampling, templates,
server/cache/scheduler behavior, model configuration and small audio/video API
contracts. It builds only those test executables and the CPU CLI. It does not
build ROCm, download weights, run full H3 oracles or compile whole-tree clang-tidy.
The workflow cancels superseded runs and has a 25-minute limit.
```sh
# Same hosted C++/CLI selection locally
cmake --preset cpu-test
cmake --build --preset pr --parallel 4
# Full CPU suite, or explicit non-hosted tools
nix build .#checks.x86_64-linux.tests
nix build .#checks.x86_64-linux.static-analysis
nix build .#checks.x86_64-linux.h3-manifest
nix build .#checks.x86_64-linux.h3-quality
nix build .#checks.x86_64-linux.h3-ml-quality
```
Full CPU builds need Python NumPy for the offline tool tests. GPU model oracles
need the documented local weights; use focused targets and CTest labels before
running the complete tree. H3 development normally needs an analytic primitive,
one transformer block or one denoiser forward. Full video generation and LPIPS
are release/quality qualification, not routine PR work. `nix flake check` runs
all declared checks and is deliberately not the hosted CI command.
## Match validation to the change
| Change | Relevant checks |
+42 -63
View File
@@ -102,10 +102,7 @@
base = gufoPackages.${system}.gufo;
in
{
default = base.override {
rocmSupport = true;
rocmGpuTargets = [ "gfx1151" ];
};
default = base;
}
);
@@ -125,10 +122,6 @@
pkgs.${system}.sqlite
];
env = {
ROCM_PATH = "${pkgs.${system}.rocmPackages.clr}";
GUFO_HIPCUB_ROOT = "${pkgs.${system}.rocmPackages.hipcub}";
GUFO_ROCPRIM_ROOT = "${pkgs.${system}.rocmPackages.rocprim}";
GUFO_ROCWMMA_ROOT = "${pkgs.${system}.rocmPackages.rocwmma}";
TORCH_HOME = "${alexnetTorchHome system}";
LD_LIBRARY_PATH = pkgs.${system}.lib.makeLibraryPath [
pkgs.${system}.stdenv.cc.cc.lib
@@ -196,7 +189,7 @@
"cmake"
"src"
"tests"
"tools/gufo"
"tools"
];
files = [
".clang-format"
@@ -272,6 +265,9 @@
pkgsSys.findutils
pkgsSys.icu
pkgsSys.curl
pkgsSys.libpng
pkgsSys.libjpeg
pkgsSys.openssl
];
src = staticAnalysisSource;
} ''
@@ -374,15 +370,18 @@
echo "PASS: MiniMax H3 pinned teacher and offline LPIPS clean" > $out/result.txt
'';
testCheck = pkgsSys.runCommand "check-tests" {
mkTestCheck = full: pkgsSys.runCommand (if full then "check-tests" else "check-pr-tests") {
nativeBuildInputs = [
pkgsSys.stdenv.cc
pkgsSys.ccache
pkgsSys.cmake
pkgsSys.ninja
(pkgsSys.python3.withPackages (ps: [ ps.numpy ]))
(if full then pkgsSys.python3.withPackages (ps: [ ps.numpy ]) else pkgsSys.python3)
pkgsSys.icu
pkgsSys.curl
pkgsSys.libpng
pkgsSys.libjpeg
pkgsSys.openssl
];
src = testSource;
} ''
@@ -404,39 +403,48 @@
fi
mkdir -p build && cd build
cmake "$src" -GNinja $ccache_launcher -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=ON -DSTRIX_ENABLE_WARNINGS=ON -DSTRIX_ENABLE_SANITIZERS=OFF
ninja
ctest --output-on-failure
cmake "$src" -GNinja $ccache_launcher -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_TESTING=ON -DENGINE_ENABLE_HIP=OFF
${if full then ''
cmake --build . --parallel "$NIX_BUILD_CORES"
ctest --output-on-failure --timeout 120
'' else ''
cmake --build . --parallel "$NIX_BUILD_CORES" --target check-pr
''}
if [[ -n "$ccache_launcher" ]]; then
ccache --show-stats
fi
mkdir -p $out
echo "PASS: CPU build and CTest suite passed" > $out/result.txt
echo "PASS: ${if full then "Full CPU" else "Hosted CPU contract"} suite passed" > $out/result.txt
'';
testCheck = mkTestCheck true;
prTestCheck = mkTestCheck false;
mkServeCheck =
let
audioCmd = self.lib.${system}.mkGufoServe {
# Test command rendering without realizing the GPU package.
mkServe = args: self.lib.${system}.mkGufoServe (args // { gufo = "/gufo-test"; });
audioCmd = mkServe {
modality = "audio";
model = "/var/models/qwen3-tts";
context = 4096;
port = 9100;
sessions = 2;
};
ttsAliasCmd = self.lib.${system}.mkGufoServe {
ttsAliasCmd = mkServe {
modality = "tts";
model = "/var/models/qwen3-tts";
};
bothCmd = self.lib.${system}.mkGufoServe {
bothCmd = mkServe {
modality = "audio";
ttsModel = "/var/models/qwen3-tts";
asrModel = "/var/models/qwen3-asr";
ttsContext = 4096;
asrContext = 1024;
};
voicesCmd = self.lib.${system}.mkGufoServe {
voicesCmd = mkServe {
modality = "audio";
ttsModel = "/var/models/qwen3-tts";
voices = {
@@ -451,16 +459,16 @@
};
};
};
asrCmd = self.lib.${system}.mkGufoServe {
asrCmd = mkServe {
modality = "asr";
model = "/var/models/qwen3-asr";
context = 1024;
};
sttAliasCmd = self.lib.${system}.mkGufoServe {
sttAliasCmd = mkServe {
modality = "stt";
model = "/var/models/qwen3-asr";
};
cmd = self.lib.${system}.mkGufoServe {
cmd = mkServe {
model = "/var/models/qwen.gguf";
context = 4096;
servedModelName = "qwen-test";
@@ -483,13 +491,13 @@
cacheDiskBytes = 1024;
cacheDiskStagingBytes = 512;
};
dsparkCmd = self.lib.${system}.mkGufoServe {
dsparkCmd = mkServe {
model = "/var/models/ds4.gguf";
speculative = "dspark";
dsparkModel = "/var/models/dspark.gguf";
draftTokens = 3;
};
mtpCmd = self.lib.${system}.mkGufoServe {
mtpCmd = mkServe {
model = "/var/models/qwen-flash.gguf";
speculative = "mtp";
mtpModel = "/var/models/mtp.gguf";
@@ -564,46 +572,16 @@
echo "PASS: mkGufoServe CLI string check passed" > $out/result.txt
'';
# Canonical PR umbrella. Nix builds these independent derivations in
# parallel and reuses their results across flake checks and PR runs.
# The ROCm PyTorch/LPIPS closure remains an explicit h3-ml-quality
# check: realizing its multi-gigabyte offline-evaluation toolchain on
# every hosted PR runner exhausts the runner disk before tests start.
# The hosted runner builds CPU contracts only. GPU compilation,
# whole-tree clang-tidy, full CPU and H3 oracles are explicit checks.
prCheck = pkgsSys.runCommand "check-pr" { } ''
mkdir -p $out/bin
cp "${self.packages.${system}.default}/bin/gufo" $out/bin/gufo
ln -sf gufo $out/bin/gufo-server
cat "${formatCheck}/result.txt"
cat "${staticAnalysisCheck}/result.txt"
cat "${dependencyInventoryCheck}/result.txt"
cat "${docsCheck}/result.txt"
cat "${h3ManifestCheck}/result.txt"
cat "${h3QualityCheck}/result.txt"
cat "${testCheck}/result.txt"
cat "${mkServeCheck}/result.txt"
cat <<EOF > $out/pr-summary.txt
PR Check Summary
Status: ALL GATES PASSED
Package: ${self.packages.${system}.default.name}
Revision: ${version}
System: ${system}
Composed Gates:
1. Format Validation (clang-format)
2. Static Analysis (clang-tidy)
3. Dependency and License Inventory (THIRD_PARTY_NOTICES.md)
4. Documentation & Local Link Validation (check-docs.py)
5. MiniMax H3 Source-Manifest Validation
6. MiniMax H3 Quality-Oracle Validation
7. CPU Build and Runtime/Unit Tests (CTest)
8. Declarative Server Wrapper Generation (mkGufoServe)
Explicit Offline Gate (not in hosted PR closure):
- MiniMax H3 Pinned Teacher & Offline LPIPS Validation
Production Package Validation:
- gfx1151 ROCm/HIP build
- Installed gufo-server version/help smoke
EOF
mkdir -p $out
cat ${formatCheck}/result.txt \
${dependencyInventoryCheck}/result.txt \
${docsCheck}/result.txt \
${prTestCheck}/result.txt \
${mkServeCheck}/result.txt > $out/pr-summary.txt
cat $out/pr-summary.txt
'';
in
{
@@ -615,6 +593,7 @@ EOF
h3-quality = h3QualityCheck;
h3-ml-quality = h3MlQualityCheck;
tests = testCheck;
pr-tests = prTestCheck;
mk-serve = mkServeCheck;
pr = prCheck;
}
+23
View File
@@ -0,0 +1,23 @@
MIT License
Copyright (c) 2026 The ds4.c authors
Copyright (c) 2026 Marco Palaferri for the GB10/GX10 DSpark CUDA modifications
Copyright (c) 2023-2026 The ggml authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+20
View File
@@ -0,0 +1,20 @@
Copyright (C) 2024-2025 Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+10
View File
@@ -0,0 +1,10 @@
Copyright (c) 2010, Liu Liu
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of the authors nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+28
View File
@@ -0,0 +1,28 @@
Copyright (c) 2018- , Advanced Micro Devices, Inc. (Chao Liu, Jing Zhang)
Copyright (c) 2019- , Advanced Micro Devices, Inc. (Letao Qin, Qianfeng Zhang, Liang Huang, Shaojie Wang)
Copyright (c) 2022- , Advanced Micro Devices, Inc. (Anthony Chang, Chunyu Lai, Illia Silin, Adam Osewski, Poyen Chen, Jehandad Khan)
Copyright (c) 2019-2021, Advanced Micro Devices, Inc. (Hanwen Chang)
Copyright (c) 2019-2020, Advanced Micro Devices, Inc. (Tejash Shah)
Copyright (c) 2020 , Advanced Micro Devices, Inc. (Xiaoyan Zhou)
Copyright (c) 2021-2022, Advanced Micro Devices, Inc. (Jianfeng Yan)
SPDX-License-Identifier: MIT
Copyright (c) 2018-2025, Advanced Micro Devices, Inc. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+22
View File
@@ -0,0 +1,22 @@
COPYRIGHT AND PERMISSION NOTICE
Copyright (c) 1996 - 2026, Daniel Stenberg, <[email protected]>, and many
contributors, see the THANKS file.
All rights reserved.
Permission to use, copy, modify, and distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright
notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of a copyright holder shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization of the copyright holder.
+674
View File
@@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+165
View File
@@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
+127
View File
@@ -0,0 +1,127 @@
# License
Most files in FFmpeg are under the GNU Lesser General Public License version 2.1
or later (LGPL v2.1+). Read the file `COPYING.LGPLv2.1` for details. Some other
files have MIT/X11/BSD-style licenses. In combination the LGPL v2.1+ applies to
FFmpeg.
Some optional parts of FFmpeg are licensed under the GNU General Public License
version 2 or later (GPL v2+). See the file `COPYING.GPLv2` for details. None of
these parts are used by default, you have to explicitly pass `--enable-gpl` to
configure to activate them. In this case, FFmpeg's license changes to GPL v2+.
Specifically, the GPL parts of FFmpeg are:
- optional x86 optimization in the files
- `libavcodec/x86/flac_dsp_gpl.asm`
- `libavcodec/x86/idct_mmx.c`
- `libavfilter/x86/vf_removegrain.asm`
- the following building and testing tools
- `compat/solaris/make_sunver.pl`
- `doc/t2h.pm`
- `doc/texi2pod.pl`
- `libswresample/tests/swresample.c`
- `tests/checkasm/*`
- `tests/tiny_ssim.c`
- the following filters in libavfilter:
- `signature_lookup.c`
- `vf_blackframe.c`
- `vf_boxblur.c`
- `vf_colormatrix.c`
- `vf_cover_rect.c`
- `vf_cropdetect.c`
- `vf_delogo.c`
- `vf_eq.c`
- `vf_find_rect.c`
- `vf_fspp.c`
- `vf_histeq.c`
- `vf_hqdn3d.c`
- `vf_kerndeint.c`
- `vf_lensfun.c` (GPL version 3 or later)
- `vf_mcdeint.c`
- `vf_mpdecimate.c`
- `vf_nnedi.c`
- `vf_owdenoise.c`
- `vf_perspective.c`
- `vf_phase.c`
- `vf_pp7.c`
- `vf_pullup.c`
- `vf_repeatfields.c`
- `vf_sab.c`
- `vf_signature.c`
- `vf_smartblur.c`
- `vf_spp.c`
- `vf_stereo3d.c`
- `vf_super2xsai.c`
- `vf_tinterlace.c`
- `vf_uspp.c`
- `vf_vaguedenoiser.c`
- `vsrc_mptestsrc.c`
Should you, for whatever reason, prefer to use version 3 of the (L)GPL, then
the configure parameter `--enable-version3` will activate this licensing option
for you. Read the file `COPYING.LGPLv3` or, if you have enabled GPL parts,
`COPYING.GPLv3` to learn the exact legal terms that apply in this case.
There are a handful of files under other licensing terms, namely:
* The files `libavcodec/jfdctfst.c`, `libavcodec/jfdctint_template.c` and
`libavcodec/jrevdct.c` are taken from libjpeg, see the top of the files for
licensing details. Specifically note that you must credit the IJG in the
documentation accompanying your program if you only distribute executables.
You must also indicate any changes including additions and deletions to
those three files in the documentation.
* `tests/reference.pnm` is under the expat license.
## External libraries
FFmpeg can be combined with a number of external libraries, which sometimes
affect the licensing of binaries resulting from the combination.
### Compatible libraries
The following libraries are under GPL version 2:
- avisynth
- frei0r
- libcdio
- libdavs2
- librubberband
- libvidstab
- libx264
- libx265
- libxavs
- libxavs2
- libxvid
When combining them with FFmpeg, FFmpeg needs to be licensed as GPL as well by
passing `--enable-gpl` to configure.
The following libraries are under LGPL version 3:
- gmp
- libaribb24
- liblensfun
When combining them with FFmpeg, use the configure option `--enable-version3` to
upgrade FFmpeg to the LGPL v3.
The VMAF, mbedTLS, RK MPI, OpenCORE and VisualOn libraries are under the Apache License
2.0. That license is incompatible with the LGPL v2.1 and the GPL v2, but not with
version 3 of those licenses. So to combine these libraries with FFmpeg, the
license version needs to be upgraded by passing `--enable-version3` to configure.
The smbclient library is under the GPL v3, to combine it with FFmpeg,
the options `--enable-gpl` and `--enable-version3` have to be passed to
configure to upgrade FFmpeg to the GPL v3.
### Incompatible libraries
There are certain libraries you can combine with FFmpeg whose licenses are not
compatible with the GPL and/or the LGPL. If you wish to enable these
libraries, even in circumstances that their license may be incompatible, pass
`--enable-nonfree` to configure. This will cause the resulting binary to be
unredistributable.
The Fraunhofer FDK AAC and OpenSSL libraries are under licenses which are
incompatible with the GPLv2 and v3. To the best of our knowledge, they are
compatible with the LGPL.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Salvatore Sanfilippo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (C) Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+74
View File
@@ -0,0 +1,74 @@
MIT License
Copyright (C) Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---
This product includes software from copyright holders as shown below, and distributed under their license terms as specified.
Copyright (c) 1992-2022 The University of Tennessee and The University
of Tennessee Research Foundation. All rights
reserved.
Copyright (c) 2000-2022 The University of California Berkeley. All
rights reserved.
Copyright (c) 2006-2022 The University of Colorado Denver. All rights
reserved.
$COPYRIGHT$
Additional copyrights may follow
$HEADER$
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer listed
in this license in the documentation and/or other materials
provided with the distribution.
- Neither the name of the copyright holders nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
The copyright holders provide no reassurances that the source code
provided does not infringe any patent, copyright, or any other
intellectual property rights of third parties. The copyright holders
disclaim any liability to any recipient for claims brought against
recipient by any third party for infringement of that parties
intellectual property rights.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (C) Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+26
View File
@@ -0,0 +1,26 @@
Copyright (c) 2010-2011, Duane Merrill. All rights reserved.
Copyright (c) 2011-2018, NVIDIA CORPORATION. All rights reserved.
Modifications Copyright (c) 2019-2025, Advanced Micro Devices, Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the NVIDIA CORPORATION nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+568
View File
@@ -0,0 +1,568 @@
UNICODE LICENSE V3
COPYRIGHT AND PERMISSION NOTICE
Copyright © 1991-2026 Unicode, Inc.
NOTICE TO USER: Carefully read the following legal agreement. BY
DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR
SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE
TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT
DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
Permission is hereby granted, free of charge, to any person obtaining a
copy of data files and any associated documentation (the "Data Files") or
software and any associated documentation (the "Software") to deal in the
Data Files or Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, and/or sell
copies of the Data Files or Software, and to permit persons to whom the
Data Files or Software are furnished to do so, provided that either (a)
this copyright and permission notice appear with all copies of the Data
Files or Software, or (b) this copyright and permission notice appear in
associated Documentation.
THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA
FILES OR SOFTWARE.
Except as contained in this notice, the name of a copyright holder shall
not be used in advertising or otherwise to promote the sale, use or other
dealings in these Data Files or Software without prior written
authorization of the copyright holder.
SPDX-License-Identifier: Unicode-3.0
----------------------------------------------------------------------
Third-Party Software Licenses
This section contains third-party software notices and/or additional
terms for licensed third-party software components included within ICU
libraries.
----------------------------------------------------------------------
ICU License - ICU 1.8.1 to ICU 57.1
COPYRIGHT AND PERMISSION NOTICE
Copyright (c) 1995-2016 International Business Machines Corporation and others
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, and/or sell copies of the Software, and to permit persons
to whom the Software is furnished to do so, provided that the above
copyright notice(s) and this permission notice appear in all copies of
the Software and that both the above copyright notice(s) and this
permission notice appear in supporting documentation.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY
SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Except as contained in this notice, the name of a copyright holder
shall not be used in advertising or otherwise to promote the sale, use
or other dealings in this Software without prior written authorization
of the copyright holder.
All trademarks and registered trademarks mentioned herein are the
property of their respective owners.
----------------------------------------------------------------------
Chinese/Japanese Word Break Dictionary Data (cjdict.txt)
# The Google Chrome software developed by Google is licensed under
# the BSD license. Other software included in this distribution is
# provided under other licenses, as set forth below.
#
# The BSD License
# http://opensource.org/licenses/bsd-license.php
# Copyright (C) 2006-2008, Google Inc.
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided with
# the distribution.
# Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
#
# The word list in cjdict.txt are generated by combining three word lists
# listed below with further processing for compound word breaking. The
# frequency is generated with an iterative training against Google web
# corpora.
#
# * Libtabe (Chinese)
# - https://sourceforge.net/project/?group_id=1519
# - Its license terms and conditions are shown below.
#
# * IPADIC (Japanese)
# - http://chasen.aist-nara.ac.jp/chasen/distribution.html
# - Its license terms and conditions are shown below.
#
# ---------COPYING.libtabe ---- BEGIN--------------------
#
# /*
# * Copyright (c) 1999 TaBE Project.
# * Copyright (c) 1999 Pai-Hsiang Hsiao.
# * All rights reserved.
# *
# * Redistribution and use in source and binary forms, with or without
# * modification, are permitted provided that the following conditions
# * are met:
# *
# * . Redistributions of source code must retain the above copyright
# * notice, this list of conditions and the following disclaimer.
# * . Redistributions in binary form must reproduce the above copyright
# * notice, this list of conditions and the following disclaimer in
# * the documentation and/or other materials provided with the
# * distribution.
# * . Neither the name of the TaBE Project nor the names of its
# * contributors may be used to endorse or promote products derived
# * from this software without specific prior written permission.
# *
# * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
# * OF THE POSSIBILITY OF SUCH DAMAGE.
# */
#
# /*
# * Copyright (c) 1999 Computer Systems and Communication Lab,
# * Institute of Information Science, Academia
# * Sinica. All rights reserved.
# *
# * Redistribution and use in source and binary forms, with or without
# * modification, are permitted provided that the following conditions
# * are met:
# *
# * . Redistributions of source code must retain the above copyright
# * notice, this list of conditions and the following disclaimer.
# * . Redistributions in binary form must reproduce the above copyright
# * notice, this list of conditions and the following disclaimer in
# * the documentation and/or other materials provided with the
# * distribution.
# * . Neither the name of the Computer Systems and Communication Lab
# * nor the names of its contributors may be used to endorse or
# * promote products derived from this software without specific
# * prior written permission.
# *
# * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
# * OF THE POSSIBILITY OF SUCH DAMAGE.
# */
#
# Copyright 1996 Chih-Hao Tsai @ Beckman Institute,
# University of Illinois
# [email protected] http://casper.beckman.uiuc.edu/~c-tsai4
#
# ---------------COPYING.libtabe-----END--------------------------------
#
#
# ---------------COPYING.ipadic-----BEGIN-------------------------------
#
# Copyright 2000, 2001, 2002, 2003 Nara Institute of Science
# and Technology. All Rights Reserved.
#
# Use, reproduction, and distribution of this software is permitted.
# Any copy of this software, whether in its original form or modified,
# must include both the above copyright notice and the following
# paragraphs.
#
# Nara Institute of Science and Technology (NAIST),
# the copyright holders, disclaims all warranties with regard to this
# software, including all implied warranties of merchantability and
# fitness, in no event shall NAIST be liable for
# any special, indirect or consequential damages or any damages
# whatsoever resulting from loss of use, data or profits, whether in an
# action of contract, negligence or other tortuous action, arising out
# of or in connection with the use or performance of this software.
#
# A large portion of the dictionary entries
# originate from ICOT Free Software. The following conditions for ICOT
# Free Software applies to the current dictionary as well.
#
# Each User may also freely distribute the Program, whether in its
# original form or modified, to any third party or parties, PROVIDED
# that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear
# on, or be attached to, the Program, which is distributed substantially
# in the same form as set out herein and that such intended
# distribution, if actually made, will neither violate or otherwise
# contravene any of the laws and regulations of the countries having
# jurisdiction over the User or the intended distribution itself.
#
# NO WARRANTY
#
# The program was produced on an experimental basis in the course of the
# research and development conducted during the project and is provided
# to users as so produced on an experimental basis. Accordingly, the
# program is provided without any warranty whatsoever, whether express,
# implied, statutory or otherwise. The term "warranty" used herein
# includes, but is not limited to, any warranty of the quality,
# performance, merchantability and fitness for a particular purpose of
# the program and the nonexistence of any infringement or violation of
# any right of any third party.
#
# Each user of the program will agree and understand, and be deemed to
# have agreed and understood, that there is no warranty whatsoever for
# the program and, accordingly, the entire risk arising from or
# otherwise connected with the program is assumed by the user.
#
# Therefore, neither ICOT, the copyright holder, or any other
# organization that participated in or was otherwise related to the
# development of the program and their respective officials, directors,
# officers and other employees shall be held liable for any and all
# damages, including, without limitation, general, special, incidental
# and consequential damages, arising out of or otherwise in connection
# with the use or inability to use the program or any product, material
# or result produced or otherwise obtained by using the program,
# regardless of whether they have been advised of, or otherwise had
# knowledge of, the possibility of such damages at any time during the
# project or thereafter. Each user will be deemed to have agreed to the
# foregoing by his or her commencement of use of the program. The term
# "use" as used herein includes, but is not limited to, the use,
# modification, copying and distribution of the program and the
# production of secondary products from the program.
#
# In the case where the program, whether in its original form or
# modified, was distributed or delivered to or received by a user from
# any person, organization or entity other than ICOT, unless it makes or
# grants independently of ICOT any specific warranty to the user in
# writing, such person, organization or entity, will also be exempted
# from and not be held liable to the user for any such damages as noted
# above as far as the program is concerned.
#
# ---------------COPYING.ipadic-----END----------------------------------
----------------------------------------------------------------------
Lao Word Break Dictionary Data (laodict.txt)
# Copyright (C) 2016 and later: Unicode, Inc. and others.
# License & terms of use: http://www.unicode.org/copyright.html
# Copyright (c) 2015 International Business Machines Corporation
# and others. All Rights Reserved.
#
# Project: https://github.com/rober42539/lao-dictionary
# Dictionary: https://github.com/rober42539/lao-dictionary/laodict.txt
# License: https://github.com/rober42539/lao-dictionary/LICENSE.txt
# (copied below)
#
# This file is derived from the above dictionary version of Nov 22, 2020
# ----------------------------------------------------------------------
# Copyright (C) 2013 Brian Eugene Wilson, Robert Martin Campbell.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer. Redistributions in binary
# form must reproduce the above copyright notice, this list of conditions and
# the following disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
# OF THE POSSIBILITY OF SUCH DAMAGE.
# --------------------------------------------------------------------------
----------------------------------------------------------------------
Burmese Word Break Dictionary Data (burmesedict.txt)
# Copyright (c) 2014 International Business Machines Corporation
# and others. All Rights Reserved.
#
# This list is part of a project hosted at:
# github.com/kanyawtech/myanmar-karen-word-lists
#
# --------------------------------------------------------------------------
# Copyright (c) 2013, LeRoy Benjamin Sharon
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met: Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer. Redistributions in binary form must reproduce the
# above copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
#
# Neither the name Myanmar Karen Word Lists, nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
# --------------------------------------------------------------------------
----------------------------------------------------------------------
Time Zone Database
ICU uses the public domain data and code derived from Time Zone
Database for its time zone support. The ownership of the TZ database
is explained in BCP 175: Procedure for Maintaining the Time Zone
Database section 7.
# 7. Database Ownership
#
# The TZ database itself is not an IETF Contribution or an IETF
# document. Rather it is a pre-existing and regularly updated work
# that is in the public domain, and is intended to remain in the
# public domain. Therefore, BCPs 78 [RFC5378] and 79 [RFC3979] do
# not apply to the TZ Database or contributions that individuals make
# to it. Should any claims be made and substantiated against the TZ
# Database, the organization that is providing the IANA
# Considerations defined in this RFC, under the memorandum of
# understanding with the IETF, currently ICANN, may act in accordance
# with all competent court orders. No ownership claims will be made
# by ICANN or the IETF Trust on the database or the code. Any person
# making a contribution to the database or code waives all rights to
# future claims in that contribution or in the TZ Database.
----------------------------------------------------------------------
Google double-conversion
Copyright 2006-2011, the V8 project authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
JSON parsing library (nlohmann/json)
File: vendor/json/upstream/single_include/nlohmann/json.hpp (only for ICU4C)
MIT License
Copyright (c) 2013-2022 Niels Lohmann
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
----------------------------------------------------------------------
File: aclocal.m4 (only for ICU4C)
Section: pkg.m4 - Macros to locate and utilise pkg-config.
Copyright © 2004 Scott James Remnant <[email protected]>.
Copyright © 2012-2015 Dan Nicholson <[email protected]>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
As a special exception to the GNU General Public License, if you
distribute this file as part of a program that contains a
configuration script generated by Autoconf, you may include it under
the same distribution terms that you use for the rest of that
program.
(The condition for the exception is fulfilled because
ICU4C includes a configuration script generated by Autoconf,
namely the `configure` script.)
----------------------------------------------------------------------
File: config.guess (only for ICU4C)
This file is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <https://www.gnu.org/licenses/>.
As a special exception to the GNU General Public License, if you
distribute this file as part of a program that contains a
configuration script generated by Autoconf, you may include it under
the same distribution terms that you use for the rest of that
program. This Exception is an additional permission under section 7
of the GNU General Public License, version 3 ("GPLv3").
(The condition for the exception is fulfilled because
ICU4C includes a configuration script generated by Autoconf,
namely the `configure` script.)
----------------------------------------------------------------------
File: install-sh (only for ICU4C)
Copyright 1991 by the Massachusetts Institute of Technology
Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation, and that the name of M.I.T. not be used in advertising or
publicity pertaining to distribution of the software without specific,
written prior permission. M.I.T. makes no representations about the
suitability of this software for any purpose. It is provided "as is"
without express or implied warranty.
----------------------------------------------------------------------
File: sorttable.js (only for ICU4J)
The MIT Licence, for code from kryogenix.org
Code downloaded from the Browser Experiments section of kryogenix.org is
licenced under the so-called MIT licence. The licence is below.
Copyright (c) 1997-date Stuart Langridge
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+260
View File
@@ -0,0 +1,260 @@
libjpeg-turbo note: This file has been modified by The libjpeg-turbo Project
to include only information relevant to libjpeg-turbo, to wordsmith certain
sections, and to remove impolitic language that existed in the libjpeg v8
README. It is included only for reference. Please see README.md for
information specific to libjpeg-turbo.
The Independent JPEG Group's JPEG software
==========================================
This distribution contains a release of the Independent JPEG Group's free JPEG
software. You are welcome to redistribute this software and to use it for any
purpose, subject to the conditions under LEGAL ISSUES, below.
This software is the work of Tom Lane, Guido Vollbeding, Philip Gladstone,
Bill Allombert, Jim Boucher, Lee Crocker, Bob Friesenhahn, Ben Jackson,
Julian Minguillon, Luis Ortiz, George Phillips, Davide Rossi, Ge' Weijers,
and other members of the Independent JPEG Group.
IJG is not affiliated with the ISO/IEC JTC1/SC29/WG1 standards committee
(also known as JPEG, together with ITU-T SG16).
DOCUMENTATION ROADMAP
=====================
This file contains the following sections:
OVERVIEW General description of JPEG and the IJG software.
LEGAL ISSUES Copyright, lack of warranty, terms of distribution.
REFERENCES Where to learn more about JPEG.
ARCHIVE LOCATIONS Where to find newer versions of this software.
FILE FORMAT WARS Software *not* to get.
TO DO Plans for future IJG releases.
Other documentation files in the distribution are:
User documentation:
doc/usage.txt Usage instructions for cjpeg, djpeg, jpegtran,
rdjpgcom, and wrjpgcom.
doc/*.1 Unix-style man pages for programs (same info as
usage.txt).
doc/wizard.txt Advanced usage instructions for JPEG wizards only.
doc/change.log Version-to-version change highlights.
Programmer and internal documentation:
doc/libjpeg.txt How to use the JPEG library in your own programs.
src/example.c Sample code for calling the JPEG library.
doc/structure.txt Overview of the JPEG library's internal structure.
doc/coderules.txt Coding style rules --- please read if you contribute
code.
Please read at least usage.txt. Some information can also be found in the JPEG
FAQ (Frequently Asked Questions) article. See ARCHIVE LOCATIONS below to find
out where to obtain the FAQ article.
If you want to understand how the JPEG code works, we suggest reading one or
more of the REFERENCES, then looking at the documentation files (in roughly
the order listed) before diving into the code.
OVERVIEW
========
This package contains C software to implement JPEG image encoding, decoding,
and transcoding. JPEG (pronounced "jay-peg") is a standardized compression
method for full-color and grayscale images. JPEG's strong suit is compressing
photographic images or other types of images that have smooth color and
brightness transitions between neighboring pixels. Images with sharp lines or
other abrupt features may not compress well with JPEG, and a higher JPEG
quality may have to be used to avoid visible compression artifacts with such
images.
JPEG is normally lossy, meaning that the output pixels are not necessarily
identical to the input pixels. However, on photographic content and other
"smooth" images, very good compression ratios can be obtained with no visible
compression artifacts, and extremely high compression ratios are possible if
you are willing to sacrifice image quality (by reducing the "quality" setting
in the compressor.)
This software implements JPEG baseline, extended-sequential, progressive, and
lossless compression processes. Provision is made for supporting all variants
of these processes, although some uncommon parameter settings aren't
implemented yet. We have made no provision for supporting the hierarchical
processes defined in the standard.
We provide a set of library routines for reading and writing JPEG image files,
plus two sample applications "cjpeg" and "djpeg", which use the library to
perform conversion between JPEG and some other popular image file formats.
The library is intended to be reused in other applications.
In order to support file conversion and viewing software, we have included
considerable functionality beyond the bare JPEG coding/decoding capability;
for example, the color quantization modules are not strictly part of JPEG
decoding, but they are essential for output to colormapped file formats. These
extra functions can be compiled out of the library if not required for a
particular application.
We have also included "jpegtran", a utility for lossless transcoding between
different JPEG processes, and "rdjpgcom" and "wrjpgcom", two simple
applications for inserting and extracting textual comments in JFIF files.
The emphasis in designing this software has been on achieving portability and
flexibility, while also making it fast enough to be useful. In particular,
the software is not intended to be read as a tutorial on JPEG. (See the
REFERENCES section for introductory material.) Rather, it is intended to
be reliable, portable, industrial-strength code. We do not claim to have
achieved that goal in every aspect of the software, but we strive for it.
We welcome the use of this software as a component of commercial products.
No royalty is required, but we do ask for an acknowledgement in product
documentation, as described under LEGAL ISSUES.
LEGAL ISSUES
============
In plain English:
1. We don't promise that this software works. (But if you find any bugs,
please let us know!)
2. You can use this software for whatever you want. You don't have to pay us.
3. You may not pretend that you wrote this software. If you use it in a
program, you must acknowledge somewhere in your documentation that
you've used the IJG code.
In legalese:
The authors make NO WARRANTY or representation, either express or implied,
with respect to this software, its quality, accuracy, merchantability, or
fitness for a particular purpose. This software is provided "AS IS", and you,
its user, assume the entire risk as to its quality and accuracy.
This software is copyright (C) 1991-2020, Thomas G. Lane, Guido Vollbeding.
All Rights Reserved except as specified below.
Permission is hereby granted to use, copy, modify, and distribute this
software (or portions thereof) for any purpose, without fee, subject to these
conditions:
(1) If any part of the source code for this software is distributed, then this
README file must be included, with this copyright and no-warranty notice
unaltered; and any additions, deletions, or changes to the original files
must be clearly indicated in accompanying documentation.
(2) If only executable code is distributed, then the accompanying
documentation must state that "this software is based in part on the work of
the Independent JPEG Group".
(3) Permission for use of this software is granted only if the user accepts
full responsibility for any undesirable consequences; the authors accept
NO LIABILITY for damages of any kind.
These conditions apply to any software derived from or based on the IJG code,
not just to the unmodified library. If you use our work, you ought to
acknowledge us.
Permission is NOT granted for the use of any IJG author's name or company name
in advertising or publicity relating to this software or products derived from
it. This software may be referred to only as "the Independent JPEG Group's
software".
We specifically permit and encourage the use of this software as the basis of
commercial products, provided that all warranty or liability claims are
assumed by the product vendor.
REFERENCES
==========
We recommend reading one or more of these references before trying to
understand the innards of the JPEG software.
The best short technical introduction to the JPEG compression algorithm is
Wallace, Gregory K. "The JPEG Still Picture Compression Standard",
Communications of the ACM, April 1991 (vol. 34 no. 4), pp. 30-44.
(Adjacent articles in that issue discuss MPEG motion picture compression,
applications of JPEG, and related topics.) If you don't have the CACM issue
handy, a PDF file containing a revised version of Wallace's article is
available at http://www.ijg.org/files/Wallace.JPEG.pdf. The file (actually
a preprint for an article that appeared in IEEE Trans. Consumer Electronics)
omits the sample images that appeared in CACM, but it includes corrections
and some added material. Note: the Wallace article is copyright ACM and IEEE,
and it may not be used for commercial purposes.
A somewhat less technical, more leisurely introduction to JPEG can be found in
"The Data Compression Book" by Mark Nelson and Jean-loup Gailly, published by
M&T Books (New York), 2nd ed. 1996, ISBN 1-55851-434-1. This book provides
good explanations and example C code for a multitude of compression methods
including JPEG. It is an excellent source if you are comfortable reading C
code but don't know much about data compression in general. The book's JPEG
sample code is far from industrial-strength, but when you are ready to look
at a full implementation, you've got one here...
The best currently available description of JPEG is the textbook "JPEG Still
Image Data Compression Standard" by William B. Pennebaker and Joan L.
Mitchell, published by Van Nostrand Reinhold, 1993, ISBN 0-442-01272-1.
Price US$59.95, 638 pp. The book includes the complete text of the ISO JPEG
standards (DIS 10918-1 and draft DIS 10918-2).
The original JPEG standard is divided into two parts, Part 1 being the actual
specification, while Part 2 covers compliance testing methods. Part 1 is
titled "Digital Compression and Coding of Continuous-tone Still Images,
Part 1: Requirements and guidelines" and has document numbers ISO/IEC IS
10918-1, ITU-T T.81. Part 2 is titled "Digital Compression and Coding of
Continuous-tone Still Images, Part 2: Compliance testing" and has document
numbers ISO/IEC IS 10918-2, ITU-T T.83.
The JPEG standard does not specify all details of an interchangeable file
format. For the omitted details, we follow the "JFIF" conventions, revision
1.02. JFIF version 1 has been adopted as ISO/IEC 10918-5 (05/2013) and
Recommendation ITU-T T.871 (05/2011): Information technology - Digital
compression and coding of continuous-tone still images: JPEG File Interchange
Format (JFIF). It is available as a free download in PDF file format from
https://www.iso.org/standard/54989.html and http://www.itu.int/rec/T-REC-T.871.
A PDF file of the older JFIF 1.02 specification is available at
http://www.w3.org/Graphics/JPEG/jfif3.pdf.
The TIFF 6.0 file format specification can be obtained from
http://mirrors.ctan.org/graphics/tiff/TIFF6.ps.gz. The JPEG incorporation
scheme found in the TIFF 6.0 spec of 3-June-92 has a number of serious
problems. IJG does not recommend use of the TIFF 6.0 design (TIFF Compression
tag 6). Instead, we recommend the JPEG design proposed by TIFF Technical Note
#2 (Compression tag 7). Copies of this Note can be obtained from
http://www.ijg.org/files/. It is expected that the next revision
of the TIFF spec will replace the 6.0 JPEG design with the Note's design.
Although IJG's own code does not support TIFF/JPEG, the free libtiff library
uses our library to implement TIFF/JPEG per the Note.
ARCHIVE LOCATIONS
=================
The "official" archive site for this software is www.ijg.org.
The most recent released version can always be found there in
directory "files".
The JPEG FAQ (Frequently Asked Questions) article is a source of some
general information about JPEG. It is available at
http://www.faqs.org/faqs/jpeg-faq.
FILE FORMAT COMPATIBILITY
=========================
This software implements ITU T.81 | ISO/IEC 10918 with some extensions from
ITU T.871 | ISO/IEC 10918-5 (JPEG File Interchange Format-- see REFERENCES).
Informally, the term "JPEG image" or "JPEG file" most often refers to JFIF or
a subset thereof, but there are other formats containing the name "JPEG" that
are incompatible with the original JPEG standard or with JFIF (for instance,
JPEG 2000 and JPEG XR). This software therefore does not support these
formats. Indeed, one of the original reasons for developing this free software
was to help force convergence on a common, interoperable format standard for
JPEG files.
JFIF is a minimal or "low end" representation. TIFF/JPEG (TIFF revision 6.0 as
modified by TIFF Technical Note #2) can be used for "high end" applications
that need to record a lot of additional data about an image.
TO DO
=====
Please send bug reports, offers of help, etc. to [email protected].
+135
View File
@@ -0,0 +1,135 @@
libjpeg-turbo Licenses
======================
libjpeg-turbo is covered by two compatible BSD-style open source licenses:
- The IJG (Independent JPEG Group) License, which is listed in
[README.ijg](README.ijg)
This license applies to the libjpeg API library and associated programs,
including any code inherited from libjpeg and any modifications to that
code. Note that the libjpeg-turbo SIMD source code bears the
[zlib License](https://opensource.org/licenses/Zlib), but in the context of
the overall libjpeg API library, the terms of the zlib License are subsumed
by the terms of the IJG License.
- The Modified (3-clause) BSD License, which is listed below
This license applies to the TurboJPEG API library and associated programs, as
well as the build system. Note that the TurboJPEG API library wraps the
libjpeg API library, so in the context of the overall TurboJPEG API library,
both the terms of the IJG License and the terms of the Modified (3-clause)
BSD License apply.
Complying with the libjpeg-turbo Licenses
=========================================
This section provides a roll-up of the libjpeg-turbo licensing terms, to the
best of our understanding. This is not a license in and of itself. It is
intended solely for clarification.
1. If you are distributing a modified version of the libjpeg-turbo source,
then:
1. You cannot alter or remove any existing copyright or license notices
from the source.
**Origin**
- Clause 1 of the IJG License
- Clause 1 of the Modified BSD License
- Clauses 1 and 3 of the zlib License
2. You must add your own copyright notice to the header of each source
file you modified, so others can tell that you modified that file. (If
there is not an existing copyright header in that file, then you can
simply add a notice stating that you modified the file.)
**Origin**
- Clause 1 of the IJG License
- Clause 2 of the zlib License
3. You must include the IJG README file, and you must not alter any of the
copyright or license text in that file.
**Origin**
- Clause 1 of the IJG License
2. If you are distributing only libjpeg-turbo binaries without the source, or
if you are distributing an application that statically links with
libjpeg-turbo, then:
1. Your product documentation must include a message stating:
This software is based in part on the work of the Independent JPEG
Group.
**Origin**
- Clause 2 of the IJG license
2. If your binary distribution includes or uses the TurboJPEG API, then
your product documentation must include the text of the Modified BSD
License (see below.)
**Origin**
- Clause 2 of the Modified BSD License
3. You cannot use the name of the IJG or The libjpeg-turbo Project or the
contributors thereof in advertising, publicity, etc.
**Origin**
- IJG License
- Clause 3 of the Modified BSD License
4. The IJG and The libjpeg-turbo Project do not warrant libjpeg-turbo to be
free of defects, nor do we accept any liability for undesirable
consequences resulting from your use of the software.
**Origin**
- IJG License
- Modified BSD License
- zlib License
The Modified (3-clause) BSD License
===================================
Copyright (C) 2009-2026 D. R. Commander. All Rights Reserved.<br>
Copyright (C) 2015 Viktor Szathmáry. All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- Neither the name of the libjpeg-turbo Project nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Why Two Licenses?
=================
The zlib License could have been used instead of the Modified (3-clause) BSD
License, and since the IJG License effectively subsumes the distribution
conditions of the zlib License, this would have effectively placed
libjpeg-turbo binary distributions under the IJG License. However, the IJG
License specifically refers to the Independent JPEG Group and does not extend
attribution and endorsement protections to other entities. Thus, it was
desirable to choose a license that granted us the same protections for new code
that were granted to the IJG for code derived from their software.
+134
View File
@@ -0,0 +1,134 @@
COPYRIGHT NOTICE, DISCLAIMER, and LICENSE
=========================================
PNG Reference Library License version 2
---------------------------------------
* Copyright (c) 1995-2026 The PNG Reference Library Authors.
* Copyright (c) 2018-2026 Cosmin Truta.
* Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.
* Copyright (c) 1996-1997 Andreas Dilger.
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
The software is supplied "as is", without warranty of any kind,
express or implied, including, without limitation, the warranties
of merchantability, fitness for a particular purpose, title, and
non-infringement. In no event shall the Copyright owners, or
anyone distributing the software, be liable for any damages or
other liability, whether in contract, tort or otherwise, arising
from, out of, or in connection with the software, or the use or
other dealings in the software, even if advised of the possibility
of such damage.
Permission is hereby granted to use, copy, modify, and distribute
this software, or portions hereof, for any purpose, without fee,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you
must not claim that you wrote the original software. If you
use this software in a product, an acknowledgment in the product
documentation would be appreciated, but is not required.
2. Altered source versions must be plainly marked as such, and must
not be misrepresented as being the original software.
3. This Copyright notice may not be removed or altered from any
source or altered source distribution.
PNG Reference Library License version 1 (for libpng 0.5 through 1.6.35)
-----------------------------------------------------------------------
libpng versions 1.0.7, July 1, 2000, through 1.6.35, July 15, 2018 are
Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are
derived from libpng-1.0.6, and are distributed according to the same
disclaimer and license as libpng-1.0.6 with the following individuals
added to the list of Contributing Authors:
Simon-Pierre Cadieux
Eric S. Raymond
Mans Rullgard
Cosmin Truta
Gilles Vollant
James Yu
Mandar Sahastrabuddhe
Google Inc.
Vadim Barkov
and with the following additions to the disclaimer:
There is no warranty against interference with your enjoyment of
the library or against infringement. There is no warranty that our
efforts or the library will fulfill any of your particular purposes
or needs. This library is provided with all faults, and the entire
risk of satisfactory quality, performance, accuracy, and effort is
with the user.
Some files in the "contrib" directory and some configure-generated
files that are distributed with libpng have other copyright owners, and
are released under other open source licenses.
libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are
Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from
libpng-0.96, and are distributed according to the same disclaimer and
license as libpng-0.96, with the following individuals added to the
list of Contributing Authors:
Tom Lane
Glenn Randers-Pehrson
Willem van Schaik
libpng versions 0.89, June 1996, through 0.96, May 1997, are
Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88,
and are distributed according to the same disclaimer and license as
libpng-0.88, with the following individuals added to the list of
Contributing Authors:
John Bowler
Kevin Bracey
Sam Bushell
Magnus Holmgren
Greg Roelofs
Tom Tanner
Some files in the "scripts" directory have other copyright owners,
but are released under this license.
libpng versions 0.5, May 1995, through 0.88, January 1996, are
Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
For the purposes of this copyright and license, "Contributing Authors"
is defined as the following set of individuals:
Andreas Dilger
Dave Martindale
Guy Eric Schalnat
Paul Schmidt
Tim Wegner
The PNG Reference Library is supplied "AS IS". The Contributing
Authors and Group 42, Inc. disclaim all warranties, expressed or
implied, including, without limitation, the warranties of
merchantability and of fitness for any purpose. The Contributing
Authors and Group 42, Inc. assume no liability for direct, indirect,
incidental, special, exemplary, or consequential damages, which may
result from the use of the PNG Reference Library, even if advised of
the possibility of such damage.
Permission is hereby granted to use, copy, modify, and distribute this
source code, or portions hereof, for any purpose, without fee, subject
to the following restrictions:
1. The origin of this source code must not be misrepresented.
2. Altered versions must be plainly marked as such and must not
be misrepresented as being the original source.
3. This Copyright notice may not be removed or altered from any
source or altered source distribution.
The Contributing Authors and Group 42, Inc. specifically permit,
without fee, and encourage the use of this source code as a component
to supporting the PNG file format in commercial products. If you use
this source code in a product, acknowledgment is not required but would
be appreciated.
+114
View File
@@ -0,0 +1,114 @@
MIT License
Copyright (c) 2017 - 2025 Advanced Micro Devices, Inc. All rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
The following files
- src/include/miopen/kernel_cache.hpp
- src/kernel_cache.cpp
are licensed using the MIT license described at the top of this file in
addition to an Apache-2.0 license using the following text:
Copyright 2015 Vratis, Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
------------------------------------------------------------------------------
driver/mloSoftmaxHost.hpp is available under a BSD-2-Clause license
src/include/miopen/mlo_internal.hpp is licensed using the MIT described above
and a BSD-2-Clause license
Both files use the following license text for their BSD license text:
Copyright (c)2017 Advanced Micro Devices, Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted
provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and
the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions
and the following disclaimer in the documentation and/or
other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------------
The file src/md5.cpp is derived from a public domain implementation. The
original license text is as follows:
Author:
Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
This software was written by Alexander Peslyak in 2001. No copyright is
claimed, and the software is hereby placed in the public domain.
In case this attempt to disclaim copyright and place the software in the
public domain is deemed null and void, then the software is
Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
general public under the following terms:
Redistribution and use in source and binary forms, with or without
modification, are permitted.
There's ABSOLUTELY NO WARRANTY, express or implied.
(This is a heavily cut-down "BSD license".)
This differs from Colin Plumb's older public domain implementation in that
no exactly 32-bit integer data type is required (any 32-bit or wider
unsigned integer data type will do), there's no compile-time endianness
configuration, and the function prototypes match OpenSSL's. No code from
Colin Plumb's implementation has been reused; this comment merely compares
the properties of the two independent implementations.
The primary goals of this implementation are portability and ease of use.
It is meant to be fast, but not as fast as possible. Some known
optimizations are not included to reduce source code size and avoid
compile-time configuration.
+177
View File
@@ -0,0 +1,177 @@
Apache License
Version 2.0, January 2004
https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
+202
View File
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2026 Alibaba Cloud
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+169
View File
@@ -0,0 +1,169 @@
MIT License
Copyright (C) Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
---
This product includes software from copyright holders as shown below, and distributed under their license terms as specified.
-- Innovative Computing Laboratory
-- Electrical Engineering and Computer Science Department
-- University of Tennessee
-- (C) Copyright 2009-2020
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of the University of Tennessee, Knoxville nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
This software is provided by the copyright holders and contributors "as is" and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright holders or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage.
---
-- Copyright (c) 2014-6, The Science and Technology Facilities Council (STFC)
-- All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the STFC nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL STFC BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Copyright (c) 1992-2022 The University of Tennessee and The University
of Tennessee Research Foundation. All rights
reserved.
Copyright (c) 2000-2022 The University of California Berkeley. All
rights reserved.
Copyright (c) 2006-2022 The University of Colorado Denver. All rights
reserved.
$COPYRIGHT$
Additional copyrights may follow
$HEADER$
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer listed
in this license in the documentation and/or other materials
provided with the distribution.
- Neither the name of the copyright holders nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
The copyright holders provide no reassurances that the source code
provided does not infringe any patent, copyright, or any other
intellectual property rights of third parties. The copyright holders
disclaim any liability to any recipient for claims brought against
recipient by any third party for infringement of that parties
intellectual property rights.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---
Copyright (c) 2012-, King Abdullah University of Science and Technology
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---
Copyright (c) 2009-2014 Eric Fiselier (LLVM).
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017-2025 Advanced Micro Devices, Inc. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+7
View File
@@ -0,0 +1,7 @@
Copyright (C) 2016-2025 Advanced Micro Devices, Inc. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+23
View File
@@ -0,0 +1,23 @@
/*
* Copyright 2018-2020 Philippe Tillet
* Copyright 2020-2022 OpenAI
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-29
View File
@@ -1,29 +0,0 @@
{
"version": 1,
"skills": {
"cpp-coding-standards": {
"source": "affaan-m/ecc",
"sourceType": "github",
"skillPath": "skills/cpp-coding-standards/SKILL.md",
"computedHash": "e8c59d13fe23b088e24dcf6b77845823c5b5636b5c52434550f9b1837bc44ac3"
},
"cpp-pro": {
"source": "jeffallan/claude-skills",
"sourceType": "github",
"skillPath": "skills/cpp-pro/SKILL.md",
"computedHash": "a5632d28a479fe433c7fd9ab98903b9cd010926b3ddd73785ff0325d0277dbe6"
},
"cpp-testing": {
"source": "affaan-m/ecc",
"sourceType": "github",
"skillPath": "skills/cpp-testing/SKILL.md",
"computedHash": "fbe37695056aa5cf9454f8cf6d883f38c3bca0324ca7aeb288db2fe3b887e97d"
},
"refactoring-patterns": {
"source": "wondelai/skills",
"sourceType": "github",
"skillPath": "refactoring-patterns/SKILL.md",
"computedHash": "b896bb518a3c6233e93c3a3d319f0e98b9eb0c65813c432e4452d8287942cd1b"
}
}
}
+2 -2
View File
@@ -1396,8 +1396,8 @@ int RunBench(std::span<const char* const> args) {
<< " |\n";
std::cout << "| " << std::string(30, '-') << " | " << std::string(10, '-')
<< " | " << std::string(10, '-') << " | " << std::string(10, '-')
<< " | " << std::string(15, '-')
<< " | " << std::string(21, '-') << " |\n";
<< " | " << std::string(15, '-') << " | " << std::string(21, '-')
<< " |\n";
// Warmup run
{
+3 -4
View File
@@ -948,10 +948,9 @@ int RunServe(std::span<const char* const> args) {
llm_parser.AddOption("", "--preserve-thinking", "MODE",
"Replay prior reasoning: on, off, or auto",
"Reasoning Defaults", &preserve_thinking);
llm_parser.AddOption(
"", "--speculative", "MODE",
"HTTP draft backend: dspark, dflash2, mtp, or off",
"Speculative", &speculative_backend);
llm_parser.AddOption("", "--speculative", "MODE",
"HTTP draft backend: dspark, dflash2, mtp, or off",
"Speculative", &speculative_backend);
llm_parser.AddOption("", "--dflash-model", "PATH",
"Path to Qwen DFlash2 GGUF file", "Speculative",
&dflash_model_path);
@@ -325,8 +325,7 @@ static __global__ void GroupedCausalGqaKernel(
float* reductions =
shared_queries + static_cast<std::size_t>(grouped_heads) * head_dimension;
for (std::uint32_t grouped = 0; grouped < grouped_heads; ++grouped) {
const std::uint32_t query_head =
key_value_head * grouped_heads + grouped;
const std::uint32_t query_head = key_value_head * grouped_heads + grouped;
const std::size_t query_base =
(static_cast<std::size_t>(query_row) * query_heads + query_head) *
head_dimension;
@@ -334,8 +333,8 @@ static __global__ void GroupedCausalGqaKernel(
dimension += blockDim.x) {
shared_queries[static_cast<std::size_t>(grouped) * head_dimension +
dimension] =
Bf16ToFloat(FloatToBf16(
Bf16ToFloat(query[query_base + dimension]) * scale));
Bf16ToFloat(
FloatToBf16(Bf16ToFloat(query[query_base + dimension]) * scale));
}
}
__syncthreads();
@@ -385,8 +384,7 @@ static __global__ void GroupedCausalGqaKernel(
for (std::uint32_t stride = kLogicalLanes / 2U; stride != 0;
stride >>= 1U) {
if (lane < stride) {
reductions[lane] =
fmaxf(reductions[lane], reductions[lane + stride]);
reductions[lane] = fmaxf(reductions[lane], reductions[lane + stride]);
}
__syncthreads();
}
@@ -434,10 +432,9 @@ static __global__ void GroupedCausalGqaKernel(
dimension;
const float value_element = Bf16ToFloat(value[value_index]);
for (std::uint32_t grouped = 0; grouped < grouped_heads; ++grouped) {
sums[grouped] =
fmaf(scores[static_cast<std::size_t>(grouped) * sequence +
key_row],
value_element, sums[grouped]);
sums[grouped] = fmaf(
scores[static_cast<std::size_t>(grouped) * sequence + key_row],
value_element, sums[grouped]);
}
}
for (std::uint32_t grouped = 0; grouped < grouped_heads; ++grouped) {
@@ -468,11 +465,12 @@ inline void LaunchCausalGqa(const std::uint16_t* query,
sizeof(float);
constexpr std::size_t kMaximumGroupedSharedBytes = 48U << 10U;
if (key_value_heads != 0 && query_heads % key_value_heads == 0 &&
grouped_heads <= 8 && grouped_shared_bytes <= kMaximumGroupedSharedBytes) {
hipLaunchKernelGGL(
GroupedCausalGqaKernel, dim3(sequence, key_value_heads), dim3(64),
grouped_shared_bytes, stream, query, key, value, output, sequence,
query_heads, key_value_heads, head_dimension, scale);
grouped_heads <= 8 &&
grouped_shared_bytes <= kMaximumGroupedSharedBytes) {
hipLaunchKernelGGL(GroupedCausalGqaKernel, dim3(sequence, key_value_heads),
dim3(64), grouped_shared_bytes, stream, query, key,
value, output, sequence, query_heads, key_value_heads,
head_dimension, scale);
return;
}
const std::size_t shared_bytes =
+2 -3
View File
@@ -61,9 +61,8 @@ public:
for (std::uint32_t length = 1; length <= cap; ++length) {
survival *= probability;
expected_tokens += survival;
const float cost = q8_target_
? 1.0F + 0.02F * static_cast<float>(length)
: kQ4RelativeCost[length - 1];
const float cost = q8_target_ ? 1.0F + 0.02F * static_cast<float>(length)
: kQ4RelativeCost[length - 1];
const float score = expected_tokens / cost;
if (score > best_score) {
best_score = score;
@@ -10,10 +10,13 @@
namespace gufo::hip::detail {
// Call only after the convolution launch has finished reading the old history.
// Each thread owns one channel; load the short-batch tail before overwriting it.
__device__ __forceinline__ void StoreSSMConvHistory(
const float* input, float* state, std::size_t channel,
std::size_t batch_size, std::size_t qkv_size) {
// Each thread owns one channel; load the short-batch tail before overwriting
// it.
__device__ __forceinline__ void StoreSSMConvHistory(const float* input,
float* state,
std::size_t channel,
std::size_t batch_size,
std::size_t qkv_size) {
float tail[4];
#pragma unroll
for (std::size_t j = 0; j < 4; ++j) {
@@ -34,10 +34,14 @@ __launch_bounds__(Resident ? 128 : 1024, 1) __global__
rows = sequence.rows;
const std::size_t offset = sequence.row_offset;
conv_out += offset * conv_row_stride;
if (alpha_buf) alpha_buf += offset * projection_row_stride;
if (beta_buf) beta_buf += offset * projection_row_stride;
if (gate) gate += offset * inner_row_stride;
if (out_buf) out_buf += offset * inner_row_stride;
if (alpha_buf)
alpha_buf += offset * projection_row_stride;
if (beta_buf)
beta_buf += offset * projection_row_stride;
if (gate)
gate += offset * inner_row_stride;
if (out_buf)
out_buf += offset * inner_row_stride;
}
static_assert(!Resident || std::is_same_v<StateT, float>);
const std::uint32_t key_dim = Resident ? 128 : key_dimension;
+4 -7
View File
@@ -41,13 +41,10 @@ void LaunchBatchedQuantGEMMResidualFp16(core::GgmlType type,
// Qualified gate/up weight pairs share a kernel that emits FP16 SwiGLU
// directly. Returns false without launching for other pairs.
// All buffers are disjoint; the output may reuse the dead FP32 up allocation.
bool TryLaunchBatchedDualQuantGEMMSwiGLUFp16(core::GgmlType gate_type,
core::GgmlType up_type,
const void* gate_weights,
const void* up_weights,
const void* input, void* output,
std::size_t batch, std::size_t m,
std::size_t k, hipStream_t stream);
bool TryLaunchBatchedDualQuantGEMMSwiGLUFp16(
core::GgmlType gate_type, core::GgmlType up_type, const void* gate_weights,
const void* up_weights, const void* input, void* output, std::size_t batch,
std::size_t m, std::size_t k, hipStream_t stream);
} // namespace gufo::hip
#endif
#endif
+4 -4
View File
@@ -83,10 +83,10 @@ void LaunchSSMConvRecurrenceRows(
void LaunchSSMConvRecurrenceBatch(
const float* qkv_in, const float* conv_weights, float* conv_out,
const float* alpha_buf, const float* beta_buf, const float* ssm_a,
const float* ssm_dt, const float* ssm_norm, const float* gate, float* out_buf,
std::span<const SsmSequenceState> sequences, std::uint32_t layer_idx,
std::size_t qkv_size, std::uint32_t num_key_heads, std::uint32_t num_heads,
std::uint32_t key_dim, std::uint32_t val_dim,
const float* ssm_dt, const float* ssm_norm, const float* gate,
float* out_buf, std::span<const SsmSequenceState> sequences,
std::uint32_t layer_idx, std::size_t qkv_size, std::uint32_t num_key_heads,
std::uint32_t num_heads, std::uint32_t key_dim, std::uint32_t val_dim,
std::size_t projection_row_stride, std::size_t inner_row_stride,
hipStream_t stream = nullptr,
QwenRecurrentStateStorage state_storage = QwenRecurrentStateStorage::kFp32);
+3 -3
View File
@@ -27,9 +27,9 @@ void LaunchBatchedSwiGLUActivation(const float* gate, const float* up,
/// SwiGLU for adjacent gate/up rows stored as [batch, 2, intermediate_size].
void LaunchPackedSwiGLUActivation(const float* gate_up, float* out,
std::size_t batch_size,
std::size_t intermediate_size,
hipStream_t stream = nullptr);
std::size_t batch_size,
std::size_t intermediate_size,
hipStream_t stream = nullptr);
/// Exact projection and SwiGLU for adjacent packed gate/up weights.
/// Returns false without launching when the shape has no qualified fused route.
+4 -5
View File
@@ -581,11 +581,10 @@ tokenization::TokenId QwenGpuExecutor::ForwardPromptChunk(
layer.ffn_gate.type, batch_size, intermediate_size,
batch_size * intermediate_size * sizeof(float));
if (half_prefill) {
const bool paired =
TryLaunchBatchedDualQuantGEMMSwiGLUFp16(
layer.ffn_gate.type, layer.ffn_up.type, layer.ffn_gate.data,
layer.ffn_up.data, arena_.d_scratch_bf16, arena_.d_ffn_up,
batch_size, intermediate_size, hidden_size, arena_.stream);
const bool paired = TryLaunchBatchedDualQuantGEMMSwiGLUFp16(
layer.ffn_gate.type, layer.ffn_up.type, layer.ffn_gate.data,
layer.ffn_up.data, arena_.d_scratch_bf16, arena_.d_ffn_up,
batch_size, intermediate_size, hidden_size, arena_.stream);
if (!paired) {
gemm_weight(layer.ffn_gate, arena_.d_scratch_bf16, arena_.d_normed,
arena_.d_ffn_gate, intermediate_size, hidden_size);
+2 -2
View File
@@ -218,8 +218,8 @@ __device__ inline void GetQKScaleMin(std::size_t index,
const unsigned upper_scale = (high & 15U) | ((low >> 6U) << 4U);
const unsigned lower_minimum = middle & 63U;
const unsigned upper_minimum = (high >> 4U) | ((middle >> 6U) << 4U);
sc = static_cast<std::uint8_t>(
lower_scale ^ ((lower_scale ^ upper_scale) & upper_mask));
sc = static_cast<std::uint8_t>(lower_scale ^
((lower_scale ^ upper_scale) & upper_mask));
m = static_cast<std::uint8_t>(
lower_minimum ^ ((lower_minimum ^ upper_minimum) & upper_mask));
return;
+6 -7
View File
@@ -34,14 +34,13 @@ void CopyRecurrentState(void* live, void* packed, std::size_t layer_bytes,
const std::size_t packed_pitch = (interval - 1) * layer_bytes;
const std::size_t destination_pitch = save ? packed_pitch : live_pitch;
const std::size_t source_pitch = save ? live_pitch : packed_pitch;
if (layer_bytes >= 1024 * 1024 &&
((live_pitch | packed_pitch) & 15U) == 0 &&
if (layer_bytes >= 1024 * 1024 && ((live_pitch | packed_pitch) & 15U) == 0 &&
groups <= std::numeric_limits<std::uint32_t>::max() / live_pitch) {
LaunchCopyRecurrentStateRows(
destination, source, static_cast<std::uint32_t>(packed_pitch),
static_cast<std::uint32_t>(destination_pitch),
static_cast<std::uint32_t>(source_pitch),
static_cast<std::uint32_t>(groups), stream);
LaunchCopyRecurrentStateRows(destination, source,
static_cast<std::uint32_t>(packed_pitch),
static_cast<std::uint32_t>(destination_pitch),
static_cast<std::uint32_t>(source_pitch),
static_cast<std::uint32_t>(groups), stream);
} else {
HIP_CHECK(hipMemcpy2DAsync(destination, destination_pitch, source,
source_pitch, packed_pitch, groups,
@@ -68,10 +68,10 @@ private:
Session() = default;
struct LinearState {
float* conv_state{nullptr}; ///< [kernel-1][channels]
float* state{nullptr}; ///< [v_heads][d][d]
RollbackRows conv_snapshots; ///< [max_spec-1][kernel-1][channels]
RollbackRows state_snapshots; ///< [max_spec-1][v_heads][d][d]
float* conv_state{nullptr}; ///< [kernel-1][channels]
float* state{nullptr}; ///< [v_heads][d][d]
RollbackRows conv_snapshots; ///< [max_spec-1][kernel-1][channels]
RollbackRows state_snapshots; ///< [max_spec-1][v_heads][d][d]
};
struct AttentionState {
const qwen::vision::DeviceRope* rope{nullptr};
@@ -110,8 +110,8 @@ private:
std::uint32_t position_{0};
std::vector<LinearState> linear_;
std::vector<AttentionState> attention_;
float* ple_history_{nullptr}; ///< [PleConvHistory()][hc_dim]
RollbackRows ple_snapshots_; ///< [max_spec-1][PleConvHistory()][hc_dim]
float* ple_history_{nullptr}; ///< [PleConvHistory()][hc_dim]
RollbackRows ple_snapshots_; ///< [max_spec-1][PleConvHistory()][hc_dim]
NgramHistory ngram_;
std::vector<NgramHistory> ngram_snapshots_;
std::uint32_t spec_base_{0}; ///< position before the speculative batch
+1 -1
View File
@@ -114,7 +114,7 @@ struct LayerWeights {
TensorRef shexp_down; ///< [shared_ff -> hidden]
// Speculative `nextn` block only.
TensorRef nextn_enorm; ///< [hidden]
TensorRef nextn_enorm; ///< [hidden]
TensorRef nextn_hnorm; ///< [hc_dim], one RMS denominator across all streams
TensorRef nextn_eh_proj; ///< GGUF rows: [fc_embedding | fc_hidden]
HcMixer nextn_head; ///< Output mixer of the draft block.
-1
View File
@@ -9,7 +9,6 @@ add_library(gufo_qwen3_tts STATIC
config.cpp
loader.cpp
prompt.cpp
reference_runner.cpp
tensor.cpp
tokenizer.cpp
hip/encoder_support.cpp
-312
View File
@@ -1,312 +0,0 @@
#include "src/models/qwen3_tts/reference_runner.hpp"
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <algorithm>
#include <array>
#include <cerrno>
#include <chrono>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <iterator>
#include <limits>
#include <stdexcept>
#include <string>
#include <thread>
#include <utility>
#include <vector>
#include "src/core/json.hpp"
namespace gufo::models::qwen3_tts {
namespace {
void SetError(std::string* error, std::string message) {
if (error != nullptr) {
*error = std::move(message);
}
}
bool ContainsNull(std::string_view value) {
return value.find('\0') != std::string_view::npos;
}
class TemporaryDirectory {
public:
TemporaryDirectory() {
std::array<char, 64> pattern{};
const std::string prefix = "/tmp/gufo-qwen3-tts-XXXXXX";
std::copy(prefix.begin(), prefix.end(), pattern.begin());
if (const char* created = mkdtemp(pattern.data()); created != nullptr) {
path_ = created;
}
}
~TemporaryDirectory() {
if (!path_.empty()) {
std::error_code ignored;
std::filesystem::remove_all(path_, ignored);
}
}
TemporaryDirectory(const TemporaryDirectory&) = delete;
TemporaryDirectory& operator=(const TemporaryDirectory&) = delete;
TemporaryDirectory(TemporaryDirectory&&) = delete;
TemporaryDirectory& operator=(TemporaryDirectory&&) = delete;
[[nodiscard]] const std::filesystem::path& path() const noexcept {
return path_;
}
private:
std::filesystem::path path_;
};
std::string ReadTextFile(const std::filesystem::path& path,
std::size_t maximum_bytes) {
std::error_code error;
const std::uintmax_t size = std::filesystem::file_size(path, error);
if (error || size > maximum_bytes) {
return {};
}
std::ifstream input(path, std::ios::binary);
return std::string(std::istreambuf_iterator<char>(input),
std::istreambuf_iterator<char>());
}
template<typename Element>
bool ReadBinaryFile(const std::filesystem::path& path,
std::size_t element_count, std::vector<Element>* output) {
if (output == nullptr ||
element_count >
std::numeric_limits<std::size_t>::max() / sizeof(Element)) {
return false;
}
const std::size_t bytes = element_count * sizeof(Element);
std::error_code error;
if (std::filesystem::file_size(path, error) != bytes || error) {
return false;
}
output->resize(element_count);
std::ifstream input(path, std::ios::binary);
input.read(reinterpret_cast<char*>(output->data()),
static_cast<std::streamsize>(bytes));
return input.good() || input.eof();
}
std::vector<std::string> BuildArguments(
const OfficialReferenceOptions& options, const SynthesisRequest& request,
const std::filesystem::path& output_directory) {
std::vector<std::string> arguments{
options.python_executable.string(),
options.runner_script.string(),
"--reference-root",
options.reference_root.string(),
"--model",
options.model_root.string(),
"--out",
output_directory.string(),
"--text",
request.text,
"--speaker",
request.speaker,
"--language",
request.language,
"--max-new-tokens",
std::to_string(request.max_new_tokens),
"--seed",
std::to_string(request.seed),
};
if (!request.instruct.empty()) {
arguments.emplace_back("--instruct");
arguments.push_back(request.instruct);
}
if (request.greedy) {
arguments.emplace_back("--greedy");
}
return arguments;
}
int WaitForChild(pid_t child,
const OfficialReferenceRunner::CancellationCheck& cancelled) {
while (true) {
int status = 0;
const pid_t waited = waitpid(child, &status, WNOHANG);
if (waited == child) {
return status;
}
if (waited < 0 && errno != EINTR) {
return -1;
}
if (cancelled && cancelled()) {
(void)kill(child, SIGTERM);
const auto deadline =
std::chrono::steady_clock::now() + std::chrono::seconds(2);
while (std::chrono::steady_clock::now() < deadline) {
const pid_t terminated = waitpid(child, &status, WNOHANG);
if (terminated == child) {
return status;
}
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
(void)kill(child, SIGKILL);
(void)waitpid(child, &status, 0);
return status;
}
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
}
bool ChildSucceeded(int status) {
return status >= 0 && WIFEXITED(status) && WEXITSTATUS(status) == 0;
}
} // namespace
OfficialReferenceRunner::OfficialReferenceRunner(
OfficialReferenceOptions options)
: options_(std::move(options)) {}
bool OfficialReferenceRunner::Validate(std::string* error) const {
if (!std::filesystem::is_directory(options_.model_root)) {
SetError(error, "Qwen3-TTS model directory does not exist");
return false;
}
if (!std::filesystem::is_regular_file(options_.model_root /
"model.safetensors") ||
!std::filesystem::is_regular_file(
options_.model_root / "speech_tokenizer" / "model.safetensors")) {
SetError(error, "Qwen3-TTS model weights are incomplete");
return false;
}
if (!std::filesystem::is_directory(options_.reference_root / "qwen_tts")) {
SetError(error, "official Qwen3-TTS source directory is invalid");
return false;
}
if (!std::filesystem::is_regular_file(options_.python_executable)) {
SetError(error, "official Qwen3-TTS Python executable does not exist");
return false;
}
if (!std::filesystem::is_regular_file(options_.runner_script)) {
SetError(error, "Qwen3-TTS reference runner script does not exist");
return false;
}
return true;
}
bool OfficialReferenceRunner::Generate(const SynthesisRequest& request,
const CancellationCheck& is_cancelled,
SynthesisResult* result,
std::string* error) const {
if (result == nullptr) {
SetError(error, "Qwen3-TTS reference result must not be null");
return false;
}
*result = {};
if (request.text.empty() || request.speaker.empty() ||
request.language.empty() || request.max_new_tokens == 0) {
SetError(error, "Qwen3-TTS reference request is incomplete");
return false;
}
if (ContainsNull(request.text) || ContainsNull(request.speaker) ||
ContainsNull(request.language) || ContainsNull(request.instruct)) {
SetError(error, "Qwen3-TTS reference arguments must not contain NUL");
return false;
}
if (!Validate(error)) {
return false;
}
if (is_cancelled && is_cancelled()) {
SetError(error, "Qwen3-TTS reference generation cancelled");
return false;
}
const TemporaryDirectory temporary;
if (temporary.path().empty()) {
SetError(error, "cannot create Qwen3-TTS reference output directory");
return false;
}
const std::filesystem::path log_path = temporary.path() / "reference.log";
const int log = open(log_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (log < 0) {
SetError(error, "cannot create Qwen3-TTS reference log");
return false;
}
std::vector<std::string> arguments =
BuildArguments(options_, request, temporary.path());
std::vector<char*> raw_arguments;
raw_arguments.reserve(arguments.size() + 1);
for (std::string& argument : arguments) {
raw_arguments.push_back(argument.data());
}
raw_arguments.push_back(nullptr);
const pid_t child = fork();
if (child == 0) {
(void)dup2(log, STDOUT_FILENO);
(void)dup2(log, STDERR_FILENO);
close(log);
execv(raw_arguments.front(), raw_arguments.data());
_exit(127);
}
close(log);
if (child < 0) {
SetError(error, "cannot start official Qwen3-TTS reference process");
return false;
}
const int status = WaitForChild(child, is_cancelled);
if (!ChildSucceeded(status)) {
std::string message;
if (is_cancelled && is_cancelled()) {
message = "Qwen3-TTS reference generation cancelled";
} else {
message = ReadTextFile(log_path, 64U << 10U);
}
if (message.empty()) {
message = "official Qwen3-TTS reference process failed";
}
SetError(error, std::move(message));
return false;
}
const std::string metadata_text =
ReadTextFile(temporary.path() / "meta.json", 1U << 20U);
try {
const json::Value metadata = json::parse(metadata_text);
const std::size_t sample_count = metadata.member_size("samples");
const std::size_t code_steps = metadata.member_size("code_steps");
const std::size_t code_groups = metadata.member_size("code_groups");
const std::size_t sample_rate = metadata.member_size("sample_rate");
if (sample_count == 0 || code_steps == 0 || code_groups == 0 ||
sample_rate == 0 ||
sample_rate > std::numeric_limits<std::uint32_t>::max() ||
code_groups > std::numeric_limits<std::uint32_t>::max() ||
code_steps > std::numeric_limits<std::size_t>::max() / code_groups) {
throw std::runtime_error("invalid reference output dimensions");
}
SynthesisResult generated;
generated.sample_rate = static_cast<std::uint32_t>(sample_rate);
generated.code_groups = static_cast<std::uint32_t>(code_groups);
if (!ReadBinaryFile(temporary.path() / "waveform.f32", sample_count,
&generated.samples) ||
!ReadBinaryFile(temporary.path() / "codes.i32",
code_steps * code_groups, &generated.codes)) {
throw std::runtime_error("invalid reference output payload");
}
*result = std::move(generated);
return true;
} catch (const std::exception& exception) {
SetError(error, std::string("cannot read Qwen3-TTS reference output: ") +
exception.what());
return false;
}
}
} // namespace gufo::models::qwen3_tts
-43
View File
@@ -1,43 +0,0 @@
#ifndef GUFO_MODELS_QWEN3_TTS_REFERENCE_RUNNER_HPP_
#define GUFO_MODELS_QWEN3_TTS_REFERENCE_RUNNER_HPP_
#include <cstddef>
#include <filesystem>
#include <string>
#include "src/models/qwen3_tts/synthesis.hpp"
namespace gufo::models::qwen3_tts {
struct OfficialReferenceOptions {
std::filesystem::path model_root;
std::filesystem::path reference_root;
std::filesystem::path python_executable;
std::filesystem::path runner_script;
};
/// Runs the pinned official Qwen3-TTS implementation as an intentionally slow
/// process-isolated oracle. This is a validation backend, not the production
/// HIP implementation.
class OfficialReferenceRunner {
public:
using CancellationCheck = ::gufo::models::qwen3_tts::CancellationCheck;
explicit OfficialReferenceRunner(OfficialReferenceOptions options);
[[nodiscard]] bool Validate(std::string* error = nullptr) const;
[[nodiscard]] bool Generate(const SynthesisRequest& request,
const CancellationCheck& is_cancelled,
SynthesisResult* result,
std::string* error = nullptr) const;
[[nodiscard]] const OfficialReferenceOptions& options() const noexcept {
return options_;
}
private:
OfficialReferenceOptions options_;
};
} // namespace gufo::models::qwen3_tts
#endif // GUFO_MODELS_QWEN3_TTS_REFERENCE_RUNNER_HPP_
+4 -4
View File
@@ -31,8 +31,8 @@ void TestDefaultOptions() {
}
void TestDs4SamplingOptions() {
const std::array<const char*, 4> args = {
"--temperature", "0.6", "--seed", "7"};
const std::array<const char*, 4> args = {"--temperature", "0.6", "--seed",
"7"};
const auto options = gufo::cli::ParseBenchOptions(args);
Expect(options && options->sampling.temperature == 0.6F &&
options->sampling.seed == 7,
@@ -114,8 +114,8 @@ void TestInvalidDepth() {
Expect(!gufo::cli::ParseBenchOptions(range_args, &error).has_value(),
"invalid draft range rejected");
const std::array<const char*, 4> unsupported_floor = {"--speculative", "dflash2",
"--min-draft-tokens", "2"};
const std::array<const char*, 4> unsupported_floor = {
"--speculative", "dflash2", "--min-draft-tokens", "2"};
Expect(!gufo::cli::ParseBenchOptions(unsupported_floor, &error).has_value() &&
error.find("min-draft-tokens") != std::string::npos,
"DFlash2 rejects an unsupported minimum draft length");
@@ -113,7 +113,6 @@ int main(int argc, char** argv) {
CheckBackend({"--speculative", "off", "--dspark-model", "support.gguf"}, "");
CheckBackend({"--dspark-model", "support.gguf", "--speculative", "off"}, "");
const char* args[] = {"--dspark-model", "support.gguf", "--draft-tokens",
"3"};
const auto prompt = gufo::cli::ParsePromptOptions(args);
+6 -9
View File
@@ -36,15 +36,12 @@ QwenGemmRoute Route(GgmlType type, std::size_t m, std::size_t k,
void TestFormatCapabilities() {
constexpr std::array all_types{
GgmlType::kF32, GgmlType::kF16,
GgmlType::kQ4_0, GgmlType::kQ4_1,
GgmlType::kQ5_0, GgmlType::kQ5_1,
GgmlType::kQ8_0, GgmlType::kQ8_1,
GgmlType::kQ2_K, GgmlType::kQ3_K,
GgmlType::kQ4_K, GgmlType::kQ5_K,
GgmlType::kQ6_K, GgmlType::kQ8_K,
GgmlType::kIQ2_XXS, GgmlType::kIQ4_NL,
GgmlType::kIQ3_S, GgmlType::kIQ4_XS,
GgmlType::kF32, GgmlType::kF16, GgmlType::kQ4_0,
GgmlType::kQ4_1, GgmlType::kQ5_0, GgmlType::kQ5_1,
GgmlType::kQ8_0, GgmlType::kQ8_1, GgmlType::kQ2_K,
GgmlType::kQ3_K, GgmlType::kQ4_K, GgmlType::kQ5_K,
GgmlType::kQ6_K, GgmlType::kQ8_K, GgmlType::kIQ2_XXS,
GgmlType::kIQ4_NL, GgmlType::kIQ3_S, GgmlType::kIQ4_XS,
GgmlType::kBF16,
};
for (const auto type : all_types) {
+33 -26
View File
@@ -44,7 +44,8 @@ void TestConcurrentSsmRecurrence() {
constexpr std::size_t qkv = 2 * key_heads * dim + inner;
constexpr std::size_t conv_per_sequence = layers * qkv * 4;
constexpr std::size_t state_per_sequence = layers * heads * dim * dim;
constexpr std::size_t replay_qkv = layers * gufo::hip::kSsmReplayCapacity * qkv;
constexpr std::size_t replay_qkv =
layers * gufo::hip::kSsmReplayCapacity * qkv;
constexpr std::size_t replay_control =
layers * gufo::hip::kSsmReplayCapacity * heads;
const auto values = [](std::size_t size, float phase) {
@@ -74,16 +75,18 @@ void TestConcurrentSsmRecurrence() {
enabled[i] = i % 2;
offset += i + 1;
}
DeviceBuffer<std::uint32_t> device_positions(positions), device_enabled(enabled);
DeviceBuffer<std::uint32_t> device_positions(positions),
device_enabled(enabled);
const auto clear = [](auto& buffer) {
HIP_CHECK(hipMemset(buffer.data(), 0,
buffer.size() * sizeof(*buffer.data())));
HIP_CHECK(
hipMemset(buffer.data(), 0, buffer.size() * sizeof(*buffer.data())));
};
const auto expect_equal = [](const auto& expected, const auto& actual) {
if (expected.size() != actual.size() ||
std::memcmp(expected.data(), actual.data(),
expected.size() * sizeof(expected[0])) != 0)
throw std::runtime_error("concurrent SSM changed output, state or replay");
throw std::runtime_error(
"concurrent SSM changed output, state or replay");
};
for (const auto storage : {Storage::kFp32, Storage::kBf16}) {
const std::size_t element_bytes =
@@ -95,25 +98,27 @@ void TestConcurrentSsmRecurrence() {
element_bytes);
} else {
const auto bits = gufo::test::FloatToBf16Bits(initial_state[i]);
std::memcpy(state_bytes.data() + i * element_bytes, &bits, element_bytes);
std::memcpy(state_bytes.data() + i * element_bytes, &bits,
element_bytes);
}
}
DeviceBuffer<std::uint8_t> recurrent(state_bytes);
for (std::size_t i = 0; i < count; ++i) {
auto& sequence = sequences[i];
sequence.conv = conv_state.data() + i * conv_per_sequence;
sequence.recurrent = recurrent.data() + i * state_per_sequence * element_bytes;
sequence.replay = {
captured_qkv.data() + i * replay_qkv,
captured_alpha.data() + i * replay_control,
captured_beta.data() + i * replay_control,
device_positions.data() + sequence.row_offset, device_enabled.data() + i};
sequence.recurrent =
recurrent.data() + i * state_per_sequence * element_bytes;
sequence.replay = {captured_qkv.data() + i * replay_qkv,
captured_alpha.data() + i * replay_control,
captured_beta.data() + i * replay_control,
device_positions.data() + sequence.row_offset,
device_enabled.data() + i};
}
const auto reset = [&] {
conv_state.CopyFrom(initial_conv);
recurrent.CopyFrom(state_bytes);
for (auto* buffer : {&conv_output, &output, &captured_qkv,
&captured_alpha, &captured_beta})
&captured_alpha, &captured_beta})
clear(*buffer);
};
for (const std::size_t width : {1U, 2U, 4U, 6U, 8U}) {
@@ -125,8 +130,8 @@ void TestConcurrentSsmRecurrence() {
input.data() + row * qkv, weights.data(), sequence.conv,
conv_output.data() + row * qkv, sequence.recurrent,
controls.data() + row * heads * 2,
controls.data() + row * heads * 2 + heads, decay.data(), dt.data(),
norm.data(), gate.data() + row * inner,
controls.data() + row * heads * 2 + heads, decay.data(),
dt.data(), norm.data(), gate.data() + row * inner,
write_output ? output.data() + row * inner : nullptr, layer, qkv,
key_heads, heads, dim, dim, sequence.rows, heads * 2, inner,
nullptr, sequence.replay, storage);
@@ -134,7 +139,7 @@ void TestConcurrentSsmRecurrence() {
const auto expected_state = recurrent.CopyToHost();
std::vector<std::vector<float>> expected;
for (auto* buffer : {&conv_state, &conv_output, &output, &captured_qkv,
&captured_alpha, &captured_beta})
&captured_alpha, &captured_beta})
expected.push_back(buffer->CopyToHost());
reset();
gufo::hip::LaunchSSMConvRecurrenceBatch(
@@ -146,12 +151,13 @@ void TestConcurrentSsmRecurrence() {
expect_equal(expected_state, recurrent.CopyToHost());
std::size_t index = 0;
for (auto* buffer : {&conv_state, &conv_output, &output, &captured_qkv,
&captured_alpha, &captured_beta})
&captured_alpha, &captured_beta})
expect_equal(expected[index++], buffer->CopyToHost());
}
}
}
std::cout << "Concurrent SSM: FP32/BF16 state, ragged rows and replay exact\n";
std::cout
<< "Concurrent SSM: FP32/BF16 state, ragged rows and replay exact\n";
}
void TestRecurrentRollbackRows(bool large_state) {
@@ -312,7 +318,8 @@ void TestBf16RecurrentMemoryAndSnapshot() {
}
// Independent causal-convolution oracle, including nonzero carried history.
// Only short batches need scalar output checks; all batches check the exact tail.
// Only short batches need scalar output checks; all batches check the exact
// tail.
void CheckSSMConvolution(const std::vector<float>& input,
const std::vector<float>& weights,
const std::vector<float>& history,
@@ -327,7 +334,8 @@ void CheckSSMConvolution(const std::vector<float>& input,
? history[(c * 4) + batch + j]
: input[((batch + j - 4) * channels) + c];
if (std::memcmp(&expected, &state[(c * 4) + j], sizeof(float)) != 0) {
throw std::runtime_error("convolution history differs from causal tail");
throw std::runtime_error(
"convolution history differs from causal tail");
}
}
}
@@ -341,9 +349,8 @@ void CheckSSMConvolution(const std::vector<float>& input,
for (std::size_t c = 0; c < channels; ++c) {
double dot = 0.0;
for (std::size_t j = 0; j < 4; ++j) {
const float value = t + j < 3
? history[(c * 4) + t + j + 1]
: input[((t + j - 3) * channels) + c];
const float value = t + j < 3 ? history[(c * 4) + t + j + 1]
: input[((t + j - 3) * channels) + c];
dot += static_cast<double>(value) * weights[(c * 4) + j];
}
const double expected = dot / (1.0 + std::exp(-dot));
@@ -736,11 +743,11 @@ void TestBatchedSSMRowSplitRecurrenceEquivalence(std::size_t batch) {
HIP_CHECK(hipDeviceSynchronize());
CheckSSMConvolution(h_qkv, h_weights, h_history, d_conv_ref, d_state_ref,
batch, qkv_dim);
batch, qkv_dim);
CheckSSMConvolution(h_qkv, h_weights, h_history, d_conv_new, d_state_new,
batch, qkv_dim);
batch, qkv_dim);
CheckSSMConvolution(h_qkv, h_weights, h_history, d_conv_bf16, d_state_bf16,
batch, qkv_dim);
batch, qkv_dim);
const auto download = [](std::vector<float>& dst, const float* src) {
HIP_CHECK(hipMemcpy(dst.data(), src, dst.size() * sizeof(float),
@@ -326,14 +326,14 @@ int main() {
CheckPooling(29, 16); // batch wraps the raw-key ring
CheckPooling(65533, 16); // many wraps; absolute rotary position retained
bool ok = true;
ok = Run(100, 20000, 0x1234ABCDU) && ok; // deep, ragged group
ok = Run(100, 20000, 0x1234ABCDU) && ok; // deep, ragged group
ok = Run(7, 131069, 0x2468ACE0U) && ok;
ok = Run(257, 131069, 0xC0FFEE01U, false, 0, true, ScoreLayout::kAligned) &&
ok;
ok = Run(129, 131069, 0, true, 0, true) && ok; // deep, partial word
ok = Run(1, 9001, 0x0BADF00DU) && ok; // decode
ok = Run(40, 2040, 0xDEADBEEFU) && ok; // straddles the budget
ok = Run(3, 6000, 0x5EED5EEDU, true) && ok; // all scores tie at zero
ok = Run(1, 9001, 0x0BADF00DU) && ok; // decode
ok = Run(40, 2040, 0xDEADBEEFU) && ok; // straddles the budget
ok = Run(3, 6000, 0x5EED5EEDU, true) && ok; // all scores tie at zero
// A threshold tie group that fits exactly, then one that needs the
// lowest-index prefix. Both must produce the same CPU-sorted mask.
ok = Run(1, 9001, 0, false, kBudget) && ok;
@@ -37,8 +37,7 @@ Decoded Decode(qfn::Session& session, std::size_t count,
sampler, &step, &error, false),
error);
Require(!step.tokens.empty(), "empty decode step");
out.tokens.insert(out.tokens.end(), step.tokens.begin(),
step.tokens.end());
out.tokens.insert(out.tokens.end(), step.tokens.begin(), step.tokens.end());
}
const auto after = session.Statistics();
out.stats = {after.cycles - before.cycles, after.drafted - before.drafted,
@@ -81,11 +80,11 @@ int main(int argc, char** argv) {
try {
std::string error;
constexpr std::uint32_t kContext = 8192;
auto model = qfn::Model::Load(
argv[2],
{.max_context = kContext, .mtp_model_path = argv[4],
.max_draft_tokens = 7},
&error);
auto model = qfn::Model::Load(argv[2],
{.max_context = kContext,
.mtp_model_path = argv[4],
.max_draft_tokens = 7},
&error);
Require(model != nullptr, error);
const auto pattern = model->Tokenize(
"The quick brown fox jumps over the lazy dog. "
@@ -131,8 +130,8 @@ int main(int argc, char** argv) {
start = std::chrono::steady_clock::now();
Require(restored->RestoreSnapshot(*at_prompt, &error), error);
std::cout << "restore_ms=" << Millis(start) << "\n";
Require(std::equal(prompt.begin(), prompt.end(),
restored->Tokens().begin(), restored->Tokens().end()),
Require(std::equal(prompt.begin(), prompt.end(), restored->Tokens().begin(),
restored->Tokens().end()),
"restored tokens differ");
Require(restored->Position() == prompt.size(), "restored position");
Require(std::memcmp(prompt_logits.data(), restored->Logits().data(),
+4 -1
View File
@@ -1,7 +1,10 @@
# Developer tools
Use the Nix development environment for these tools. Production inference and
speed measurements use `nix build` binaries under `result/bin`.
speed measurements use `nix build` binaries under `result/bin` or the
`release` CMake preset with the same toolchain. Tools are not part of the
production install. `gpu-test` enables `GUFO_BUILD_TOOLS` for the compiled
benchmark/tuning executables; Python tools run from this source tree.
| Directory | Purpose |
| --- | --- |
+17 -4
View File
@@ -3,8 +3,9 @@
check-dependencies.py — Third-party dependency inventory consistency checker for gufo
Validates that:
1. Every shipped/linked runtime dependency in Nix/CMake is documented in THIRD_PARTY_NOTICES.md and NOTICE.
2. SPDX license identifiers and pinned versions are valid and present.
1. The declared direct-dependency inventory includes required components.
2. License identifiers and version/source records are present.
This is a consistency check, not a legal compliance determination.
3. Emits a deterministic machine-readable dependency inventory report.
"""
@@ -21,7 +22,17 @@ REQUIRED_SHIPPED_COMPONENTS = {
"hipBLASLt",
"rocBLAS",
"Composable Kernel",
"ROCprofiler SDK / ROCTx",
"MIOpen",
"AOTriton",
"hipCUB",
"rocPRIM",
"rocWMMA",
"OpenSSL",
"libpng",
"libjpeg-turbo",
"DS4",
"h3.c",
"ccv",
"llama.cpp",
"ICU",
"curl",
@@ -32,6 +43,8 @@ REQUIRED_EVALUATION_COMPONENTS = {
"PyTorch",
"Torchvision",
"LPIPS",
"Triton",
"ROCprofiler SDK / ROCTx",
}
@@ -149,7 +162,7 @@ def verify_dependencies(
print(
f"[check-dependencies] PASSED: All {len(components)} third-party dependencies "
f"verified consistent with licenses, pins, and package boundaries."
f"have license/version records and required package bindings."
)
return 0