include/brotensor/gguf.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | // GGUF reader for brotensor. Chunk 1 — loader + storage carrier only; no | ||
| 4 | // dequant kernels and no ops dispatch on quant dtypes. | ||
| 5 | // | ||
| 6 | // Format spec: https://github.com/ggerganov/ggml/blob/master/docs/gguf.md | ||
| 7 | // | ||
| 8 | // The file is mmap'd; TensorInfo::data points directly into the mapping, so | ||
| 9 | // it's valid only while the owning File is alive. Zero-copy on the read path. | ||
| 10 | // brotensor is 2D-only — gguf::shape_to_2d() collapses the GGUF | ||
| 11 | // (innermost-first) dim list to (rows, cols) for upload_raw(). | ||
| 12 | |||
| 13 | #include <brotensor/tensor.h> | ||
| 14 | #include <brotensor/detail/string_hash.h> | ||
| 15 | |||
| 16 | #include <cstddef> | ||
| 17 | #include <cstdint> | ||
| 18 | #include <functional> | ||
| 19 | #include <string> | ||
| 20 | #include <string_view> | ||
| 21 | #include <unordered_map> | ||
| 22 | #include <utility> | ||
| 23 | #include <vector> | ||
| 24 | |||
| 25 | namespace brotensor::gguf { | ||
| 26 | |||
| 27 | enum class ValueType : uint32_t { | ||
| 28 | U8 = 0, I8 = 1, U16 = 2, I16 = 3, U32 = 4, I32 = 5, | ||
| 29 | F32 = 6, Bool = 7, String = 8, Array = 9, | ||
| 30 | U64 = 10, I64 = 11, F64 = 12, | ||
| 31 | }; | ||
| 32 | |||
| 33 | 190 | struct Value { | |
| 34 | 95 | ValueType type = ValueType::U32; | |
| 35 | // Only the field matching `type` (or, for arrays, `array_elem_type` on | ||
| 36 | // each element) is meaningful. | ||
| 37 | union Scalar { | ||
| 38 | uint8_t u8; int8_t i8; | ||
| 39 | uint16_t u16; int16_t i16; | ||
| 40 | uint32_t u32; int32_t i32; | ||
| 41 | uint64_t u64; int64_t i64; | ||
| 42 | float f32; double f64; | ||
| 43 | bool b; | ||
| 44 | 95 | } scalar{}; | |
| 45 | std::string str; | ||
| 46 | 95 | ValueType array_elem_type = ValueType::U32; | |
| 47 | std::vector<Value> array; | ||
| 48 | }; | ||
| 49 | |||
| 50 | 83 | struct TensorInfo { | |
| 51 | std::string name; | ||
| 52 | // Dim order is innermost-first, as stored on disk. shape_to_2d() returns | ||
| 53 | // (rows = product(shape[1..]), cols = shape[0]). | ||
| 54 | std::vector<int64_t> shape; | ||
| 55 | 83 | uint32_t ggml_type = 0; | |
| 56 | 83 | brotensor::Dtype dtype = brotensor::Dtype::FP32; | |
| 57 | 83 | bool dtype_supported = true; | |
| 58 | 83 | const uint8_t* data = nullptr; | |
| 59 | 83 | std::size_t nbytes = 0; | |
| 60 | 83 | int64_t numel = 1; | |
| 61 | }; | ||
| 62 | |||
| 63 | class File { | ||
| 64 | public: | ||
| 65 | 78 | File() = default; | |
| 66 | ~File(); | ||
| 67 | |||
| 68 | File(File&& other) noexcept; | ||
| 69 | File& operator=(File&& other) noexcept; | ||
| 70 | File(const File&) = delete; | ||
| 71 | File& operator=(const File&) = delete; | ||
| 72 | |||
| 73 | static File open(const std::string& path); | ||
| 74 | |||
| 75 | const TensorInfo* find_tensor(std::string_view name) const; | ||
| 76 | const TensorInfo& get_tensor(std::string_view name) const; | ||
| 77 | 1 | const std::vector<TensorInfo>& tensors() const { return tensors_; } | |
| 78 | |||
| 79 | const Value* find_meta(std::string_view key) const; | ||
| 80 | const Value& get_meta(std::string_view key) const; | ||
| 81 | 1 | const std::vector<std::pair<std::string, Value>>& metadata() const { return metadata_; } | |
| 82 | |||
| 83 | 5 | uint32_t version() const { return version_; } | |
| 84 | 6 | uint32_t alignment() const { return alignment_; } | |
| 85 | 9 | std::size_t tensor_count() const { return tensors_.size(); } | |
| 86 | |||
| 87 | private: | ||
| 88 | void release_() noexcept; | ||
| 89 | |||
| 90 | 26 | void* file_ = nullptr; | |
| 91 | 26 | void* mapping_ = nullptr; | |
| 92 | 26 | void* base_ = nullptr; | |
| 93 | 26 | std::size_t file_size_ = 0; | |
| 94 | |||
| 95 | 26 | uint32_t version_ = 0; | |
| 96 | 26 | uint32_t alignment_ = 32; | |
| 97 | |||
| 98 | std::vector<std::pair<std::string, Value>> metadata_; | ||
| 99 | std::unordered_map<std::string, std::size_t, detail::StringHash, std::equal_to<>> meta_index_; | ||
| 100 | std::vector<TensorInfo> tensors_; | ||
| 101 | std::unordered_map<std::string, std::size_t, detail::StringHash, std::equal_to<>> tensor_index_; | ||
| 102 | }; | ||
| 103 | |||
| 104 | // Convert GGUF shape (innermost-first) to brotensor 2D (rows, cols). cols is | ||
| 105 | // the innermost (contiguous) axis; rows is the product of the remaining axes. | ||
| 106 | // 1D input → (shape[0], 1). Empty shape throws. | ||
| 107 | std::pair<int, int> shape_to_2d(const std::vector<int64_t>& shape); | ||
| 108 | |||
| 109 | // Allocate `dst` on the current default device sized for `info.numel` | ||
| 110 | // elements at `info.dtype` (via dtype_storage_bytes) and copy info.nbytes | ||
| 111 | // bytes from the mmap into it. For quant dtypes, cols must be a multiple of | ||
| 112 | // dtype_block_size(dtype) (blocks run along the contiguous axis). Throws if | ||
| 113 | // info.dtype_supported is false or rows*cols != info.numel. | ||
| 114 | void upload_raw(const TensorInfo& info, int rows, int cols, | ||
| 115 | brotensor::Tensor& dst); | ||
| 116 | |||
| 117 | } // namespace brotensor::gguf | ||
| 118 |