Skip to content

Commit 868af32

Browse files
committed
[common] Stream buffer for uint8 data
- Ensures a char trait implementation for uint8 exists, that can be used with std::basic_streambuff. - Adds an implementation of std::basic_streambuff for a single vector. Will be used by llama.cpp and tests when loading from a single memory buffer.
1 parent 3ca5a71 commit 868af32

File tree

3 files changed

+262
-1
lines changed

3 files changed

+262
-1
lines changed

ggml/include/uint8-buff-stream.h

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
#pragma once
2+
3+
#include <cstdint>
4+
#include <cstring>
5+
#include <iostream>
6+
#include <streambuf>
7+
#include <vector>
8+
9+
#ifdef __APPLE__
10+
# include <locale>
11+
12+
/// @brief Custom ctype specialization for uint8_t to work around libc++
13+
/// limitation in macOS
14+
template <> struct std::ctype<uint8_t> : public std::ctype_base {
15+
using char_type = uint8_t;
16+
static std::locale::id id;
17+
18+
ctype() : std::ctype_base() {}
19+
20+
ctype([[maybe_unused]] const std::locale::facet & other) : std::ctype_base() {}
21+
22+
ctype & operator=(const ctype & other) {
23+
if (this != &other) {
24+
std::ctype_base::operator=(other);
25+
}
26+
return *this;
27+
}
28+
29+
// Required public interface methods
30+
bool is(mask m, [[maybe_unused]] char_type c) const {
31+
return (m & space) != 0; // Treat all uint8_t as non-space
32+
}
33+
34+
const char_type * is(const char_type * low, const char_type * high, mask * vec) const {
35+
for (; low != high; ++low, ++vec) {
36+
*vec = 0; // No special character properties
37+
}
38+
return high;
39+
}
40+
41+
const char_type * scan_is(mask m, const char_type * low, const char_type * high) const {
42+
for (; low != high; ++low) {
43+
if (is(m, *low)) {
44+
return low;
45+
}
46+
}
47+
return high;
48+
}
49+
50+
const char_type * scan_not(mask m, const char_type * low, const char_type * high) const {
51+
for (; low != high; ++low) {
52+
if (!is(m, *low)) {
53+
return low;
54+
}
55+
}
56+
return high;
57+
}
58+
59+
char_type toupper(char_type c) const {
60+
return c; // No case conversion for uint8_t
61+
}
62+
63+
const char_type * toupper([[maybe_unused]] char_type * low, const char_type * high) const {
64+
return high; // No case conversion for uint8_t
65+
}
66+
67+
char_type tolower(char_type c) const {
68+
return c; // No case conversion for uint8_t
69+
}
70+
71+
const char_type * tolower([[maybe_unused]] char_type * low, const char_type * high) const {
72+
return high; // No case conversion for uint8_t
73+
}
74+
75+
char_type widen(char c) const { return static_cast<char_type>(c); }
76+
77+
const char * widen(const char * low, const char * high, char_type * dest) const {
78+
for (; low != high; ++low, ++dest) {
79+
*dest = static_cast<char_type>(*low);
80+
}
81+
return high;
82+
}
83+
84+
char narrow(char_type c, [[maybe_unused]] char dfault) const { return static_cast<char>(c); }
85+
86+
const char_type * narrow(const char_type * low, const char_type * high, [[maybe_unused]] char dfault,
87+
char * dest) const {
88+
for (; low != high; ++low, ++dest) {
89+
*dest = static_cast<char>(*low);
90+
}
91+
return high;
92+
}
93+
};
94+
#endif
95+
96+
/// @brief Custom traits for uint8_t for usage in std template classes that use char_traits (e.g. std::basic_streambuf)
97+
template <> struct std::char_traits<uint8_t> {
98+
using char_type = uint8_t;
99+
using int_type = int;
100+
using off_type = std::streamoff;
101+
using pos_type = std::streampos;
102+
using state_type = std::mbstate_t;
103+
104+
static void assign(char_type & c1, const char_type & c2) noexcept { c1 = c2; }
105+
106+
static constexpr bool eq(char_type a, char_type b) noexcept { return a == b; }
107+
108+
static constexpr bool lt(char_type a, char_type b) noexcept { return a < b; }
109+
110+
static int compare(const char_type * s1, const char_type * s2, std::size_t n) {
111+
for (std::size_t i = 0; i < n; ++i) {
112+
if (lt(s1[i], s2[i])) {
113+
return -1;
114+
}
115+
if (lt(s2[i], s1[i])) {
116+
return 1;
117+
}
118+
}
119+
return 0;
120+
}
121+
122+
static std::size_t length(const char_type * s) {
123+
std::size_t i = 0;
124+
while (!eq(s[i], char_type())) {
125+
++i;
126+
}
127+
return i;
128+
}
129+
130+
static const char_type * find(const char_type * s, std::size_t n, const char_type & c) {
131+
for (std::size_t i = 0; i < n; ++i) {
132+
if (eq(s[i], c)) {
133+
return s + i;
134+
}
135+
}
136+
return nullptr;
137+
}
138+
139+
static char_type * move(char_type * s1, const char_type * s2, std::size_t n) {
140+
return static_cast<char_type *>(std::memmove(s1, s2, n));
141+
}
142+
143+
static char_type * copy(char_type * s1, const char_type * s2, std::size_t n) {
144+
return static_cast<char_type *>(std::memcpy(s1, s2, n));
145+
}
146+
147+
static char_type * assign(char_type * s, std::size_t n, char_type c) {
148+
for (std::size_t i = 0; i < n; ++i) {
149+
s[i] = c;
150+
}
151+
return s;
152+
}
153+
154+
static constexpr int_type not_eof(int_type c) noexcept { return eq_int_type(c, eof()) ? 0 : c; }
155+
156+
static constexpr char_type to_char_type(int_type c) noexcept {
157+
return c >= 0 && c <= 255 ? static_cast<char_type>(c) : char_type();
158+
}
159+
160+
static constexpr int_type to_int_type(char_type c) noexcept { return static_cast<int_type>(c); }
161+
162+
static constexpr bool eq_int_type(int_type c1, int_type c2) noexcept { return c1 == c2; }
163+
164+
static constexpr int_type eof() noexcept { return static_cast<int_type>(-1); }
165+
};
166+
167+
#ifdef GGML_SHARED
168+
# if defined(_WIN32) && !defined(__MINGW32__)
169+
# ifdef GGML_BUILD
170+
# define GGML_CLASS_API __declspec(dllexport)
171+
# else
172+
# define GGML_CLASS_API __declspec(dllimport)
173+
# endif
174+
# else
175+
# define GGML_CLASS_API __attribute__((visibility("default")))
176+
# endif
177+
#else
178+
# define GGML_CLASS_API
179+
#endif
180+
181+
/// @brief Custom streambuf for uint8_t
182+
class GGML_CLASS_API Uint8BufferStreamBuf : public std::basic_streambuf<uint8_t> {
183+
public:
184+
Uint8BufferStreamBuf(std::vector<uint8_t> && _data);
185+
186+
protected:
187+
int_type underflow() override;
188+
189+
/// @brief Efficient bulk reading. The standard implementation specifies that this function can be overridden
190+
/// to provide a more efficient implementation: sgetn will call this function if it is overridden.
191+
std::streamsize xsgetn(char_type * s, std::streamsize n) override;
192+
193+
pos_type seekoff(off_type off, std::ios_base::seekdir dir,
194+
std::ios_base::openmode which = std::ios_base::in) override;
195+
196+
pos_type seekpos(pos_type pos, std::ios_base::openmode which = std::ios_base::in) override;
197+
198+
private:
199+
std::vector<uint8_t> data;
200+
};

