char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Xuan-Son Nguyencommon: json.h: fix clang lto (#27575)70adb1b4c

char/aliceai-t5-moe
12.9 KiB352 linesraw
1#pragma once
2
3#include <cstddef>
4#include <cstdint>
5#include <initializer_list>
6#include <iterator>
7#include <map>
8#include <memory>
9#include <set>
10#include <stdexcept>
11#include <string>
12#include <string_view>
13#include <type_traits>
14#include <unordered_map>
15#include <utility>
16#include <vector>
17
18// common_json, a thin wrapper around vendor json library
19// the underlay library is pimpl, we are using nlohmann::json for now
20//
21// many features of the library are deliberately left out, to keep this interface small and generic and to keep compile time down
22//
23// some main differences compared to nlohmann::json :
24// - object keys keep the order in which they are added
25// - errors are always throw as common_json_error
26// - obj.push_back({key, val}) is intentionally unsupported to avoid confusion with push_back on a vector; write it as obj[key] = val for clarity
27// - a braced pair in value position does not build, e.g. {"key", {"a", "b"}}; write array({"a", "b"}) where nlohmann made an array
28//
29// in doubt, search the code base for an existing usage example; do not add anything to this header unless absolutely necessary
30
31class common_json;
32
33// common_json_value holds a list of these, and each of them holds a value, so one must come first
34struct common_json_item;
35
36struct common_json_error : std::runtime_error {
37    using std::runtime_error::runtime_error;
38};
39
40// one value, tagged so that this header stays free of the backing library
41// note: a value that holds a tree is single use, the second use gives null
42struct common_json_value {
43    enum value_type {
44        VAL_NULL,
45        VAL_BOOL,
46        VAL_INT,
47        VAL_UINT,
48        VAL_DOUBLE,
49        VAL_STRING,
50        VAL_JSON,
51    };
52
53    value_type type = VAL_NULL;
54
55    union {
56        bool     val_bool;
57        int64_t  val_int;
58        uint64_t val_uint = 0;
59        double   val_double;
60    };
61
62    std::string                  val_string;
63    std::shared_ptr<common_json> val_json;
64
65    common_json_value(std::nullptr_t = nullptr) : type(VAL_NULL) {}
66    common_json_value(bool val) : type(VAL_BOOL), val_bool(val) {}
67    common_json_value(std::string val) : type(VAL_STRING), val_string(std::move(val)) {}
68    // without this a string_view lands on the common_json ctor below and recurses
69    common_json_value(std::string_view val) : type(VAL_STRING), val_string(val) {}
70    common_json_value(const char * val);
71    common_json_value(const common_json & val);
72    common_json_value(common_json && val);
73    // only for the types instantiated in json.cpp, the rest fails at link time
74    template <typename T> common_json_value(const std::vector<T> & vals);
75    // a set becomes an array, in the set's own order
76    template <typename T> common_json_value(const std::set<T> & vals);
77    // a map becomes an object, keyed in the map's own order
78    template <typename T> common_json_value(const std::map<std::string, T> & vals);
79    template <typename T> common_json_value(const std::unordered_map<std::string, T> & vals);
80
81    // nested object, e.g. {"fn", {{"name", "x"}}}
82    // note: a nested pair {"a", "b"} does not build, use common_json::array({"a", "b"}) for an array
83    common_json_value(std::initializer_list<common_json_item> items);
84
85    template <typename T, typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, bool>::value, int>::type = 0>
86    common_json_value(T val) : type(std::is_signed<T>::value ? VAL_INT : VAL_UINT) {
87        if (std::is_signed<T>::value) {
88            val_int = (int64_t) val;
89        } else {
90            val_uint = (uint64_t) val;
91        }
92    }
93
94    template <typename T, typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0>
95    common_json_value(T val) : type(VAL_DOUBLE), val_double((double) val) {}
96};
97
98struct common_json_item {
99    std::string       key;
100    common_json_value val;
101
102    template <typename T>
103    common_json_item(std::string key, T && val) :
104        key(std::move(key)), val(std::forward<T>(val)) {}
105
106    // a braced list cannot deduce T, so it needs its own overload
107    common_json_item(std::string key, std::initializer_list<common_json_item> items) :
108        key(std::move(key)), val(items) {}
109};
110
111// the types common_json_value holds on its own
112// anything else reaches its common_json ctor and recurses forever
113template <typename T> struct common_json_is_value : std::integral_constant<bool,
114    std::is_arithmetic<T>::value ||
115    std::is_same<T, std::nullptr_t>::value ||
116    std::is_same<T, std::string>::value ||
117    std::is_same<T, std::string_view>::value ||
118    std::is_same<T, char *>::value ||
119    std::is_same<T, const char *>::value ||
120    std::is_same<T, common_json>::value> {};
121
122template <typename T, typename A>
123struct common_json_is_value<std::vector<T, A>> : std::true_type {};
124
125template <typename T, typename C, typename A>
126struct common_json_is_value<std::set<T, C, A>> : std::true_type {};
127
128template <typename V, typename C, typename A>
129struct common_json_is_value<std::map<std::string, V, C, A>> : std::true_type {};
130
131template <typename V, typename H, typename E, typename A>
132struct common_json_is_value<std::unordered_map<std::string, V, H, E, A>> : std::true_type {};
133
134class common_json {
135  public:
136    common_json();
137    common_json(const common_json & other);
138    common_json(common_json && other) noexcept;
139    common_json(std::initializer_list<common_json_item> items);
140    common_json(const common_json_value & val);
141
142    // direct, a value would need two conversions in a row
143    common_json(std::nullptr_t);
144
145    // one step, so that "abc" or a vector can go straight into a common_json
146    template <typename T, typename std::enable_if<!std::is_same<typename std::decay<T>::type, common_json>::value &&
147                                                  !std::is_same<typename std::decay<T>::type, common_json_value>::value, int>::type = 0>
148    common_json(T && val) : common_json(common_json_value(std::forward<T>(val))) {
149        static_assert(common_json_is_value<typename std::decay<T>::type>::value,
150                      "no common_json_value ctor holds this type, add one instead of letting it recurse");
151    }
152
153    // by value, same as the backing library
154    // the right side is copied before the left side can invalidate it, e.g. msg["a"] = msg.at("b")
155    common_json & operator=(common_json other) noexcept;
156
157    ~common_json();
158
159    // throws common_json_error if the text is not valid JSON
160    static common_json parse(const std::string & text);
161
162    // gives a discarded value instead of throwing, check it with is_discarded()
163    static common_json parse_no_throw(const std::string & text);
164
165    bool is_discarded() const;
166
167    static common_json array();
168    static common_json array(std::initializer_list<common_json_value> vals);
169    static common_json object();
170    static common_json object(std::initializer_list<common_json_item> items);
171
172    // holds a single value, e.g. make("abc").dump() gives "\"abc\""
173    static common_json make(const common_json_value & val);
174
175    bool is_null()    const;
176    bool is_object()  const;
177    bool is_array()   const;
178    bool is_string()  const;
179    bool is_boolean() const;
180    bool is_number()  const;
181    bool is_number_integer() const;
182    bool is_number_float()   const;
183
184    bool   empty() const;
185    size_t size()  const;
186
187    bool contains(const std::string & key) const;
188
189    bool operator==(const common_json_value & val) const;
190    bool operator!=(const common_json_value & val) const;
191
192    // at() throws common_json_error if the key is missing, operator[] adds a null value instead
193    // note: a const operator[] cannot add, it throws like at()
194    common_json       & at(const std::string & key);
195    const common_json & at(const std::string & key) const;
196    common_json       & at(size_t idx);
197    const common_json & at(size_t idx) const;
198
199    common_json       & operator[](const std::string & key);
200    const common_json & operator[](const std::string & key) const;
201    common_json       & operator[](const char * key)       { return (*this)[std::string(key)]; }
202    const common_json & operator[](const char * key) const { return (*this)[std::string(key)]; }
203    common_json       & operator[](int idx)       { return (*this)[to_idx(idx)]; }
204    const common_json & operator[](int idx) const { return (*this)[to_idx(idx)]; }
205    common_json       & operator[](size_t idx);
206    const common_json & operator[](size_t idx) const;
207
208    common_json       & front();
209    const common_json & front() const;
210    common_json       & back();
211    const common_json & back()  const;
212
213    void clear();
214
215    void erase(const std::string & key);
216    void erase(size_t idx);
217
218    // only for the types instantiated in json.cpp, the rest fails at link time
219    template <typename T> T get() const;
220
221    // implicit get<T>() for plain values, so they can be assigned to their C++ type directly
222    // note: kept to this short list on purpose, a wider one makes j["key"] ambiguous
223    // note: a numeric one would make "str = json;" ambiguous, a number converts to char too
224    operator std::string() const;
225
226    template <typename T>
227    T value(const std::string & key, T def) const {
228        return contains(key) ? at(key).get<T>() : def;
229    }
230
231    std::string value(const std::string & key, const char * def) const;
232
233    // a JSON default needs no get<T>(), it is already the right type
234    common_json value(const std::string & key, const common_json & def) const {
235        return contains(key) ? at(key) : def;
236    }
237
238    void assign(const common_json_value & val);
239    void set(const common_json_item & item);
240    void push_back(const common_json_value & val);
241
242    // appends one object, e.g. push_back({{"a", 1}})
243    void push_back(std::initializer_list<common_json_item> items);
244
245    // 1 if the key is there, 0 if not
246    size_t count(const std::string & key) const;
247
248    // appends every value of another array; inserting an array into itself throws
249    void insert(const common_json & vals);
250
251    // a common_json goes through the copy assignment above, everything else becomes a value
252    template <typename T, typename std::enable_if<!std::is_same<typename std::decay<T>::type, common_json>::value, int>::type = 0>
253    common_json & operator=(T && val) {
254        assign(common_json_value(std::forward<T>(val)));
255        return *this;
256    }
257
258    std::string dump(int indent = -1) const;
259
260    // same as dump(), but bad UTF-8 gets replaced instead of throwing
261    std::string dump_safe(int indent = -1) const;
262
263    // walks an array by index, or an object in insertion order
264    // a plain value gives itself once, same as the backing library
265    class iterator {
266      public:
267        using iterator_category = std::forward_iterator_tag;
268        using value_type        = common_json;
269        using difference_type   = std::ptrdiff_t;
270        using pointer           = common_json *;
271        using reference         = common_json &;
272
273        iterator(common_json * node, size_t idx) : node(node), idx(idx) {}
274
275        common_json & operator*() const;
276        common_json & value()     const { return **this; }
277        std::string   key()       const;
278
279        iterator & operator++() {
280            idx++;
281            return *this;
282        }
283
284        bool operator!=(const iterator & other) const { return idx != other.idx; }
285        bool operator==(const iterator & other) const { return idx == other.idx; }
286
287      private:
288        common_json * node;
289        size_t        idx;
290    };
291
292    iterator begin() const;
293    iterator end()   const;
294
295    // allows: for (const auto & [key, val] : obj.items())
296    class items_view {
297      public:
298        // the members are public, so an entry also works with structured bindings
299        struct entry {
300            std::string   k;
301            common_json & v;
302
303            const std::string & key()   const { return k; }
304            common_json &       value() const { return v; }
305        };
306
307        items_view(common_json * node, size_t n) : node(node), n(n) {}
308
309        class iterator {
310          public:
311            iterator(common_json * node, size_t idx) : node(node), idx(idx) {}
312
313            entry operator*() const;
314
315            iterator & operator++() {
316                idx++;
317                return *this;
318            }
319
320            bool operator!=(const iterator & other) const { return idx != other.idx; }
321
322          private:
323            common_json * node;
324            size_t        idx;
325        };
326
327        iterator begin() const { return iterator(node, 0); }
328        iterator end()   const { return iterator(node, n); }
329
330      private:
331        common_json * node;
332        size_t        n;
333    };
334
335    items_view items() const;
336
337  private:
338    // a negative index must not turn into a huge size_t
339    static size_t to_idx(int idx) {
340        if (idx < 0) {
341            throw common_json_error("negative array index");
342        }
343        return (size_t) idx;
344    }
345
346    // the backing value is built here, json.cpp checks that it fits
347    // it cannot be a pointer: a value inside a tree would then not be a common_json
348    // at() could then only give back a copy instead of a real reference
349    alignas(8) unsigned char storage[32];
350};
351
352using common_json_entry = common_json::items_view::entry;