ggml/src/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ add_library(ggml-base
194194
../include/ggml-cpp.h
195195
../include/ggml-opt.h
196196
../include/gguf.h
197+
../include/uint8-buff-stream.h
197198
ggml.c
198199
ggml.cpp
199200
ggml-alloc.c
@@ -203,7 +204,8 @@ add_library(ggml-base
203204
ggml-threading.h
204205
ggml-quants.c
205206
ggml-quants.h
206-
gguf.cpp)
207+
gguf.cpp
208+
uint8-buff-stream.cpp)
207209

208210
target_include_directories(ggml-base PRIVATE .)
209211
if (GGML_BACKEND_DL)

ggml/src/uint8-buff-stream.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#include "uint8-buff-stream.h"
2+
3+
#ifdef __APPLE__
4+
std::locale::id std::ctype<uint8_t>::id;
5+
#endif
6+
7+
GGML_API Uint8BufferStreamBuf::Uint8BufferStreamBuf(std::vector<uint8_t> && _data) : data(std::move(_data)) {
8+
setg(const_cast<uint8_t *>(data.data()), const_cast<uint8_t *>(data.data()),
9+
const_cast<uint8_t *>(data.data()) + data.size());
10+
}
11+
12+
GGML_API Uint8BufferStreamBuf::int_type Uint8BufferStreamBuf::underflow() {
13+
if (gptr() < egptr()) {
14+
return traits_type::to_int_type(*gptr());
15+
}
16+
return traits_type::eof();
17+
}
18+
19+
GGML_API std::streamsize Uint8BufferStreamBuf::xsgetn(char_type * s, std::streamsize n) {
20+
std::streamsize available = egptr() - gptr();
21+
std::streamsize to_read = std::min(n, available);
22+
if (to_read > 0) {
23+
std::memcpy(s, gptr(), to_read);
24+
setg(eback(), gptr() + to_read, egptr());
25+
}
26+
return to_read;
27+
}
28+
29+
GGML_API Uint8BufferStreamBuf::pos_type Uint8BufferStreamBuf::seekoff(off_type off, std::ios_base::seekdir dir,
30+
std::ios_base::openmode which) {
31+
if (!(which & std::ios_base::in)) {
32+
return pos_type(off_type(-1));
33+
}
34+
char_type * new_pos = nullptr;
35+
if (dir == std::ios_base::beg) {
36+
new_pos = eback() + off;
37+
} else if (dir == std::ios_base::cur) {
38+
new_pos = gptr() + off;
39+
} else if (dir == std::ios_base::end) {
40+
new_pos = egptr() + off;
41+
}
42+
if (new_pos >= eback() && new_pos <= egptr()) {
43+
setg(eback(), new_pos, egptr());
44+
return new_pos - eback();
45+
}
46+
return pos_type(off_type(-1));
47+
}
48+
49+
GGML_API Uint8BufferStreamBuf::pos_type Uint8BufferStreamBuf::seekpos(pos_type pos, std::ios_base::openmode which) {
50+
if (!(which & std::ios_base::in)) {
51+
return pos_type(off_type(-1));
52+
}
53+
char_type * new_pos = eback() + pos;
54+
if (new_pos >= eback() && new_pos <= egptr()) {
55+
setg(eback(), new_pos, egptr());
56+
return pos;
57+
}
58+
return pos_type(off_type(-1));
59+
}

0 commit comments

Comments
 (0)