├── .gitattributes
├── .github
└── workflows
│ └── tg-send.yml
├── LICENSE
├── README.md
├── README_EN.md
├── examples
├── parseTo
│ └── parseTo.ino
└── test
│ └── test.ino
├── keywords.txt
├── library.properties
└── src
├── GSON.h
└── utils
├── bson.h
├── entry.h
├── entry_stack.h
├── entry_t.h
├── parser.h
├── parser_stream.h
├── str.h
├── string.h
└── types.h
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.github/workflows/tg-send.yml:
--------------------------------------------------------------------------------
1 |
2 | name: Telegram Message
3 | on:
4 | release:
5 | types: [published]
6 | jobs:
7 | build:
8 | name: Send Message
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: send telegram message on push
12 | uses: appleboy/telegram-action@master
13 | with:
14 | to: ${{ secrets.TELEGRAM_TO }}
15 | token: ${{ secrets.TELEGRAM_TOKEN }}
16 | disable_web_page_preview: true
17 | message: |
18 | ${{ github.event.repository.name }} v${{ github.event.release.tag_name }}
19 | ${{ github.event.release.body }}
20 | https://github.com/${{ github.repository }}
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 GyverLibs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/GyverLibs/GSON/releases/latest/download/GSON.zip)
2 | [](https://registry.platformio.org/libraries/gyverlibs/GSON)
3 | [](https://alexgyver.ru/)
4 | [](https://alexgyver.ru/support_alex/)
5 | [](https://github-com.translate.goog/GyverLibs/GSON?_x_tr_sl=ru&_x_tr_tl=en)
6 |
7 | [](https://t.me/GyverLibs)
8 |
9 | # GSON
10 | Парсер и сборщик данных в формате JSON для Arduino
11 | - В 6 раз быстрее и сильно легче ArduinoJSON
12 | - Парсинг JSON с обработкой ошибок
13 | - Линейная сборка JSON-пакета
14 | - Экранирование "опасных" символов
15 | - Работает на базе Text ([StringUtils](https://github.com/GyverLibs/StringUtils))
16 | - Работает с 64 битными числами
17 | - Встроенный механизм хэширования ключей
18 | - *Библиотека не подходит для хранения и изменения данных! Только парсинг и сборка пакетов*
19 |
20 | ### Совместимость
21 | Совместима со всеми Arduino платформами (используются Arduino-функции)
22 |
23 | ### Зависимости
24 | - [StringUtils](https://github.com/GyverLibs/StringUtils)
25 | - [GTL](https://github.com/GyverLibs/GTL) v1.0.7+
26 |
27 | ## Содержание
28 | - [Документация](#docs)
29 | - [Использование](#usage)
30 | - [Версии](#versions)
31 | - [Установка](#install)
32 | - [Баги и обратная связь](#feedback)
33 |
34 |
35 |
36 | ## Документация
37 | ### `Text`
38 | Под типом `Text` понимается строка в любом формате:
39 | - `"const char"` - строки
40 | - `char[]` - строки
41 | - `F("f-строки")`
42 | - `String` - строки
43 |
44 | ### `gson::Parser`
45 | ```cpp
46 | // конструктор
47 | gson::Parser;
48 |
49 | // методы
50 | bool reserve(uint16_t cap); // зарезервировать память для ускорения парсинга
51 | uint16_t length(); // получить количество элементов
52 | uint16_t size(); // получить размер документа в оперативной памяти (байт)
53 | void hashKeys(); // хешировать ключи всех элементов (операция необратима)
54 | bool hashed(); // проверка были ли хешированы ключи
55 | void reset(); // освободить память
56 | uint16_t rootLength(); // получить количество элементов в главном контейнере
57 |
58 | // установить максимальную глубину вложенности парсинга (умолч. 16)
59 | void setMaxDepth(uint8_t maxdepth);
60 |
61 | Entry get(Text key); // доступ по ключу (главный контейнер - Object)
62 | Entry get(size_t hash); // доступ по хэшу ключа (главный контейнер - Object)
63 | Entry get(int index); // доступ по индексу (главный контейнер - Array или Object) (rootLength())
64 |
65 | Entry getByIndex(parent_t index); // получить элемент по индексу в общем массиве парсера (length())
66 |
67 | Text key(int idx); // прочитать ключ по индексу
68 | size_t keyHash(int idx); // прочитать хэш ключа по индексу
69 | Text value(int idx); // прочитать значение по индексу
70 | int8_t parent(int idx); // прочитать родителя по индексу
71 | Type type(int idx); // получить тип по индексу
72 |
73 | const __FlashStringHelper* readType(uint16_t idx); // прочитать тип по индексу
74 |
75 | // парсить. Вернёт true при успешном парсинге
76 | bool parse(const Text& json);
77 | bool parse(const char* json, uint16_t len);
78 |
79 | // вывести в Print с форматированием
80 | void stringify(Print& p);
81 |
82 | // парсить в массив длины length()
83 | template
84 | bool parseTo(T& arr);
85 |
86 | // обработка ошибок
87 | bool hasError(); // есть ошибка парсинга
88 | Error getError(); // получить ошибку
89 | uint16_t errorIndex(); // индекс места ошибки в строке
90 | const __FlashStringHelper* readError(); // прочитать ошибку
91 | ```
92 |
93 | ParserStream - парсер из Stream потока
94 | ```cpp
95 | gson::ParserStream;
96 |
97 | // прочитать из потока и сохранить себе
98 | bool parse(Stream* stream, size_t length);
99 |
100 | // прочитать из строки и сохранить себе
101 | bool parse(const char* str, size_t length);
102 |
103 | // освободить память
104 | void reset();
105 |
106 | // получить скачанный json пакет как Text
107 | Text getRaw();
108 |
109 | void move(ParserStream& ps);
110 | ```
111 |
112 | #### Настройки
113 | Объявляются перед подключением библиотеки
114 | ```cpp
115 | // увеличить лимиты на хранение (описаны ниже)
116 | #define GSON_NO_LIMITS
117 | ```
118 |
119 | #### Лимиты и память
120 | - После парсинга один элемент весит 8 байт
121 | - Максимальное количество элементов: 255 на AVR и 512 на ESP
122 | - Максимальная длина json-строки: 65 536 символов
123 | - Максимальная длина ключа: 32 символа
124 | - Максимальная длина значения: 32768 символов
125 | - Максимальный уровень вложенности элементов 16, настраивается. Парсинг рекурсивный, каждый уровень добавляет несколько байт в оперативку
126 |
127 | При объявлении `#define GSON_NO_LIMITS` до подключения библиотеки лимиты повышаются:
128 | - Один элемент весит 12 байт
129 | - Максимальное количество элементов: 65 356 на ESP
130 | - Максимальная длина ключа: 256 символов
131 | - Максимальная длина значения: 65 356 символов
132 |
133 | ### Тесты
134 | Тестировал на ESP8266, пакет сообщений из телеграм бота - 3600 символов, 147 "элементов". Получал значение самого отдалённого и вложенного элемента, в GSON - через хэш. Результат:
135 |
136 | | Либа | Flash | SRAM | FreeHeap | Parse | Get |
137 | | ----------- | ------ | ----- | -------- | -------- | ------ |
138 | | ArduinoJson | 297433 | 31628 | 41104 | 10686 us | 299 us |
139 | | GSON | 279349 | 31400 | 43224 | 1744 us | 296 us |
140 |
141 | Таким образом GSON **в 6 раз быстрее** при парсинге, значения элементов получает с такой же скоростью. Сама библиотека легче на 18 кБ во Flash и 2.1 кБ в Heap. В других тестах (AVR) на получение значения GSON с хэшем работал в среднем в 1.5 раза быстрее.
142 |
143 | ### `gson::Type`
144 | ```cpp
145 | None
146 | Object
147 | Array
148 | String
149 | Int
150 | Float
151 | Bool
152 | Null
153 | ```
154 |
155 | ### `gson::Error`
156 | ```cpp
157 | None
158 | Alloc
159 | TooDeep
160 | NoParent
161 | NotContainer
162 | UnexComma
163 | UnexColon
164 | UnexToken
165 | UnexQuotes
166 | UnexOpen
167 | UnexClose
168 | UnknownToken
169 | BrokenToken
170 | BrokenString
171 | BrokenContainer
172 | EmptyKey
173 | IndexOverflow
174 | LongPacket
175 | LongKey
176 | EmptyString
177 | ```
178 |
179 | ### `gson::Entry`
180 | Также наследует всё из `Text`, документация [здесь](https://github.com/GyverLibs/StringUtils?tab=readme-ov-file#anytext)
181 |
182 | ```cpp
183 | Entry get(Text key); // получить элемент по ключу
184 | bool has(Text key); // содержит элемент с указанным ключом
185 |
186 | Entry get(size_t hash); // получить элемент по хэшу ключа
187 | bool has(size_t hash); // содержит элемент с указанным хэшем ключа
188 |
189 | Entry get(int index); // получить элемент по индексу
190 |
191 | bool valid(); // проверка корректности (существования)
192 | uint16_t length(); // получить размер (для объектов и массивов. Для остальных 0)
193 | Text key(); // получить ключ
194 | size_t keyHash(); // получить хэш ключа
195 | Text value(); // получить значение
196 | void stringify(Print& p); // вывести в Print с форматированием
197 |
198 | Type type(); // получить тип элемента
199 | bool is(gson::Type type); // сравнить тип элемента
200 | bool isContainer(); // элемент Array или Object
201 | bool isObject(); // элемент Object
202 | bool isArray(); // элемент Array
203 | parent_t index(); // индекс элемента в общем массиве парсера
204 |
205 | // парсить в массив длины length()
206 | template
207 | bool parseTo(T& arr);
208 | ```
209 |
210 | ### `gson::Str`
211 | Новый инструмент взамен `gson::string` - в 2 раза быстрее и проще.
212 |
213 | ```cpp
214 | // доступ к буферу
215 | char* buf();
216 |
217 | // очистить
218 | void clear();
219 |
220 | // прибавить данные любого типа напрямую
221 | void concat(T val);
222 |
223 | // длина
224 | uint16_t length();
225 |
226 | // зарезервировать
227 | bool reserve(uint16_t len);
228 |
229 | // прибавить любые данные как JSON значение
230 | // (целые, float, char, nullptr, любые строки)
231 | operator=(T val);
232 | operator+=(T val);
233 |
234 | // добавить float с кол-вом знаков
235 | void add(float val, uint8_t dec = 2);
236 |
237 | // прибавить строку с escape
238 | void escape(const Text& txt);
239 |
240 | // ключ для объекта, строка любого типа
241 | Str& operator[](T str);
242 |
243 | // контейнер, всегда вернёт true. Тип {, }, [, ]
244 | Str& operator()(char type);
245 | ```
246 |
247 |
248 |
249 | ## Использование
250 | ### Парсинг
251 | Библиотека **не дублирует строку** в памяти и работает с исходной строкой: запоминает позиции текста, исходную строку не меняет. Отсюда следует, что:
252 | - Строка должна существовать в памяти на всём протяжении работы с json документом
253 | - Если исходная строка - `String` - она категорически не должна изменяться программой до окончания работы с документом
254 | - `ParserStream` "скачивает" строку из стрима в память
255 |
256 | Создание документа:
257 | ```cpp
258 | gson::Parser p;
259 | ```
260 |
261 | ```cpp
262 | // получили json
263 | char json[] = R"raw({"key":"value","int":12345,"obj":{"float":3.14,"bool":false},"arr":["hello",true]})raw";
264 | String json = "{\"key\":\"value\",\"int\":12345,\"obj\":{\"float\":3.14,\"bool\":false},\"arr\":[\"hello\",true]};";
265 |
266 | // парсить
267 | p.parse(json);
268 |
269 | // обработка ошибок
270 | if (p.hasError()) {
271 | Serial.print(p.readError());
272 | Serial.print(" in ");
273 | Serial.println(p.errorIndex());
274 | } else Serial.println("done");
275 | ```
276 |
277 | После парсинга можно вывести весь пакет с типами, ключами, значениями в виде текста и родителем:
278 | ```cpp
279 | for (uint16_t i = 0; i < p.length(); i++) {
280 | // if (p.type(i) == gson::Type::Object || p.type(i) == gson::Type::Array) continue; // пропустить контейнеры
281 | Serial.print(i);
282 | Serial.print(". [");
283 | Serial.print(p.readType(i));
284 | Serial.print("] ");
285 | Serial.print(p.key(i));
286 | Serial.print(":");
287 | Serial.print(p.value(i));
288 | Serial.print(" {");
289 | Serial.print(p.parent(i));
290 | Serial.println("}");
291 | }
292 | ```
293 |
294 | Значения можно получать в типе `Text`, который может конвертироваться в другие типы и выводиться в порт:
295 | - Ключом может быть строка в любом виде (`"строка"`, `F("строка")`)
296 | - Можно обращаться ко вложенным объектам по ключу, а к массивам по индексу
297 | - Документацию на `Text` смотри [здесь](https://github.com/GyverLibs/StringUtils)
298 |
299 | ```cpp
300 | Serial.println(p["key"]); // печать value
301 | Serial.println(p[F("int")]); // печать 12345
302 | int val = p["int"].toInt16(); // конвертация в указанный тип
303 | val = p["int"]; // авто конвертация
304 | float f = p["obj"]["float"]; // вложенный объект
305 | bool b = p["flag"].toBool(); // bool
306 | Serial.println(p["arr"][0]); // hello
307 | Serial.println(p["arr"][1]); // true
308 |
309 | char buf[10];
310 | p["key"].toStr(buf, 10); // вывод во внешний char-буфер
311 |
312 | // проверка типа
313 | p["arr"].type() == gson::Type::Array;
314 |
315 | // вывод содержимого массива
316 | for (int i = 0; i < p["arr"].length(); i++) {
317 | Serial.println(p["arr"][i]);
318 | }
319 |
320 | // а лучше - так
321 | gson::Entry arr = p["arr"];
322 | for (int i = 0; i < arr.length(); i++) {
323 | Serial.println(arr[i]);
324 | }
325 |
326 | // Пусть json имеет вид [[123,456],["abc","def"]], тогда ко вложенным массивам можно обратиться:
327 | Serial.println(p[0][0]); // 123
328 | Serial.println(p[0][1]); // 456
329 | Serial.println(p[1][0]); // abc
330 | Serial.println(p[1][1]); // def
331 | ```
332 |
333 | ### bool
334 | `Text` автоматически конвертируется во все типы, кроме `bool`, используй `toBool()`. Преобразование к bool показывает существование элемента, это можно использовать вместо `has()`:
335 |
336 | ```cpp
337 | if (p["foo"]) {} // если существует
338 | if (p["foo"].toBool()) {} // если существует и true
339 | ```
340 |
341 | Сравнение с `bool` работает более корректно: если элемент не существует - результат всегда будет `false`:
342 |
343 | ```cpp
344 | if (p["foo"] == true) {} // если существует и true
345 | if (p["foo"] == false) {} // если существует и false
346 | ```
347 |
348 | ### Вывод в Entry
349 | Каждый элемент можно вывести в тип `gson::Entry` по имени (из объекта) или индексу (из массива) и использовать отдельно, чтобы не "искать" его заново:
350 | ```cpp
351 | gson::Entry e = p["arr"];
352 | Serial.println(e.length()); // длина массива
353 | Serial.println(e[0]); // hello
354 | Serial.println(e[1]); // true
355 | ```
356 |
357 | ### Хэширование
358 | GSON нативно поддерживает хэш-строки из StringUtils, работа с хэшами значительно увеличивает скорость доступа к элементам JSON документа по ключу. Строка, переданная в функцию `SH`, вообще **не существует в программе** и не занимает места: хэш считается компилятором на этапе компиляции, вместо него подставляется число. А сравнение чисел выполняется быстрее, чем сравнение строк. Для этого нужно:
359 |
360 | 1. Хэшировать ключи после парсинга:
361 | ```cpp
362 | p.parse(json);
363 | p.hashKeys();
364 | ```
365 |
366 | > Примечание: хэширование не отменяет доступ по строкам, как было в первых версиях библиотеки! Можно использовать как `p["key"]`, так и `p[su::SH("key")]`
367 |
368 | 2. Обращаться к элементам по хэшам ключей, используя функцию `su::SH`:
369 | ```cpp
370 | using su::SH;
371 |
372 | void foo() {
373 | Serial.println(p[SH("int")]);
374 | Serial.println(p[SH("obj")][SH("float")]);
375 | Serial.println(p[SH("array")][0]);
376 | }
377 | ```
378 |
379 | > Примечание: для доступа по хэшу используется перегрузка `[size_t]`, а для доступа к элементу массива - `[int]`. Поэтому для корректного доступа к элементам массива нужно использовать именно `signed int`, а не unsigned (`uint8` и `uint16`)! Иначе компилятор может вызвать доступ по хэшу вместо обращения к массиву.
380 |
381 | ```cpp
382 | gson::Entry arr = p["arr"];
383 | for (int i = 0; i < arr.length(); i++) { // счётчик int!
384 | Serial.println(arr[i]);
385 | }
386 | ```
387 |
388 | > Хеширование создаёт в памяти массив размером `колво_элементов * 4`
389 |
390 | ### Сборка
391 | JSON строка собирается **линейно**, что очень просто и приятно для МК:
392 |
393 | ```cpp
394 | gson::Str j;
395 |
396 | j('{'); // начать объект
397 |
398 | if (j["obj"]('{')) { // объект с ключом. if для визуального отделения контейнера
399 | j["int"] = 123;
400 | j["float"] = 3.1415;
401 | j["float2"].add(3.1415, 4);
402 | j["null"] = nullptr;
403 | j["cstr"] = "cstr";
404 | j[F("fstr")] = F("fstr");
405 | j[String("str")] = String("str");
406 | j["esc"].escape(R"("he ll\o world")");
407 |
408 | j('}');
409 | }
410 | if (j["arr"]('[')) {
411 | j += true;
412 | j += NAN;
413 | j += INFINITY;
414 | j += nullptr;
415 |
416 | if (j('[')) {
417 | j += 12345;
418 | j += 1234567L;
419 | j += 123456787654LL;
420 | j(']');
421 | }
422 |
423 | j(']');
424 | }
425 | j('}');
426 |
427 | Serial.println(j);
428 | ```
429 |
430 | ```json
431 | {"obj":{"int":123,"float":3.14,"float2":3.1415,"null":null,"cstr":"cstr","fstr":"fstr","str":"str","esc":"\"he\tll\\o world\""},"arr":[true,null,null,null,[12345,1234567,123456787654]]}
432 | ```
433 |
434 |
435 |
436 | ## Версии
437 | - v1.0
438 | - v1.1 - улучшен парсер, добавлено хэширование ключей и обращение по хэш-кодам
439 | - v1.2 - оптимизация под StringUtils 1.3
440 | - v1.3 - оптимизация парсера, ускорение чтения значений из Parser
441 | - v1.4 - оптимизация парсера, ускорение чтения, изначальная строка больше не меняется парсером
442 | - v1.4.1 - поддержка ядра esp8266 v2.x
443 | - v1.4.2 - добавлены Raw методы в string
444 | - v1.4.3 - обновление до актуальной StringUtils, парсинг из Text
445 | - v1.4.6 - добавил stringify для Entry
446 | - v1.4.9 - добавлено больше функций addText в gson::string
447 | - v1.5.0
448 | - Ускорен парсинг
449 | - Уменьшен распарсенный вес в оперативке
450 | - Добавлена семантика move для передачи парсера между объектами как uniq объекта
451 | - Добавлен парсер из Stream
452 | - Упразднён Static парсер
453 | - Мелкие улучшения
454 | - v1.5.1 - добавлен сборщик бираного json (bson)
455 | - v1.5.2 - улучшен сборщик BSON, исправлен пример на JS
456 | - v1.5.7 - исправлен критический баг с парсингом пустого string значения
457 | - v1.5.9 - в BSON добавлена поддержка бинарных данных. Несовместимо с декодером предыдущей версии!
458 |
459 |
460 |
461 | ## Установка
462 | - Библиотеку можно найти по названию **GSON** и установить через менеджер библиотек в:
463 | - Arduino IDE
464 | - Arduino IDE v2
465 | - PlatformIO
466 | - [Скачать библиотеку](https://github.com/GyverLibs/GSON/archive/refs/heads/main.zip) .zip архивом для ручной установки:
467 | - Распаковать и положить в *C:\Program Files (x86)\Arduino\libraries* (Windows x64)
468 | - Распаковать и положить в *C:\Program Files\Arduino\libraries* (Windows x32)
469 | - Распаковать и положить в *Документы/Arduino/libraries/*
470 | - (Arduino IDE) автоматическая установка из .zip: *Скетч/Подключить библиотеку/Добавить .ZIP библиотеку…* и указать скачанный архив
471 | - Читай более подробную инструкцию по установке библиотек [здесь](https://alexgyver.ru/arduino-first/#%D0%A3%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0_%D0%B1%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA)
472 |
473 | ### Обновление
474 | - Рекомендую всегда обновлять библиотеку: в новых версиях исправляются ошибки и баги, а также проводится оптимизация и добавляются новые фичи
475 | - Через менеджер библиотек IDE: найти библиотеку как при установке и нажать "Обновить"
476 | - Вручную: **удалить папку со старой версией**, а затем положить на её место новую. "Замену" делать нельзя: иногда в новых версиях удаляются файлы, которые останутся при замене и могут привести к ошибкам!
477 |
478 |
479 | ## Баги и обратная связь
480 | При нахождении багов создавайте **Issue**, а лучше сразу пишите на почту [alex@alexgyver.ru](mailto:alex@alexgyver.ru)
481 | Библиотека открыта для доработки и ваших **Pull Request**'ов!
482 |
483 | При сообщении о багах или некорректной работе библиотеки нужно обязательно указывать:
484 | - Версия библиотеки
485 | - Какой используется МК
486 | - Версия SDK (для ESP)
487 | - Версия Arduino IDE
488 | - Корректно ли работают ли встроенные примеры, в которых используются функции и конструкции, приводящие к багу в вашем коде
489 | - Какой код загружался, какая работа от него ожидалась и как он работает в реальности
490 | - В идеале приложить минимальный код, в котором наблюдается баг. Не полотно из тысячи строк, а минимальный код
491 |
--------------------------------------------------------------------------------
/README_EN.md:
--------------------------------------------------------------------------------
1 | This is an automatic translation, may be incorrect in some places. See sources and examples!
2 |
3 | # Gson
4 | Parser and data collector in json format for Arduino
5 | - 7 times faster and much lighter than Arduinojson
6 | - JSON Parsing with error processing
7 | - Linear assembly of a json package
8 | - shielding "dangerous" symbols
9 | - works on the basis of Anytext (Stringutils)
10 | - works with 64 bit numbers
11 | - *The library is not suitable for storing data!Only parsing and assembly from scratch*
12 |
13 | ## compatibility
14 | Compatible with all arduino platforms (used arduino functions)
15 |
16 | ### Dependencies
17 | - Library [Stringutils] (https://github.com/gyverlibs/stringutils)
18 |
19 | ## Content
20 | - [documentation] (#docs)
21 | - [use] (#usage)
22 | - [versions] (#varsions)
23 | - [installation] (# Install)
24 | - [bugs and feedback] (#fedback)
25 |
26 |
27 |
28 | ## Documentation
29 | ### `gson :: doc`
30 | `` `CPP
31 | // Designer
32 | Gson :: doc ();
33 | GSON :: doc (size);
34 | GSON :: doCstatic ();
35 |
36 | // Methods
37 | uint16_t Length ();// get the number of elements
38 | uint16_t size ();// get the size of the document in RAM (byte)
39 | Void Hashkeys ();// Heshcht the keys of all elements (the operation is irreversible)
40 | Bool Hashed ();// Checking whether the keys were drunken
41 |
42 | Entry Get (Anytext Key);// Key access (main container - Object)
43 | Entry Get (Size_t Hash);// Access to Hash Key (main container - Object)
44 | Entry Get (Int Index);// Access by index (main container - Array or Object)
45 |
46 | Anytext Key (Intx);// Read the key on the index
47 | size_t keyhash (int IDX);// Read the Hesh key on the index
48 | Anytext Value (Intx);// Read the value by index
49 | int8_t part (int itx);// Read the parent on the index
50 | Type Type (int IDX);// get the type of index
51 |
52 | const __flashstringhelper* readtype (Uint16_T IDX);// Read the type of index
53 |
54 | // Parish.True will return with successful parsing.You can specify Max.The level of investment
55 | Bool Parse (String & Json, Uint8_t Maxdepth = 16);
56 | Bool Parse (Const Char* Json, Uint8_t Maxdepth = 16);
57 |
58 | // Bring out to Print with formatting
59 | VOID stringify (print& p);
60 |
61 | // error processing
62 | Bool Haserror ();// there is a parsing error
63 | Error Geterror ();// get an error
64 | uint16_t errorindex ();// Index of the place of error in the line
65 | const __flashstringhelper* readerror ();// Read the error
66 | `` `
67 |
68 | ### limits
69 | - After parsing, one element weighs 6 bytes on AVR and 12 bytes on ESP
70 | - the maximum number of elements (meter restriction) - 255 on AVR and 65535 on ESP
71 | - The maximum level of investment is set in the function of `PARSE ()`.Parsing is recursive, each level adds several bytes to the RAM
72 |
73 | ### tests
74 | He tested on ESP8266, a package of messages from the bottle - 3500 characters, 8 messages, 180 "elements".He received the value of the most distant and invested element, in the gson - through the hash.Result:
75 |
76 | |Libe |Flash |Sram |Freeheap |Parse |Hash |Get |
77 | | ------------ | -------- | ------ | --------- | ------------| -------- | ------- |
78 | |Arduinojson |285525 |28256 |45648 |9900 US |- |158 US |
79 | |Gson |275193 |28076 |46664 |1400 US |324 US |156 US |
80 |
81 | Thus, Gson ** is 7 times faster ** during parsing, the values of the elements are obtained withis the same speed.The library itself is easier to 10 kb in Flash and 1 KB in Heap.In other tests (AVR) to obtain the GSON value with a hasa, it worked on average 1.5 times faster.
82 |
83 | ### `GSON :: Type`
84 | `` `CPP
85 | None
86 | Object
87 | Array
88 | String
89 | Int
90 | Float
91 | Bool
92 | `` `
93 |
94 | ### `gson :: error`
95 | `` `CPP
96 | None
97 | Alloc
98 | Toodeep
99 | NopARENT
100 | NotContainer
101 | UNEXCOMMA
102 | Unexcolon
103 | UNEXTOKEN
104 | Unexquotes
105 | Unexopen
106 | Unexclose
107 | Unknowntoken
108 | Brokentoken
109 | Brokenstring
110 | BrokenContainer
111 | Emptyky
112 | Indexoverflow
113 | `` `
114 |
115 | ### `GSON :: Entry`
116 | Also inherits everything from `Anytext`, documentation [here] (https://github.com/gyverlibs/stringutils?tab=Readme-ov-file#anytext)
117 |
118 | `` `CPP
119 | Entry Get (Anytext Key);// get an element by key
120 | Bool Includes (Anytext Key);// contains an element with the specified key
121 |
122 | Entry Get (Size_t Hash);// Get the element according to the Key Hash
123 | Bool Includes (Size_t Hash);// contains an element with the indicated Key Hash
124 |
125 | Entry Get (Int Index);// Get an index element
126 |
127 | Bool Valid ();// Checking correctness (existence)
128 | uint16_t Length ();// Get the size (for objects and arrays. For the rest 0)
129 | Type type ();// get the type of element
130 | Anytext Key ();// Get the key
131 | size_t keyhash ();// Get a keys of the key
132 | Anytext Value ();// get a value
133 | `` `
134 |
135 | ### `GSON :: String`
136 | `` `CPP
137 | String S;// Access to the line
138 | Void Clear ();// Clean the line
139 | Bool Reserve (Uint16_T Res);// Reserve a line
140 |
141 | // Add Gson :: String.A comma will be added
142 | String & Add (Constation String & STR);
143 |
144 | // Add the key (string of any type)
145 | String & Addkey (Anytext Key);
146 |
147 | // add text (string of any type)
148 | String & Addraw (Anytext Str, Bool ESC = FALSE);
149 |
150 | // Add a line (a string of any type)
151 | String & Addstr (Anytext Key, Anytext Value);
152 | String & Addstr (Anytext Value);
153 |
154 | // Add bool
155 | String & Addbool (Anytext Key, Const Bool & Value);
156 | String & Addbool (Const Bool & Value);
157 |
158 | // Add Float
159 | String & Addfloat (Anytext Key, const Double & Value, Uint8_t Dec = 2);
160 | String & Addfloat (Constance Double & Value, Uint8_t Dec = 2);
161 |
162 | // Add int
163 | String & Addint (Anytext Key, Const ANYVALUE & VALUE);
164 | String & Addint (Const ANYVALUE & VALUE);
165 |
166 | String & Beginobj (Anytext Key = "");// Start an object
167 | String & Endobj ();// Complete the object
168 |
169 | String & Beginarr (Anytext Key = "");// Start an array
170 | String & Endarr ();// End the array
171 |
172 | String & End ();// complete the package
173 | `` `
174 |
175 |
176 |
177 | ## Usage
178 | ### Parsing
179 | Peculiarities:
180 | - The library ** does not duplicate the json line ** in memory, but works with the original line, preserving the signs on the values
181 | - Parsing ** Changes the initial line **: some characters are replaced by `'\ 0'`
182 |
183 | It follows that:
184 | - the line must exist in memory throughout work with JSON document
185 | - If the initial line - `string` - it should categorically not change the program until the end of work with the document
186 |
187 | Creating a document:
188 | `` `CPP
189 | Gson :: doc doc;// Dynamic document
190 | Gson :: doc doc (10);// Dynamic document reserved for 10 elements
191 | GSON :: doCstatic <10> doc;// Static document reserved for 10 elements
192 | `` `
193 |
194 | The meaning is like `String`-string: the dynamic document will increase during parsing in the dynamic memory of MK, if the size of the document is unknown.You can reserve the size in advance so that Parsing occurs faster.Static - stands out statically using less memory on weak platforms.
195 |
196 | `` `CPP
197 | // received json
198 | Char JSON [] = R "BIN (" Key ": Value," Int ": 12345," Obj ": {" Float ": 3.14," Bool ": FALSE}," arr ":" Hello ", true]}) BIN ";
199 | String json = "\" key \ ": \" value \ ", \" int \ ": 12345, \" Obj \ ": {\" float \ ": 3.14, \" bool \ ": false}, \ \"Arr \": [\ "Hello \", true]}; ";
200 |
201 | // Parish
202 | doc.parse (json);
203 |
204 | // error processing
205 | if (doc.Haserror ()) {
206 | Serial.print (doc.earaderror ());
207 | Serial.print ("in");
208 | Serial.println (doc.errorindex ());
209 | } Else serial.println ("Done");
210 | `` `
211 |
212 | After parsing, you can derive the entire package with types, keys, values in the form of text and parent:
213 | ```CPP
214 | for (uint16_t i = 0; i The weight of one "element" of any type (line, number, object, array) is 6 bytes on AVR and 10 bytes on ESP.Thus, after parsing, 9 elements (54 bytes) will additionally appear in the memory.
230 |
231 | The values can be obtained in the type of `Anytext`, which can convert to other types and displayed into the port:
232 | - the key can be a line in any form (`" line "`, `f (" line ")`)
233 | - you can contact the nested objects on the key, and to the arrays on the index
234 |
235 | `` `CPP
236 | Serial.println (doc ["key"]);// Value
237 | Serial.println (doc [f ("int")]);// 12345
238 | int Val = doc ["int"]. toint16 ();// Conversion into the specified type
239 | val = doc ["int"];// Auto Convertation
240 | Float F = doc ["Obj"] ["Float"];// invested object
241 | Serial.println (doc ["arr"] [0]);// Hello
242 | Serial.println (doc ["arr"] [1]);// True
243 |
244 | // Type check
245 | doc ["arr"]. Type () == gson :: type :: array;
246 |
247 | // Conclusion of the contents of the array
248 | for (int i = 0; i
339 |
340 | ## versions
341 | - V1.0
342 | - V1.1 - Improved Parser, added keys and harsh codes.
343 |
344 |
345 |
346 | ## Installation
347 | - The library can be found by the name ** Gson ** and installed through the library manager in:
348 | - Arduino ide
349 | - Arduino ide v2
350 | - Platformio
351 | - [download the library] (https://github.com/gyverlibs/gson/archive/refs/heads/main.zip). Zip archive for manual installation:
352 | - unpack and put in * C: \ Program Files (X86) \ Arduino \ Libraries * (Windows X64)
353 | - unpack and put in * C: \ Program Files \ Arduino \ Libraries * (Windows X32)
354 | - unpack and put in *documents/arduino/libraries/ *
355 | - (Arduino id) Automatic installation from. Zip: * sketch/connect the library/add .Zip library ... * and specify downloaded archive
356 | - Read more detailed instructions for installing libraries [here] (https://alexgyver.ru/arduino-first/#%D0%A3%D1%81%D1%82%D0%B0%BD%D0%BE%BE%BE%BED0%B2%D0%BA%D0%B0_%D0%B1%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA)
357 |
358 | ### Update
359 | - I recommend always updating the library: errors and bugs are corrected in the new versions, as well as optimization and new features are added
360 | - through the IDE library manager: find the library how to install and click "update"
361 | - Manually: ** remove the folder with the old version **, and then put a new one in its place.“Replacement” cannot be done: sometimes in new versions, files that remain when replacing are deleted and can lead to errors!
362 |
363 |
364 | ## bugs and feedback
365 | Create ** Issue ** when you find the bugs, and better immediately write to the mail [alex@alexgyver.ru] (mailto: alex@alexgyver.ru)
366 | The library is open for refinement and your ** pull Request ** 'ow!
367 |
368 | When reporting about bugs or incorrect work of the library, it is necessary to indicate:
369 | - The version of the library
370 | - What is MK used
371 | - SDK version (for ESP)
372 | - version of Arduino ide
373 | - whether the built -in examples work correctly, in which the functions and designs are used, leading to a bug in your code
374 | - what code has been loaded, what work was expected from it and how it works in reality
375 | - Ideally, attach the minimum code in which the bug is observed.Not a canvas of a thousand lines, but a minimum code
--------------------------------------------------------------------------------
/examples/parseTo/parseTo.ino:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | void setup() {
5 | gson::Parser p;
6 | p.parse("[1,2,3]");
7 | p.stringify(Serial);
8 |
9 | int arr[3];
10 | p.parseTo(arr);
11 | Serial.println(arr[0]);
12 | Serial.println(arr[1]);
13 | Serial.println(arr[2]);
14 | }
15 |
16 | void loop() {
17 | }
--------------------------------------------------------------------------------
/examples/test/test.ino:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | void setup() {
5 | Serial.begin(115200);
6 | Serial.println();
7 |
8 | // gson::string gs; // создать строку
9 | // gs.beginObj(); // начать объект
10 | // gs.addString("str1", F("value")); // добавить строковое значение
11 | // gs["str2"] = "value2"; // так тоже можно
12 | // gs["int"] = (int32_t)12345; // целочисленное
13 | // gs.beginObj("obj"); // вложенный объект
14 | // gs.addFloat(F("float"), 3.14); // float
15 | // gs["float2"] = 3.14; // или так
16 | // gs["bool"] = false; // Bool значение
17 | // gs.endObj(); // завершить объект
18 |
19 | // gs.beginObj("obj2");
20 | // gs.beginArr("array");
21 | // gs.addFloat(3.14);
22 | // gs += "text"; // в массиве можно так
23 | // gs += 12345;
24 | // gs += true;
25 | // gs.endArr();
26 | // gs.endObj();
27 | // gs["last"] = "kekpek";
28 |
29 | // gs.endObj(); // завершить объект
30 | // gs.end(); // завершить пакет
31 |
32 | gson::Str gs; // создать строку
33 | gs('{'); // начать объект
34 | gs["str1"] = F("value"); // добавить строковое значение
35 | gs["str2"] = "value2"; // так тоже можно
36 | gs["int"] = (int32_t)12345; // целочисленное
37 | gs["obj"]('{'); // вложенный объект
38 | gs[F("float")] = 3.14; // float
39 | gs["float2"] = 3.14; // или так
40 | gs["bool"] = false; // Bool значение
41 | gs('}'); // завершить объект
42 |
43 | gs["obj2"]('{');
44 | gs["array"]('[');
45 | gs.add(3.14, 1);
46 | gs += "text"; // в массиве можно так
47 | gs += 12345;
48 | gs += true;
49 | gs(']');
50 | gs('}');
51 | gs["last"] = "kekpek";
52 |
53 | gs('}'); // завершить объект
54 |
55 | Serial.println("==== PACKET ====");
56 | Serial.println(gs);
57 | Serial.println();
58 |
59 | gson::Parser p(10);
60 | p.parse(gs);
61 |
62 | Serial.println("==== READ ====");
63 | Serial.println(p[0]);
64 | Serial.println(p["str2"]);
65 | Serial.println(p[F("int")]);
66 | Serial.println(p["obj"]["float"]);
67 | Serial.println(p["obj2"]["array"][0]);
68 | Serial.println(p["obj2"]["array"][1]);
69 | Serial.println(p["obj2"]["array"][2]);
70 |
71 | Serial.println(p["no-key"]);
72 | Serial.println(p["no-key"]["no-key"]);
73 | Serial.println();
74 |
75 | p.hashKeys();
76 | Serial.println("==== HASH ACCESS ====");
77 | Serial.println(p[SH("str2")]);
78 | Serial.println(p[SH("obj")][SH("float")]);
79 | Serial.println();
80 |
81 | Serial.println("==== CHUNKS ====");
82 | for (int i = 0; i < p.length(); i++) {
83 | Serial.print(i);
84 | Serial.print(". [");
85 | Serial.print(p.readType(i));
86 | Serial.print("] ");
87 | Serial.print(p.key(i));
88 | Serial.print(":");
89 | Serial.print(p.value(i));
90 | Serial.print(" {");
91 | Serial.print(p.parent(i));
92 | Serial.println("}");
93 | }
94 | Serial.println();
95 |
96 | Serial.println("==== STRINGIFY ====");
97 | p.stringify(Serial);
98 | Serial.println();
99 |
100 | Serial.println("==== STRINGIFY ====");
101 | p[SH("obj2")].stringify(Serial);
102 | Serial.println();
103 |
104 | Serial.println("==== STRINGIFY ====");
105 | p[SH("obj")][SH("float2")].stringify(Serial);
106 | Serial.println();
107 |
108 | Serial.println("==== STRINGIFY ====");
109 | p[SH("int")].stringify(Serial);
110 | Serial.println();
111 | }
112 |
113 | void loop() {
114 | }
--------------------------------------------------------------------------------
/keywords.txt:
--------------------------------------------------------------------------------
1 | #######################################
2 | # Syntax Coloring Map For GSON
3 | #######################################
4 |
5 | #######################################
6 | # Datatypes (KEYWORD1)
7 | #######################################
8 |
9 | GSON KEYWORD1
10 | Entry KEYWORD1
11 | Parser KEYWORD1
12 | ParserStream KEYWORD1
13 | Type KEYWORD1
14 | Error KEYWORD1
15 |
16 | GSON_NO_LIMITS KEYWORD1
17 |
18 | #######################################
19 | # Methods and Functions (KEYWORD2)
20 | #######################################
21 |
22 | # parser
23 | parseTo KEYWORD2
24 | reset KEYWORD2
25 | get KEYWORD2
26 | valid KEYWORD2
27 | key KEYWORD2
28 | value KEYWORD2
29 | length KEYWORD2
30 | type KEYWORD2
31 | has KEYWORD2
32 | parent KEYWORD2
33 | keyHash KEYWORD2
34 | hashKeys KEYWORD2
35 | hashed KEYWORD2
36 | size KEYWORD2
37 | readType KEYWORD2
38 | parse KEYWORD2
39 | stringify KEYWORD2
40 | hasError KEYWORD2
41 | getError KEYWORD2
42 | errorIndex KEYWORD2
43 | readError KEYWORD2
44 | setMaxDepth KEYWORD2
45 | getByIndex KEYWORD2
46 | getRaw KEYWORD2
47 | rootLength KEYWORD2
48 |
49 | # entry
50 | isContainer KEYWORD2
51 | isObject KEYWORD2
52 | isArray KEYWORD2
53 | is KEYWORD2
54 | index KEYWORD2
55 |
56 | # string
57 | clear KEYWORD2
58 | reserve KEYWORD2
59 | end KEYWORD2
60 | add KEYWORD2
61 | addKey KEYWORD2
62 | addText KEYWORD2
63 | addTextRaw KEYWORD2
64 | addTextEsc KEYWORD2
65 | addTextRawEsc KEYWORD2
66 | addText KEYWORD2
67 | addTextEsc KEYWORD2
68 | addString KEYWORD2
69 | addStringEsc KEYWORD2
70 | addStringRaw KEYWORD2
71 | addStringRawEsc KEYWORD2
72 | addBool KEYWORD2
73 | addFloat KEYWORD2
74 | addInt KEYWORD2
75 | addBoolRaw KEYWORD2
76 | addFloatRaw KEYWORD2
77 | addIntRaw KEYWORD2
78 | beginObj KEYWORD2
79 | endObj KEYWORD2
80 | beginArr KEYWORD2
81 | endArr KEYWORD2
82 | quotes KEYWORD2
83 | comma KEYWORD2
84 | colon KEYWORD2
85 | escapeDefault KEYWORD2
86 | replaceComma KEYWORD2
87 |
88 | #######################################
89 | # Constants (LITERAL1)
90 | #######################################
91 |
92 | None LITERAL1
93 | Alloc LITERAL1
94 | TooDeep LITERAL1
95 | NoParent LITERAL1
96 | NotContainer LITERAL1
97 | UnexComma LITERAL1
98 | UnexColon LITERAL1
99 | UnexToken LITERAL1
100 | UnexQuotes LITERAL1
101 | UnexOpen LITERAL1
102 | UnexClose LITERAL1
103 | UnknownToken LITERAL1
104 | BrokenToken LITERAL1
105 | BrokenString LITERAL1
106 | BrokenContainer LITERAL1
107 | EmptyKey LITERAL1
108 | IndexOverflow LITERAL1
109 | LongPacket LITERAL1
110 | LongKey LITERAL1
111 | EmptyString LITERAL1
112 |
113 | Object LITERAL1
114 | Array LITERAL1
115 | String LITERAL1
116 | Int LITERAL1
117 | Float LITERAL1
118 | Bool LITERAL1
--------------------------------------------------------------------------------
/library.properties:
--------------------------------------------------------------------------------
1 | name=GSON
2 | version=1.8.0
3 | author=AlexGyver
4 | maintainer=AlexGyver
5 | sentence=Light JSON parsing and assembling library for Arduino
6 | paragraph=Light JSON parsing and assembling library for Arduino
7 | category=Data Processing
8 | url=https://github.com/GyverLibs/GSON
9 | architectures=*
10 | depends=StringUtils,GTL
11 |
--------------------------------------------------------------------------------
/src/GSON.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "utils/parser.h"
4 | #include "utils/parser_stream.h"
5 | #include "utils/str.h"
6 | #include "utils/string.h"
7 |
8 | namespace GSON = gson;
9 |
--------------------------------------------------------------------------------
/src/utils/bson.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | // библиотека устарела и переехала в отдельный репозиторий https://github.com/GyverLibs/BSON
8 | // бинарный JSON, может распаковываться в обычный. Структура данных:
9 | /*
10 | 0 key code: [code msb:5] + [code:8]
11 | 1 key str: [len msb:5] + [len:8] + [...]
12 | 2 val code: [code msb:5] + [code:8]
13 | 3 val str: [len msb:5] + [len:8] + [...]
14 | 4 val int: [sign:1 + len:4]
15 | 5 val float: [dec:5]
16 | 6 cont: [obj:1 / arr:0] [open:1 / close:0]
17 | 7 bin: [len msb:5] + [len:8] + [...]
18 | */
19 |
20 | #define BS_MAX_LEN ((size_t)0b0001111111111111)
21 |
22 | #define BS_KEY_CODE (0 << 5)
23 | #define BS_KEY_STR (1 << 5)
24 | #define BS_VAL_CODE (2 << 5)
25 | #define BS_VAL_STR (3 << 5)
26 | #define BS_VAL_INT (4 << 5)
27 | #define BS_VAL_FLOAT (5 << 5)
28 | #define BS_CONTAINER (6 << 5)
29 | #define BS_BINARY (7 << 5)
30 |
31 | #define BS_CONT_OBJ (1 << 4)
32 | #define BS_CONT_ARR (0 << 4)
33 | #define BS_CONT_OPEN (1 << 3)
34 | #define BS_CONT_CLOSE (0 << 3)
35 |
36 | #define BS_NEGATIVE (0b00010000)
37 | #define BS_MSB5(x) ((x >> 8) & 0b00011111)
38 | #define BS_LSB5(x) (x & 0b00011111)
39 | #define BS_LSB(x) (x & 0xff)
40 |
41 | class BSON : private gtl::stack_uniq {
42 | public:
43 | using gtl::stack_uniq::write;
44 | using gtl::stack_uniq::reserve;
45 | using gtl::stack_uniq::length;
46 | using gtl::stack_uniq::buf;
47 | using gtl::stack_uniq::clear;
48 | using gtl::stack_uniq::move;
49 |
50 | BSON() {}
51 | BSON(BSON& b) {
52 | move(b);
53 | }
54 | BSON(BSON&& b) {
55 | move(b);
56 | }
57 | BSON& operator=(BSON& b) {
58 | move(b);
59 | return *this;
60 | }
61 | BSON& operator=(BSON&& b) {
62 | move(b);
63 | return *this;
64 | }
65 |
66 | operator Text() {
67 | return toText();
68 | }
69 |
70 | Text toText() {
71 | return Text(buf(), length());
72 | }
73 |
74 | // bson
75 | void add(const BSON& bson) {
76 | concat(bson);
77 | }
78 | void operator+=(const BSON& bson) {
79 | concat(bson);
80 | }
81 |
82 | // key
83 | void addKey(uint16_t key) {
84 | push(BS_KEY_CODE | (key & 0b000));
85 | push(BS_LSB(key));
86 | }
87 | void addKey(const Text& key) {
88 | _text(key, BS_KEY_STR);
89 | }
90 |
91 | BSON& operator[](uint16_t key) {
92 | addKey(key);
93 | return *this;
94 | }
95 | BSON& operator[](Text key) {
96 | addKey(key);
97 | return *this;
98 | }
99 |
100 | // code
101 | void addCode(uint16_t key, uint16_t val) {
102 | reserve(length() + 5);
103 | addKey(key);
104 | addCode(val);
105 | }
106 | void addCode(const Text& key, uint16_t val) {
107 | addKey(key);
108 | addCode(val);
109 | }
110 | void addCode(uint16_t val) {
111 | push(BS_VAL_CODE | BS_MSB5(val));
112 | push(BS_LSB(val));
113 | }
114 |
115 | // bool
116 | void addBool(bool b) {
117 | addUint(b);
118 | }
119 | void addBool(uint16_t key, bool b) {
120 | addKey(key);
121 | addUint(b);
122 | }
123 | void addBool(const Text& key, bool b) {
124 | addKey(key);
125 | addUint(b);
126 | }
127 |
128 | // uint
129 | template
130 | void addUint(T val) {
131 | uint8_t len = _uintSize(val);
132 | push(BS_VAL_INT | len);
133 | concat((uint8_t*)&val, len);
134 | }
135 | void addUint(unsigned long long val) {
136 | uint8_t len = _uint64Size(val);
137 | push(BS_VAL_INT | len);
138 | concat((uint8_t*)&val, len);
139 | }
140 | template
141 | void addUint(uint16_t key, T val) {
142 | addKey(key);
143 | addUint(val);
144 | }
145 | template
146 | void addUint(const Text& key, T val) {
147 | addKey(key);
148 | addUint(val);
149 | }
150 |
151 | // int
152 | template
153 | void addInt(T val) {
154 | uint8_t neg = (val < 0) ? BS_NEGATIVE : 0;
155 | if (neg) val = -val;
156 | uint8_t len = _uintSize(val);
157 | push(BS_VAL_INT | neg | len);
158 | concat((uint8_t*)&val, len);
159 | }
160 | void addInt(long long val) {
161 | uint8_t neg = (val < 0) ? BS_NEGATIVE : 0;
162 | if (neg) val = -val;
163 | uint8_t len = _uint64Size(val);
164 | push(BS_VAL_INT | neg | len);
165 | concat((uint8_t*)&val, len);
166 | }
167 | template
168 | void addInt(uint16_t key, T val) {
169 | addKey(key);
170 | addInt(val);
171 | }
172 | template
173 | void addInt(const Text& key, T val) {
174 | addKey(key);
175 | addInt(val);
176 | }
177 |
178 | // float
179 | template
180 | void addFloat(T value, uint8_t dec) {
181 | push(BS_VAL_FLOAT | BS_LSB5(dec));
182 | float f = value;
183 | concat((uint8_t*)&f, 4);
184 | }
185 | template
186 | void addFloat(uint16_t key, T value, uint8_t dec) {
187 | addKey(key);
188 | addFloat(value, dec);
189 | }
190 | template
191 | void addFloat(const Text& key, T value, uint8_t dec) {
192 | addKey(key);
193 | addFloat(value, dec);
194 | }
195 |
196 | #define BSON_MAKE_ADD_STR(type) \
197 | void operator=(type val) { addText(val); } \
198 | void operator+=(type val) { addText(val); }
199 |
200 | #define BSON_MAKE_ADD_INT(type) \
201 | void operator=(type val) { addInt(val); } \
202 | void operator+=(type val) { addInt(val); }
203 |
204 | #define BSON_MAKE_ADD_UINT(type) \
205 | void operator=(type val) { addUint(val); } \
206 | void operator+=(type val) { addUint(val); }
207 |
208 | #define BSON_MAKE_ADD_FLOAT(type) \
209 | void operator=(type val) { addFloat(val, 4); } \
210 | void operator+=(type val) { addFloat(val, 4); }
211 |
212 | BSON_MAKE_ADD_STR(const char*)
213 | BSON_MAKE_ADD_STR(const __FlashStringHelper*)
214 | BSON_MAKE_ADD_STR(const String&)
215 | BSON_MAKE_ADD_STR(const Text&)
216 |
217 | BSON_MAKE_ADD_UINT(bool)
218 | BSON_MAKE_ADD_UINT(char)
219 | BSON_MAKE_ADD_UINT(unsigned char)
220 | BSON_MAKE_ADD_UINT(unsigned short)
221 | BSON_MAKE_ADD_UINT(unsigned int)
222 | BSON_MAKE_ADD_UINT(unsigned long)
223 | BSON_MAKE_ADD_UINT(unsigned long long)
224 |
225 | BSON_MAKE_ADD_INT(signed char)
226 | BSON_MAKE_ADD_INT(short)
227 | BSON_MAKE_ADD_INT(int)
228 | BSON_MAKE_ADD_INT(long)
229 | BSON_MAKE_ADD_INT(long long)
230 |
231 | BSON_MAKE_ADD_FLOAT(float)
232 | BSON_MAKE_ADD_FLOAT(double)
233 |
234 | // text
235 | void addText(const Text& text) {
236 | _text(text, BS_VAL_STR);
237 | }
238 | void addText(uint16_t key, const Text& text) {
239 | reserve(length() + text.length() + 5);
240 | addKey(key);
241 | _text(text, BS_VAL_STR);
242 | }
243 | void addText(const Text& key, const Text& text) {
244 | addKey(key);
245 | _text(text, BS_VAL_STR);
246 | }
247 |
248 | // bin
249 | void addBin(const void* data, size_t size) {
250 | if (size > BS_MAX_LEN) return;
251 | beginBin(size);
252 | write((const uint8_t*)data, size);
253 | }
254 | void addBin(const Text& key, const void* data, size_t size) {
255 | if (size > BS_MAX_LEN) return;
256 | addKey(key);
257 | addBin(data, size);
258 | }
259 | void addBin(uint16_t key, const void* data, size_t size) {
260 | if (size > BS_MAX_LEN) return;
261 | addKey(key);
262 | addBin(data, size);
263 | }
264 | bool beginBin(uint16_t size) {
265 | if (size > BS_MAX_LEN) return false;
266 | push(BS_BINARY | BS_MSB5(size));
267 | push(BS_LSB(size));
268 | return true;
269 | }
270 |
271 | // object
272 | void beginObj() {
273 | push(BS_CONTAINER | BS_CONT_OBJ | BS_CONT_OPEN);
274 | }
275 | void beginObj(uint16_t key) {
276 | reserve(length() + 4);
277 | addKey(key);
278 | beginObj();
279 | }
280 | void beginObj(const Text& key) {
281 | addKey(key);
282 | beginObj();
283 | }
284 | void endObj() {
285 | push(BS_CONTAINER | BS_CONT_OBJ | BS_CONT_CLOSE);
286 | }
287 |
288 | // array
289 | void beginArr() {
290 | push(BS_CONTAINER | BS_CONT_ARR | BS_CONT_OPEN);
291 | }
292 | void beginArr(uint16_t key) {
293 | reserve(length() + 4);
294 | addKey(key);
295 | beginArr();
296 | }
297 | void beginArr(const Text& key) {
298 | addKey(key);
299 | beginArr();
300 | }
301 | void endArr() {
302 | push(BS_CONTAINER | BS_CONT_ARR | BS_CONT_CLOSE);
303 | }
304 |
305 | private:
306 | void _text(const Text& text, uint8_t type) {
307 | uint16_t len = min((size_t)text.length(), BS_MAX_LEN);
308 | reserve(length() + len + 3);
309 | push(type | BS_MSB5(len));
310 | push(BS_LSB(len));
311 | concat((const uint8_t*)text.str(), len, text.pgm());
312 | }
313 | uint8_t _uintSize(uint32_t val) {
314 | if (((uint8_t*)&val)[3]) return 4;
315 | if (((uint8_t*)&val)[2]) return 3;
316 | if (((uint8_t*)&val)[1]) return 2;
317 | if (((uint8_t*)&val)[0]) return 1;
318 | return 0;
319 | }
320 | uint8_t _uint64Size(uint64_t val) {
321 | return (val > ULONG_MAX) ? 8 : _uintSize(val);
322 | }
323 | };
324 |
--------------------------------------------------------------------------------
/src/utils/entry.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 |
5 | #include "entry_stack.h"
6 | #include "types.h"
7 |
8 | namespace gson {
9 |
10 | class Entry : public Text {
11 | public:
12 | Entry(const gsutil::EntryStack* ens = nullptr, parent_t idx = 0) : ens(ens), idx(idx) {
13 | if (_valid()) *((Text*)this) = ens->valueText(idx);
14 | }
15 |
16 | size_t printTo(Print& p) const {
17 | if (isContainer()) return stringify(p), 1;
18 | else return Text::printTo(p);
19 | }
20 |
21 | // ===================== BY KEY =====================
22 |
23 | // получить элемент по ключу
24 | Entry get(const Text& key) const {
25 | if (_valid() && ens->_get(idx).isObject()) {
26 | for (uint16_t i = idx + 1; i < ens->length(); i++) {
27 | if (ens->_get(i).parent == idx &&
28 | ens->_get(i).key_offs &&
29 | key.compare(ens->keyText(i))) return Entry(ens, i);
30 | }
31 | }
32 | return Entry();
33 | }
34 |
35 | // содержит элемент с указанным ключом
36 | bool has(const Text& key) const {
37 | return get(key).valid();
38 | }
39 |
40 | // доступ по ключу (контейнер - Object)
41 | Entry operator[](const char* key) const {
42 | return get(key);
43 | }
44 | Entry operator[](const __FlashStringHelper* key) const {
45 | return get(key);
46 | }
47 | Entry operator[](const String& key) const {
48 | return get(key);
49 | }
50 | Entry operator[](String& key) const {
51 | return get(key);
52 | }
53 |
54 | // ===================== BY HASH =====================
55 |
56 | // получить элемент по хэшу ключа
57 | Entry get(size_t hash) const {
58 | if (_valid() && ens->hashed() && ens->_get(idx).isObject()) {
59 | for (uint16_t i = idx + 1; i < ens->length(); i++) {
60 | if (ens->_get(i).parent == idx && ens->hash[i] == hash) return Entry(ens, i);
61 | }
62 | }
63 | return Entry();
64 | }
65 |
66 | // содержит элемент с указанным хэшем ключа
67 | bool has(size_t hash) const {
68 | return get(hash).valid();
69 | }
70 |
71 | // доступ по хэшу ключа (контейнер - Object)
72 | Entry operator[](size_t hash) const {
73 | return get(hash);
74 | }
75 |
76 | // ===================== BY INDEX =====================
77 |
78 | // получить элемент по индексу
79 | Entry get(int index) const {
80 | if (_valid() && (size_t)index < ens->length() && ens->_get(idx).isContainer()) {
81 | for (uint16_t i = idx + 1; i < ens->length(); i++) {
82 | if (ens->_get(i).parent == idx) {
83 | if (!index) return Entry(ens, i);
84 | --index;
85 | }
86 | }
87 | }
88 | return Entry();
89 | }
90 |
91 | // итерация по вложенным
92 | void loop(void (*cb)(Entry e)) {
93 | if (_valid() && ens->_get(idx).isContainer()) {
94 | for (uint16_t i = idx + 1; i < ens->length(); i++) {
95 | if (ens->_get(i).parent == idx) cb(Entry(ens, i));
96 | }
97 | }
98 | }
99 |
100 | // доступ по индексу (контейнер - Array или Object)
101 | Entry operator[](int index) const {
102 | return get(index);
103 | }
104 |
105 | // ===================== MISC =====================
106 |
107 | // декодировать UCN (unicode) в записи
108 | void decodeUCN() {
109 | gsutil::Entry_t& e = ens->_get(idx);
110 | e.val_len = su::unicode::decodeSelf((char*)e.value(ens->str), e.val_len);
111 | }
112 |
113 | // получить ключ
114 | Text key() const {
115 | return _valid() ? ens->keyText(idx) : Text();
116 | }
117 |
118 | // получить хэш ключа
119 | size_t keyHash() const {
120 | return _valid() ? (ens->hashed() ? ens->hash[idx] : ens->keyText(idx).hash()) : 0;
121 | }
122 | // без проверок
123 | size_t _keyHash() const {
124 | return ens->hash[idx];
125 | }
126 |
127 | // получить значение
128 | Text value() const {
129 | return *this;
130 | }
131 |
132 | // получить размер для объектов и массивов
133 | size_t length() const {
134 | if (!_valid() || !ens->_get(idx).isContainer()) return 0;
135 | size_t len = 0;
136 | for (size_t i = 0; i < ens->length(); i++) {
137 | if (ens->_get(i).parent == idx) len++;
138 | }
139 | return len;
140 | }
141 |
142 | // получить тип элемента
143 | gson::Type type() const {
144 | return _valid() ? ens->_get(idx).type : gson::Type::None;
145 | }
146 |
147 | // сравнить тип элемента
148 | bool is(gson::Type type) const {
149 | return _valid() ? ens->_get(idx).type == type : false;
150 | }
151 |
152 | // элемент Array или Object
153 | bool isContainer() const {
154 | return _valid() ? ens->_get(idx).isContainer() : false;
155 | }
156 |
157 | // элемент Object
158 | bool isObject() const {
159 | return _valid() ? ens->_get(idx).isObject() : false;
160 | }
161 |
162 | // элемент Array
163 | bool isArray() const {
164 | return _valid() ? ens->_get(idx).isArray() : false;
165 | }
166 |
167 | // вывести в Print с форматированием
168 | void stringify(Print& pr) const {
169 | if (!_valid()) return;
170 | if (ens->_get(idx).isContainer()) {
171 | uint8_t depth = 1;
172 | parent_t index = idx + 1;
173 | pr.println(ens->_get(idx).isObject() ? '{' : '[');
174 | _stringify(pr, index, ens->_get(index).parent, depth);
175 | pr.println();
176 | pr.print(ens->_get(idx).isObject() ? '}' : ']');
177 | } else {
178 | _print(pr, idx);
179 | }
180 | pr.println();
181 | }
182 |
183 | // парсить в массив длины length()
184 | template
185 | bool parseTo(T& arr) const {
186 | if (!isArray()) return false;
187 | for (size_t i = 0; i < length(); i++) {
188 | arr[i] = ens->valueText(idx + 1 + i);
189 | }
190 | return true;
191 | }
192 |
193 | // индекс элемента в общем массиве парсера
194 | parent_t index() {
195 | return idx;
196 | }
197 |
198 | // проверить коллизии хэшей в объектe
199 | bool checkCollisions(bool recursive = true) const {
200 | return (isObject() && ens->hashed()) ? _checkCollisions(*this, recursive) : 0;
201 | }
202 |
203 | void reset() {
204 | ens = nullptr;
205 | }
206 |
207 | // ===========================
208 | bool includes(size_t hash) const __attribute__((deprecated)) {
209 | return has(hash);
210 | }
211 | bool includes(const Text& key) const __attribute__((deprecated)) {
212 | return has(key);
213 | }
214 |
215 | private:
216 | const gsutil::EntryStack* ens = nullptr;
217 | parent_t idx = 0;
218 |
219 | // массив и строка существуют
220 | bool _valid() const {
221 | return ens && ens->valid();
222 | }
223 |
224 | void _printTab(Print& p, uint8_t amount) const {
225 | while (amount--) {
226 | p.print(' ');
227 | p.print(' ');
228 | }
229 | }
230 |
231 | void _print(Print& p, parent_t idx) const {
232 | gsutil::Entry_t& ent = ens->_get(idx);
233 | if (ent.key_offs) {
234 | p.print('\"');
235 | p.print(ens->keyText(idx));
236 | p.print("\":");
237 | }
238 | if (ent.is(gson::Type::String)) p.print('\"');
239 | switch ((gson::Type)ent.type) {
240 | case gson::Type::String:
241 | case gson::Type::Int:
242 | case gson::Type::Float:
243 | p.print(ens->valueText(idx));
244 | break;
245 | case gson::Type::Bool:
246 | p.print((ens->valueText(idx)[0] == 't') ? F("true") : F("false"));
247 | break;
248 | case gson::Type::Null:
249 | p.print(F("null"));
250 | break;
251 | default:
252 | break;
253 | }
254 | if (ent.is(gson::Type::String)) p.print('\"');
255 | }
256 |
257 | void _stringify(Print& p, parent_t& idx, parent_t parent, uint8_t& depth) const {
258 | bool first = true;
259 | while (idx < ens->length()) {
260 | gsutil::Entry_t& ent = ens->_get(idx);
261 | if (ent.parent != parent) return;
262 |
263 | if (first) first = false;
264 | else p.print(",\n");
265 |
266 | if (ent.isContainer()) {
267 | _printTab(p, depth);
268 | if (ent.key_offs) {
269 | p.print('\"');
270 | p.print(ens->keyText(idx));
271 | p.print("\": ");
272 | }
273 | p.print((ent.isArray()) ? '[' : '{');
274 | p.print('\n');
275 | parent_t prev = idx;
276 | ++idx;
277 | ++depth;
278 | _stringify(p, idx, prev, depth);
279 | --depth;
280 | p.print('\n');
281 | _printTab(p, depth);
282 | p.print((ent.isArray()) ? ']' : '}');
283 | } else {
284 | _printTab(p, depth);
285 | _print(p, idx);
286 | ++idx;
287 | }
288 | }
289 | }
290 |
291 | static bool _checkCollisions(const Entry& ent, bool recursive) {
292 | int16_t len = ent.length();
293 | for (int16_t i = 0; i < len; i++) {
294 | Entry e = ent.get(i);
295 | for (int16_t j = i + 1; j < len; j++) {
296 | if (e.keyHash() == ent.get(j).keyHash()) return 1;
297 | }
298 | if (recursive && e.isObject() && _checkCollisions(e, true)) return 1;
299 | }
300 | return 0;
301 | }
302 | };
303 |
304 | } // namespace gson
--------------------------------------------------------------------------------
/src/utils/entry_stack.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 |
5 | #include "entry_t.h"
6 |
7 | namespace gsutil {
8 | class EntryStack : public gtl::stack {
9 | typedef gtl::stack ST;
10 |
11 | public:
12 | void hashKeys() {
13 | if (valid() && hash.resize(length())) {
14 | for (uint16_t i = 0; i < length(); i++) {
15 | hash[i] = keyText(i).hash();
16 | }
17 | }
18 | }
19 |
20 | // ключи хешированы
21 | bool hashed() const {
22 | return hash.size() == length();
23 | }
24 |
25 | // освободить память
26 | void reset() {
27 | hash.reset();
28 | ST::reset();
29 | }
30 |
31 | // очистить буфер для следующего парсинга
32 | void clear() {
33 | hash.reset();
34 | ST::clear();
35 | }
36 |
37 | inline const char* key(int idx) const {
38 | return _get(idx).key(str);
39 | }
40 | inline const char* value(int idx) const {
41 | return _get(idx).value(str);
42 | }
43 |
44 | inline Text keyText(int idx) const {
45 | return _get(idx).keyText(str);
46 | }
47 | inline Text valueText(int idx) const {
48 | return _get(idx).valueText(str);
49 | }
50 |
51 | size_t getHash(int idx) {
52 | return hash ? hash[idx] : 0;
53 | }
54 |
55 | // буфер и строка существуют
56 | bool valid() const {
57 | return ST::valid() && str;
58 | }
59 |
60 | const char* str = nullptr;
61 | gtl::array hash;
62 | };
63 |
64 | } // namespace gsutil
--------------------------------------------------------------------------------
/src/utils/entry_t.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 | #include
5 |
6 | #include "types.h"
7 |
8 | namespace gson {
9 | #ifdef GSON_NO_LIMITS
10 | #define GSON_MAX_KEY_LEN 256
11 | #if (UINT_MAX == UINT32_MAX)
12 | #define GSON_PARENT_BIT 16
13 | typedef uint16_t parent_t;
14 | #else
15 | #define GSON_PARENT_BIT 8
16 | typedef uint8_t parent_t;
17 | #endif
18 |
19 | #else // GSON_NO_LIMITS
20 | #define GSON_MAX_KEY_LEN 32
21 | #if (UINT_MAX == UINT32_MAX)
22 | #define GSON_PARENT_BIT 9
23 | typedef uint16_t parent_t;
24 | #else
25 | #define GSON_PARENT_BIT 8
26 | typedef uint8_t parent_t;
27 | #endif
28 |
29 | #endif // GSON_NO_LIMITS
30 | } // namespace gson
31 |
32 | #define GSON_MAX_LEN 0xffff
33 | #define GSON_MAX_INDEX ((1 << GSON_PARENT_BIT) - 1)
34 |
35 | namespace gsutil {
36 |
37 | struct Entry_t {
38 | #ifdef GSON_NO_LIMITS
39 | gson::parent_t parent; // 16/8 bit
40 | gson::Type type; // 8 bit
41 | uint8_t key_len; // 8 bit
42 | uint16_t val_len; // 16 bit
43 | #else // GSON_NO_LIMITS
44 | gson::parent_t parent : GSON_PARENT_BIT; // 512/256
45 | gson::Type type : 3; // 8
46 | uint8_t key_len : 5; // 32
47 | uint16_t val_len : 15; // 32 768
48 | #endif // GSON_NO_LIMITS
49 |
50 | uint16_t key_offs;
51 | uint16_t val_offs;
52 |
53 | void reset() {
54 | key_offs = val_offs = key_len = val_len = 0;
55 | type = gson::Type::None;
56 | }
57 |
58 | inline const char* key(const char* json) const {
59 | return json + key_offs;
60 | }
61 | inline const char* value(const char* json) const {
62 | return json + val_offs;
63 | }
64 |
65 | inline Text keyText(const char* json) const {
66 | return Text(key(json), key_len);
67 | }
68 | inline Text valueText(const char* json) const {
69 | return Text(value(json), val_len);
70 | }
71 |
72 | inline bool is(gson::Type t) const {
73 | return type == t;
74 | }
75 | bool isContainer() const {
76 | return is(gson::Type::Array) || is(gson::Type::Object);
77 | }
78 | inline bool isObject() const {
79 | return is(gson::Type::Object);
80 | }
81 | inline bool isArray() const {
82 | return is(gson::Type::Array);
83 | }
84 | };
85 |
86 | } // namespace gsutil
--------------------------------------------------------------------------------
/src/utils/parser.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 |
5 | #include "entry.h"
6 | #include "entry_stack.h"
7 | #include "types.h"
8 |
9 | namespace gson {
10 |
11 | // ================== PARSER ==================
12 | class Parser {
13 | private:
14 | enum class State : uint8_t {
15 | Idle,
16 | WaitKey,
17 | WaitValue,
18 | WaitColon,
19 | };
20 |
21 | public:
22 | Parser(size_t size = 0) {
23 | reserve(size);
24 | }
25 |
26 | // зарезервировать память для ускорения парсинга
27 | bool reserve(size_t size) {
28 | return ents.reserve(size);
29 | }
30 |
31 | // освободить память
32 | void reset() {
33 | ents.reset();
34 | strp = nullptr;
35 | error = Error::None;
36 | }
37 |
38 | // очистить для нового парсинга
39 | void clear() {
40 | ents.clear();
41 | strp = nullptr;
42 | error = Error::None;
43 | }
44 |
45 | // получить количество элементов
46 | uint16_t length() const {
47 | return ents.length();
48 | }
49 |
50 | // получить размер документа в оперативной памяти (байт)
51 | uint16_t size() const {
52 | return length() * sizeof(gsutil::Entry_t);
53 | }
54 |
55 | // установить максимальную глубину вложенности парсинга (умолч. 16)
56 | void setMaxDepth(uint8_t maxdepth) {
57 | depth = maxdepth;
58 | }
59 |
60 | // хешировать ключи всех элементов (операция необратима)
61 | void hashKeys() {
62 | ents.hashKeys();
63 | }
64 |
65 | // проверить коллизии хэшей в объектах
66 | bool checkCollisions(bool recursive = true) const {
67 | return length() ? Entry(&ents, 0).checkCollisions(recursive) : false;
68 | }
69 |
70 | // проверка были ли хешированы ключи
71 | bool hashed() const {
72 | return ents.hashed();
73 | }
74 |
75 | // получить количество элементов в главном контейнере
76 | uint16_t rootLength() const {
77 | return length() ? Entry(&ents, 0).length() : 0;
78 | }
79 |
80 | // ===================== BY KEY =====================
81 |
82 | // доступ по ключу (главный контейнер - Object)
83 | Entry get(const Text& key) const {
84 | return length() ? Entry(&ents, 0).get(key) : Entry();
85 | }
86 | Entry operator[](const Text& key) const {
87 | return get(key);
88 | }
89 |
90 | // содержит элемент с указанным ключом
91 | bool has(const Text& key) const {
92 | return get(key).valid();
93 | }
94 |
95 | // ===================== BY HASH =====================
96 |
97 | // доступ по хэшу ключа (главный контейнер - Object)
98 | Entry get(size_t hash) const {
99 | return length() ? Entry(&ents, 0).get(hash) : Entry();
100 | }
101 | Entry operator[](size_t hash) const {
102 | return get(hash);
103 | }
104 |
105 | // содержит элемент с указанным хэшем ключа
106 | bool has(size_t hash) const {
107 | return get(hash).valid();
108 | }
109 |
110 | // ===================== BY INDEX =====================
111 |
112 | // доступ по индексу в главный контейнер - Array или Object
113 | Entry get(int index) const {
114 | return length() ? Entry(&ents, 0).get(index) : Entry();
115 | }
116 | Entry operator[](int index) const {
117 | return get(index);
118 | }
119 |
120 | // итерация по вложенным
121 | void loopAll(void (*cb)(Entry e)) {
122 | for (uint16_t i = 0; i < ents.length(); i++) cb(Entry(&ents, i));
123 | }
124 |
125 | // ===================== PARSE =====================
126 |
127 | // парсить в массив длины rootLength()
128 | template
129 | bool parseTo(T& arr) const {
130 | return rootLength() ? Entry(&ents, 0).parseTo(arr) : 0;
131 | }
132 |
133 | // ===================== BY INDEX =====================
134 |
135 | // получить элемент по индексу в общем массиве парсера
136 | Entry getByIndex(parent_t index) const {
137 | return index < length() ? Entry(&ents, index) : Entry();
138 | }
139 | Entry _getByIndex(parent_t index) const {
140 | return Entry(&ents, index);
141 | }
142 |
143 | // ===================== MISC =====================
144 |
145 | // прочитать ключ по индексу
146 | Text key(int idx) const {
147 | return (length() && (uint16_t)idx < length()) ? ents.keyText(idx) : "";
148 | }
149 | // без проверок
150 | Text _key(int idx) const {
151 | return ents.keyText(idx);
152 | }
153 |
154 | // прочитать хэш ключа по индексу
155 | size_t keyHash(int idx) const {
156 | return (length() && (uint16_t)idx < length()) ? (ents.hashed() ? ents.hash[idx] : ents.keyText(idx).hash()) : 0;
157 | }
158 | // без проверок
159 | size_t _keyHash(int idx) const {
160 | return ents.hash[idx];
161 | }
162 |
163 | // прочитать значение по индексу
164 | Text value(int idx) const {
165 | return (length() && (uint16_t)idx < length()) ? ents.valueText(idx) : "";
166 | }
167 | // без проверок
168 | Text _value(int idx) const {
169 | return ents.valueText(idx);
170 | }
171 |
172 | // прочитать родителя по индексу
173 | parent_t parent(int idx) const {
174 | return ((uint16_t)idx < length()) ? ents[idx].parent : 0;
175 | }
176 |
177 | // получить тип по индексу
178 | Type type(int idx) const {
179 | return ((uint16_t)idx < length()) ? ents[idx].type : Type::None;
180 | }
181 |
182 | // прочитать тип по индексу
183 | const __FlashStringHelper* readType(int index) const {
184 | return gson::readType(type(index));
185 | }
186 |
187 | // ============ PARSE ============
188 | // парсить. Вернёт true при успешном парсинге
189 | bool parse(const Text& json) {
190 | return json.pgm() ? 0 : _startParse(json.str(), json.length());
191 | }
192 | bool parse(const char* json, uint16_t len) {
193 | return _startParse(json, len);
194 | }
195 | bool parse(const uint8_t* json, uint16_t len) {
196 | return _startParse((const char*)json, len);
197 | }
198 |
199 | // вывести в Print с форматированием
200 | void stringify(Print& pr) const {
201 | if (length()) Entry(&ents, 0).stringify(pr);
202 | }
203 |
204 | // ============ ERROR ============
205 | // есть ошибка парсинга
206 | bool hasError() const {
207 | return error != Error::None;
208 | }
209 |
210 | // получить ошибку
211 | Error getError() const {
212 | return error;
213 | }
214 |
215 | // индекс места ошибки в строке
216 | uint16_t errorIndex() const {
217 | return (ents && strp) ? (strp - ents.str) : 0;
218 | }
219 |
220 | // прочитать ошибку
221 | const __FlashStringHelper* readError() const {
222 | return gson::readError(error);
223 | }
224 |
225 | // ============ PRIVATE ============
226 | private:
227 | gsutil::EntryStack ents;
228 | char* strp = nullptr;
229 | Error error = Error::None;
230 | State state = State::Idle;
231 | bool strF = 0;
232 | gsutil::Entry_t ebuf;
233 | uint8_t depth = 16;
234 | const char* endp = 0;
235 |
236 | bool _startParse(const char* json, size_t length) {
237 | if (!length) {
238 | error = Error::EmptyString;
239 | return 0;
240 | }
241 | if (length >= (uint32_t)GSON_MAX_LEN) {
242 | error = Error::LongPacket;
243 | return 0;
244 | }
245 |
246 | ents.str = json;
247 | endp = ents.str + length;
248 | strp = (char*)ents.str;
249 | state = State::Idle;
250 | strF = 0;
251 | ebuf = gsutil::Entry_t();
252 | ents.clear();
253 |
254 | if ((strp[0] == '{' && strp[length - 1] == '}') || (strp[0] == '[' && strp[length - 1] == ']')) {
255 | ents.reserve(_count(json, length));
256 | error = _parse(0);
257 | ents[0].parent = GSON_MAX_INDEX;
258 | } else {
259 | error = Error::NotContainer;
260 | }
261 | return !hasError();
262 | }
263 |
264 | Error _parse(parent_t parent) {
265 | while (strp && strp < endp && *strp) {
266 | switch (*strp) {
267 | case ' ':
268 | case '\n':
269 | case '\r':
270 | case '\t':
271 | break;
272 |
273 | case ',':
274 | if (state != State::Idle) return Error::UnexComma;
275 | state = (ents[parent].isArray()) ? State::WaitValue : State::WaitKey;
276 | break;
277 |
278 | case ':':
279 | if (ents[parent].isObject() && state == State::WaitColon) state = State::WaitValue;
280 | else return Error::UnexColon;
281 | break;
282 |
283 | case '\"':
284 | switch ((Type)ents[parent].type) {
285 | case Type::Array:
286 | switch (state) {
287 | case State::Idle:
288 | case State::WaitValue:
289 | strF = 1;
290 | break;
291 |
292 | default:
293 | return Error::UnexQuotes;
294 | }
295 | break;
296 |
297 | case Type::Object:
298 | switch (state) {
299 | case State::WaitKey:
300 | case State::Idle:
301 | ++strp;
302 | if (*strp == '\"' || strp >= endp) return Error::EmptyKey;
303 | ebuf.key_offs = strp - ents.str;
304 | while (1) {
305 | strp = (char*)memchr((void*)(strp + 1), '\"', endp - strp - 1);
306 | if (!strp) return Error::BrokenString;
307 | if (strp[-1] != '\\') break;
308 | }
309 | if (strp - ebuf.key(ents.str) > GSON_MAX_KEY_LEN) return Error::LongKey;
310 | ebuf.key_len = strp - ebuf.key(ents.str);
311 | state = State::WaitColon;
312 | break;
313 |
314 | case State::WaitValue:
315 | strF = 1;
316 | break;
317 |
318 | default:
319 | return Error::UnexQuotes;
320 | }
321 | break;
322 |
323 | default:
324 | return Error::UnexQuotes;
325 | }
326 | break;
327 |
328 | case '{':
329 | case '[': {
330 | if (strp != ents.str) { // not first symb
331 | if (!(ents[parent].isArray() && (state == State::Idle || state == State::WaitValue)) &&
332 | !(ents[parent].isObject() && state == State::WaitValue)) {
333 | return Error::UnexOpen;
334 | }
335 | }
336 | if (length() == GSON_MAX_INDEX - 1) return Error::IndexOverflow;
337 |
338 | ebuf.type = (*strp == '{') ? Type::Object : Type::Array;
339 | ebuf.parent = parent;
340 | if (!ents.push(ebuf)) return Error::Alloc;
341 | ebuf.reset();
342 | state = State::Idle;
343 | ++strp;
344 | if (strp >= endp) return Error::BrokenContainer;
345 | if (depth - 1 == 0) return Error::TooDeep;
346 |
347 | --depth;
348 | error = _parse(length() - 1); // RECURSIVE
349 | ++depth;
350 | if (hasError()) return error;
351 | } break;
352 |
353 | case '}':
354 | case ']': {
355 | if (state != State::Idle || ents[parent].type != ((*strp == '}') ? Type::Object : Type::Array)) {
356 | return Error::UnexClose;
357 | }
358 | return Error::None;
359 | } break;
360 |
361 | default: {
362 | if (!(ents[parent].isObject() && state == State::WaitValue) &&
363 | !(ents[parent].isArray() && (state == State::WaitValue || state == State::Idle))) {
364 | return Error::UnexToken;
365 | }
366 |
367 | ebuf.val_offs = strp - ents.str;
368 | switch (*strp) {
369 | case 't':
370 | case 'f':
371 | ebuf.type = Type::Bool;
372 | break;
373 | case '-':
374 | case '0' ... '9':
375 | ebuf.type = Type::Int;
376 | break;
377 | case 'n':
378 | ebuf.type = Type::Null;
379 | break;
380 | default:
381 | return Error::UnknownToken;
382 | }
383 | while (true) {
384 | if (*strp == '.') {
385 | if (ebuf.is(Type::Int)) ebuf.type = Type::Float;
386 | else return Error::UnknownToken;
387 | }
388 | if (strp + 1 >= endp || !strp[1]) return Error::BrokenToken;
389 |
390 | bool endF = 0;
391 | switch (strp[1]) { // next sym
392 | case ' ':
393 | case '\t':
394 | case '\r':
395 | case '\n':
396 | case ',':
397 | case '}':
398 | case ']':
399 | endF = 1;
400 | }
401 | if (!ebuf.val_len && (endF || !*strp)) {
402 | ebuf.val_len = strp + 1 - ebuf.value(ents.str);
403 | }
404 | if (endF) break;
405 | ++strp;
406 | }
407 | if (ebuf.is(Type::Bool)) {
408 | if (!(ebuf.val_len == 4 && !strncmp_P(ebuf.value(ents.str), PSTR("true"), 4)) &&
409 | !(ebuf.val_len == 5 && !strncmp_P(ebuf.value(ents.str), PSTR("false"), 5))) {
410 | return Error::BrokenToken;
411 | }
412 | }
413 | if (ebuf.is(Type::Null)) {
414 | if (!(ebuf.val_len == 4 && !strncmp_P(ebuf.value(ents.str), PSTR("null"), 4))) {
415 | return Error::BrokenToken;
416 | }
417 | }
418 | if (length() == GSON_MAX_INDEX - 1) return Error::IndexOverflow;
419 | ebuf.parent = parent;
420 | if (!ents.push(ebuf)) return Error::Alloc;
421 | ebuf.reset();
422 | state = State::Idle;
423 | } break;
424 |
425 | } // switch
426 |
427 | if (strF) {
428 | strF = 0;
429 | ++strp;
430 | if (strp >= endp) return Error::BrokenString;
431 | ebuf.val_offs = strp - ents.str;
432 | if (*strp != '\"') {
433 | while (1) {
434 | strp = (char*)memchr((void*)(strp + 1), '\"', endp - strp - 1);
435 | if (!strp) return Error::BrokenString;
436 | if (strp[-1] != '\\') break;
437 | }
438 | }
439 | if (length() == GSON_MAX_INDEX - 1) return Error::IndexOverflow;
440 | ebuf.val_len = strp - ebuf.value(ents.str);
441 | ebuf.parent = parent;
442 | ebuf.type = Type::String;
443 | if (!ents.push(ebuf)) return Error::Alloc;
444 | ebuf.reset();
445 | state = State::Idle;
446 | }
447 |
448 | if (strp) ++strp;
449 | } // while
450 |
451 | return (parent == 0) ? Error::None : Error::BrokenContainer;
452 | }
453 |
454 | // посчитать приблизительное количество элементов
455 | uint16_t _count(const char* str, uint16_t len) {
456 | if (!len) return 0;
457 | uint16_t count = 0;
458 | bool inStr = false;
459 | while (--len) { // len-1.. 1
460 | switch (str[len]) {
461 | case '\"':
462 | if (str[len - 1] != '\\') inStr = !inStr;
463 | break;
464 |
465 | case '{':
466 | case '[':
467 | if (!inStr) count += 2;
468 | break;
469 |
470 | case ',':
471 | if (!inStr) ++count;
472 | break;
473 | }
474 | }
475 | return count;
476 | }
477 | };
478 |
479 | // ================== DEPRECATED ==================
480 | template
481 | class ParserStatic : public Parser {};
482 |
483 | class Doc : public Parser {
484 | public:
485 | using Parser::Parser;
486 | };
487 |
488 | template
489 | class DocStatic : public Parser {};
490 |
491 | } // namespace gson
--------------------------------------------------------------------------------
/src/utils/parser_stream.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 |
4 | #include "parser.h"
5 |
6 | namespace gson {
7 |
8 | // ================== STREAM ==================
9 | class ParserStream : public Parser {
10 | public:
11 | ParserStream(size_t size = 0) : Parser(size) {}
12 |
13 | // прочитать из потока и сохранить себе
14 | bool parse(Stream* stream, size_t length) {
15 | if (!stream || !length || !json.resize(length)) return 0;
16 |
17 | if (stream->readBytes(json, length) != length) {
18 | json.reset();
19 | return 0;
20 | }
21 | return Parser::parse(json.buf(), length);
22 | }
23 |
24 | // прочитать из строки и сохранить себе
25 | bool parse(const char* str, size_t length) {
26 | if (!str || !length || !json.resize(length)) return 0;
27 |
28 | memcpy((void*)json.buf(), str, length);
29 | return Parser::parse(json.buf(), length);
30 | }
31 |
32 | // освободить память
33 | void reset() {
34 | json.reset();
35 | Parser::reset();
36 | }
37 |
38 | // получить скачанный json пакет как Text
39 | Text getRaw() {
40 | return json ? Text(json.buf(), json.size()) : Text();
41 | }
42 |
43 | private:
44 | using Parser::parse;
45 | gtl::array json;
46 | };
47 |
48 | } // namespace gson
--------------------------------------------------------------------------------
/src/utils/str.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | namespace gtl {
8 | class string : protected gtl::stack {
9 | typedef gtl::stack ST;
10 |
11 | public:
12 | // ================= STR =================
13 |
14 | void concat(char sym) {
15 | ST::push(sym);
16 | }
17 |
18 | void concat(char* str) {
19 | ST::concat(str, strlen(str));
20 | }
21 | void concat(char* str, uint16_t len) {
22 | ST::concat(str, len);
23 | }
24 |
25 | void concat(const char* str) {
26 | ST::concat(str, strlen(str));
27 | }
28 | void concat(const char* str, uint16_t len) {
29 | ST::concat(str, len);
30 | }
31 |
32 | void concat(const __FlashStringHelper* fstr) {
33 | ST::concat((PGM_P)fstr, strlen_P((PGM_P)fstr), true);
34 | }
35 | void concat(const __FlashStringHelper* fstr, uint16_t len) {
36 | ST::concat((PGM_P)fstr, len, true);
37 | }
38 |
39 | void concat(const String& s) {
40 | ST::concat(s.c_str(), s.length());
41 | }
42 | void concat(const Text& txt) {
43 | ST::concat(txt.str(), txt.length(), txt.pgm());
44 | }
45 |
46 | // ================= NUM =================
47 |
48 | void concat(bool b) {
49 | b ? concat(F("true"), 4) : concat(F("false"), 5);
50 | }
51 |
52 | void concat(signed char val) {
53 | concat((long)val);
54 | }
55 | void concat(short val) {
56 | concat((long)val);
57 | }
58 | void concat(int val) {
59 | concat((long)val);
60 | }
61 | void concat(long val) {
62 | if (addCapacity(12)) _len += su::intToStr(val, buf() + _len);
63 | }
64 | void concat(long long val) {
65 | if (addCapacity(21)) _len += su::int64ToStr(val, buf() + _len);
66 | }
67 |
68 | void concat(unsigned char val) {
69 | concat((unsigned long)val);
70 | }
71 | void concat(unsigned short val) {
72 | concat((unsigned long)val);
73 | }
74 | void concat(unsigned int val) {
75 | concat((unsigned long)val);
76 | }
77 | void concat(unsigned long val) {
78 | if (addCapacity(12)) _len += su::uintToStr(val, buf() + _len);
79 | }
80 | void concat(unsigned long long val) {
81 | if (addCapacity(21)) _len += su::uint64ToStr(val, buf() + _len);
82 | }
83 |
84 | void concat(float val, uint8_t dec = 2) {
85 | if (isnan(val) || isinf(val)) {
86 | concat(F("null"), 4);
87 | return;
88 | }
89 | uint8_t len = su::floatLen(val, dec);
90 | if (addCapacity(len)) {
91 | dtostrf(val, dec ? dec + 2 : 1, dec, buf() + _len);
92 | _len += len;
93 | }
94 | }
95 | void concat(double val, uint8_t dec = 2) {
96 | concat(float(val), dec);
97 | }
98 |
99 | using ST::buf;
100 | using ST::clear;
101 | using ST::concat;
102 | using ST::length;
103 | using ST::reserve;
104 | };
105 | } // namespace gtl
106 |
107 | namespace gson {
108 |
109 | class Str : public Printable, public gtl::string {
110 | public:
111 | Str() {}
112 | Str(uint16_t res) {
113 | reserve(res);
114 | }
115 |
116 | explicit operator bool() const {
117 | return true;
118 | }
119 |
120 | operator Text() const {
121 | return Text(buf(), length());
122 | }
123 |
124 | size_t printTo(Print& p) const {
125 | return p.write(buf(), length());
126 | }
127 |
128 | void operator=(const Str& str) {
129 | if (!str.length()) return;
130 | _checkNc();
131 | concat(str);
132 | _resetNc();
133 | }
134 | void operator+=(const Str& str) {
135 | if (!str.length()) return;
136 | _checkNc();
137 | concat(str);
138 | _resetNc();
139 | }
140 |
141 | // =================== VAL ===================
142 | #define GS_STR_MAKE_VAL(T) \
143 | void operator=(T val) { _val(val); } \
144 | void operator+=(T val) { _val(val); }
145 |
146 | GS_STR_MAKE_VAL(bool)
147 | GS_STR_MAKE_VAL(signed char)
148 | GS_STR_MAKE_VAL(unsigned char)
149 | GS_STR_MAKE_VAL(short)
150 | GS_STR_MAKE_VAL(unsigned short)
151 | GS_STR_MAKE_VAL(int)
152 | GS_STR_MAKE_VAL(unsigned int)
153 | GS_STR_MAKE_VAL(long)
154 | GS_STR_MAKE_VAL(unsigned long)
155 | GS_STR_MAKE_VAL(long long)
156 | GS_STR_MAKE_VAL(unsigned long long)
157 | GS_STR_MAKE_VAL(float)
158 | GS_STR_MAKE_VAL(double)
159 |
160 | // добавить float с кол-вом знаков
161 | void add(float val, uint8_t dec = 2) {
162 | _checkNc();
163 | concat(val, dec);
164 | _resetNc();
165 | }
166 |
167 | // =================== NULL ===================
168 | void operator=(nullptr_t) { _null(); }
169 | void operator+=(nullptr_t) { _null(); }
170 |
171 | // =================== STR ===================
172 | void operator=(char s) { _str(s); }
173 | void operator+=(char s) { _str(s); }
174 | void operator=(char* s) { _str(s); }
175 | void operator+=(char* s) { _str(s); }
176 | void operator=(const char* s) { _str(s); }
177 | void operator+=(const char* s) { _str(s); }
178 | void operator=(const __FlashStringHelper* s) { _str(s); }
179 | void operator+=(const __FlashStringHelper* s) { _str(s); }
180 | void operator=(const String& s) { _str(s); }
181 | void operator+=(const String& s) { _str(s); }
182 | void operator=(const Text& s) { _str(s); }
183 | void operator+=(const Text& s) { _str(s); }
184 |
185 | // прибавить строку с escape
186 | void escape(const Text& txt) {
187 | _checkNc();
188 | push('\"');
189 | uint16_t len = txt.length();
190 | reserve(len);
191 | char p = 0;
192 | for (uint16_t i = 0; i < len; i++) {
193 | char c = txt._charAt(i);
194 | switch (c) {
195 | case '\"':
196 | case '\\':
197 | if (p != '\\') push('\\');
198 | push(c);
199 | break;
200 | case '\n':
201 | push('\\');
202 | push('n');
203 | break;
204 | case '\r':
205 | push('\\');
206 | push('r');
207 | break;
208 | case '\t':
209 | push('\\');
210 | push('t');
211 | break;
212 | default:
213 | push(c);
214 | break;
215 | }
216 | p = c;
217 | }
218 | push('\"');
219 | _resetNc();
220 | }
221 |
222 | // =================== KEY ===================
223 | Str& operator[](const char* s) { return _key(s); }
224 | Str& operator[](const __FlashStringHelper* s) { return _key(s); }
225 | Str& operator[](const String& s) { return _key(s); }
226 | Str& operator[](const Text& s) { return _key(s); }
227 |
228 | // =================== CONT ===================
229 | Str& operator()(char cont) {
230 | switch (cont) {
231 | case '{':
232 | case '[':
233 | _checkNc();
234 | break;
235 | default:
236 | _resetNc();
237 | break;
238 | }
239 | push(cont);
240 | return *this;
241 | }
242 |
243 | using gtl::string::concat;
244 |
245 | private:
246 | bool _nc = false;
247 |
248 | Str& _key(const Text& t) {
249 | _checkNc();
250 | push('\"');
251 | concat(t);
252 | push('\"');
253 | push(':');
254 | return *this;
255 | }
256 | template
257 | void _val(T val) {
258 | _checkNc();
259 | concat(val);
260 | _resetNc();
261 | }
262 | void _str(const Text& t) {
263 | _checkNc();
264 | push('\"');
265 | concat(t);
266 | push('\"');
267 | _resetNc();
268 | }
269 | void _str(char ch) {
270 | _checkNc();
271 | push('\"');
272 | push(ch);
273 | push('\"');
274 | _resetNc();
275 | }
276 | void _null() {
277 | _checkNc();
278 | concat(F("null"), 4);
279 | _resetNc();
280 | }
281 | inline void _checkNc() {
282 | if (_nc) _nc = false, push(',');
283 | }
284 | inline void _resetNc() {
285 | _nc = true;
286 | }
287 | };
288 |
289 | } // namespace gson
--------------------------------------------------------------------------------
/src/utils/string.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 | #include
5 |
6 | namespace gson {
7 |
8 | class string : public Printable {
9 | public:
10 | string(uint16_t res = 0) {
11 | if (res) reserve(res);
12 | }
13 |
14 | // доступ к строке
15 | String s;
16 |
17 | // доступ к строке
18 | operator String&() {
19 | return s;
20 | }
21 |
22 | // доступ к строке
23 | operator Text() {
24 | return s;
25 | }
26 |
27 | // Напечатать в Print
28 | size_t printTo(Print& p) const {
29 | return p.print(s);
30 | }
31 |
32 | // очистить строку
33 | void clear() {
34 | s = "";
35 | }
36 |
37 | // длина строки
38 | uint16_t length() const {
39 | return s.length();
40 | }
41 |
42 | // зарезервировать строку
43 | bool reserve(uint16_t res) {
44 | return s.reserve(res);
45 | }
46 |
47 | // завершить пакет
48 | string& end() {
49 | replaceComma('\0');
50 | return *this;
51 | }
52 |
53 | // =============== ADD ===============
54 |
55 | // прибавить gson::string. Будет добавлена запятая
56 | string& add(const string& str) {
57 | s += str.s;
58 | replaceComma(',');
59 | return *this;
60 | }
61 |
62 | // прибавить gson::string. Будет добавлена запятая
63 | void operator+=(const string& str) {
64 | add(str);
65 | }
66 |
67 | // =============== KEY ===============
68 |
69 | // добавить ключ (строка любого типа)
70 | string& addKey(const Text& key) {
71 | if (key.valid()) {
72 | _addRaw(key, true, false);
73 | colon();
74 | }
75 | return *this;
76 | }
77 |
78 | // добавить ключ (строка любого типа)
79 | string& operator[](const Text& key) {
80 | return addKey(key);
81 | }
82 |
83 | // =============== TEXT ===============
84 |
85 | // прибавить текст (строка любого типа) без запятой и кавычек
86 | string& addTextRaw(const Text& txt) {
87 | if (txt.valid()) _addRaw(txt, false, false);
88 | return *this;
89 | }
90 |
91 | // прибавить текст (строка любого типа) без запятой и кавычек с escape символов
92 | string& addTextRawEsc(const Text& txt) {
93 | if (txt.valid()) _addRaw(txt, false, true);
94 | return *this;
95 | }
96 |
97 | // прибавить текст (строка любого типа) без кавычек
98 | string& addText(const Text& txt) {
99 | if (txt.valid()) {
100 | addTextRaw(txt);
101 | comma();
102 | }
103 | return *this;
104 | }
105 |
106 | // прибавить текст (строка любого типа) без кавычек с escape символов
107 | string& addTextEsc(const Text& txt) {
108 | if (txt.valid()) {
109 | addTextRawEsc(txt);
110 | comma();
111 | }
112 | return *this;
113 | }
114 |
115 | // добавить текст (строка любого типа) без кавычек
116 | string& addText(const Text& key, const Text& txt) {
117 | if (key.valid() && txt.valid()) {
118 | addKey(key);
119 | addText(txt);
120 | }
121 | return *this;
122 | }
123 |
124 | // добавить текст (строка любого типа) без кавычек с escape символов
125 | string& addTextEsc(const Text& key, const Text& txt) {
126 | if (key.valid() && txt.valid()) {
127 | addKey(key);
128 | addTextEsc(txt);
129 | }
130 | return *this;
131 | }
132 |
133 | // =============== STRING BIN ===============
134 |
135 | // добавить строку (строка любого типа) с escape символов без запятой
136 | string& addStringRawEsc(const Text& value) {
137 | if (value.valid()) _addRaw(value, true, true);
138 | return *this;
139 | }
140 |
141 | // добавить строку (строка любого типа) без запятой
142 | string& addStringRaw(const Text& value) {
143 | if (value.valid()) _addRaw(value, true, false);
144 | return *this;
145 | }
146 |
147 | // =============== STRING ===============
148 |
149 | // добавить строку (строка любого типа) с escape символов
150 | string& addStringEsc(const Text& key, const Text& value) {
151 | if (key.valid() && value.valid()) {
152 | addKey(key);
153 | addStringEsc(value);
154 | }
155 | return *this;
156 | }
157 |
158 | // добавить строку (строка любого типа) с escape символов
159 | string& addStringEsc(const Text& value) {
160 | if (value.valid()) {
161 | _addRaw(value, true, true);
162 | comma();
163 | }
164 | return *this;
165 | }
166 |
167 | // добавить строку (строка любого типа)
168 | string& addString(const Text& key, const Text& value) {
169 | if (key.valid() && value.valid()) {
170 | addKey(key);
171 | addString(value);
172 | }
173 | return *this;
174 | }
175 |
176 | // добавить строку (строка любого типа)
177 | string& addString(const Text& value) {
178 | if (value.valid()) {
179 | _addRaw(value, true, false);
180 | comma();
181 | }
182 | return *this;
183 | }
184 |
185 | // добавить строку (строка любого типа)
186 | void operator=(const char* value) {
187 | _addString(value);
188 | }
189 | void operator+=(const char* value) {
190 | _addString(value);
191 | }
192 |
193 | void operator=(const __FlashStringHelper* value) {
194 | _addString(value);
195 | }
196 | void operator+=(const __FlashStringHelper* value) {
197 | _addString(value);
198 | }
199 |
200 | void operator=(const String& value) {
201 | _addString(value);
202 | }
203 | void operator+=(const String& value) {
204 | _addString(value);
205 | }
206 |
207 | void operator=(const Text& value) {
208 | _addString(value);
209 | }
210 | void operator+=(const Text& value) {
211 | _addString(value);
212 | }
213 |
214 | // =============== BOOL ===============
215 |
216 | // добавить bool
217 | string& addBool(const Text& key, const bool& value) {
218 | if (key.valid()) {
219 | addKey(key);
220 | addBool(value);
221 | }
222 | return *this;
223 | }
224 |
225 | // добавить bool
226 | string& addBool(const bool& value) {
227 | addBoolRaw(value);
228 | comma();
229 | return *this;
230 | }
231 |
232 | // добавить bool без запятой
233 | string& addBoolRaw(const bool& value) {
234 | s += value ? F("true") : F("false");
235 | return *this;
236 | }
237 |
238 | // добавить bool
239 | void operator=(const bool& value) {
240 | addBool(value);
241 | }
242 | void operator+=(const bool& value) {
243 | addBool(value);
244 | }
245 |
246 | // =============== FLOAT ===============
247 |
248 | // добавить float
249 | string& addFloat(const Text& key, const double& value, uint8_t dec = 2) {
250 | if (key.valid()) {
251 | addKey(key);
252 | addFloat(value, dec);
253 | }
254 | return *this;
255 | }
256 |
257 | // добавить float
258 | string& addFloat(const double& value, uint8_t dec = 2) {
259 | addFloatRaw(value, dec);
260 | comma();
261 | return *this;
262 | }
263 |
264 | // добавить float без запятой
265 | string& addFloatRaw(const double& value, uint8_t dec = 2) {
266 | if (isnan(value)) s += '0';
267 | else {
268 | char buf[33];
269 | dtostrf(value, dec + 2, dec, buf);
270 | s += buf;
271 | }
272 | return *this;
273 | }
274 |
275 | // добавить float
276 | void operator=(const double& value) {
277 | addFloat(value);
278 | }
279 | void operator+=(const double& value) {
280 | addFloat(value);
281 | }
282 |
283 | // =============== INT ===============
284 |
285 | #ifndef SUTIL_NO_VALUE
286 | // добавить int
287 | string& addInt(const Text& key, const Value& value) {
288 | if (key.valid() && value.valid()) {
289 | addKey(key);
290 | addInt(value);
291 | }
292 | return *this;
293 | }
294 |
295 | // добавить int
296 | string& addInt(const Value& value) {
297 | if (value.valid()) {
298 | value.addString(s);
299 | comma();
300 | }
301 | return *this;
302 | }
303 |
304 | // добавить int без запятой
305 | string& addIntRaw(const Value& value) {
306 | if (value.valid()) value.addString(s);
307 | return *this;
308 | }
309 | #else
310 | // добавить int
311 | template
312 | string& addInt(const Text& key, T value) {
313 | if (key.valid()) {
314 | addKey(key);
315 | addInt(value);
316 | }
317 | return *this;
318 | }
319 |
320 | // добавить int
321 | template
322 | string& addInt(T value) {
323 | s += value;
324 | comma();
325 | return *this;
326 | }
327 |
328 | // добавить int без запятой
329 | template
330 | string& addIntRaw(T value) {
331 | s += value;
332 | return *this;
333 | }
334 | #endif
335 |
336 | #define GSON_MAKE_ADD_INT(type) \
337 | void operator=(type value) { addInt(value); } \
338 | void operator+=(type value) { addInt(value); }
339 |
340 | GSON_MAKE_ADD_INT(Value)
341 | GSON_MAKE_ADD_INT(char)
342 | GSON_MAKE_ADD_INT(unsigned char)
343 | GSON_MAKE_ADD_INT(short)
344 | GSON_MAKE_ADD_INT(unsigned short)
345 | GSON_MAKE_ADD_INT(int)
346 | GSON_MAKE_ADD_INT(unsigned int)
347 | GSON_MAKE_ADD_INT(long)
348 | GSON_MAKE_ADD_INT(unsigned long)
349 |
350 | #ifndef SUTIL_NO_VALUE
351 | GSON_MAKE_ADD_INT(long long)
352 | GSON_MAKE_ADD_INT(unsigned long long)
353 | #endif
354 | // =============== CONTAINER ===============
355 |
356 | // начать объект
357 | string& beginObj(const Text& key = Text()) {
358 | addKey(key);
359 | s += '{';
360 | return *this;
361 | }
362 |
363 | // завершить объект. last - не добавлять запятую
364 | string& endObj(bool last = false) {
365 | replaceComma('}');
366 | if (!last) comma();
367 | return *this;
368 | }
369 |
370 | // начать массив
371 | string& beginArr(const Text& key = Text()) {
372 | addKey(key);
373 | s += '[';
374 | return *this;
375 | }
376 |
377 | // завершить массив. last - не добавлять запятую
378 | string& endArr(bool last = false) {
379 | replaceComma(']');
380 | if (!last) comma();
381 | return *this;
382 | }
383 |
384 | // запятая
385 | void comma() {
386 | afterValue();
387 | s += ',';
388 | }
389 |
390 | // двойные кавычки
391 | void quotes() {
392 | s += '\"';
393 | }
394 |
395 | // двоеточие
396 | void colon() {
397 | s += ':';
398 | }
399 |
400 | // делать escape символов при прибавлении через оператор = (умолч. вкл, true)
401 | void escapeDefault(bool esc) {
402 | _esc = esc;
403 | }
404 |
405 | // =============== PRIVATE ===============
406 | protected:
407 | // вызывается перед запятой (после добавления значения)
408 | virtual void afterValue() {}
409 |
410 | // escape символов
411 | virtual void escape(const Text& text) {
412 | uint16_t len = text.length();
413 | char p = 0;
414 | for (uint16_t i = 0; i < len; i++) {
415 | char c = text.charAt(i);
416 | switch (c) {
417 | case '\"':
418 | case '\\':
419 | if (p != '\\') s += '\\';
420 | s += c;
421 | break;
422 | case '\n':
423 | s += '\\';
424 | s += 'n';
425 | break;
426 | case '\r':
427 | s += '\\';
428 | s += 'r';
429 | break;
430 | case '\t':
431 | s += '\\';
432 | s += 't';
433 | break;
434 | default:
435 | s += c;
436 | break;
437 | }
438 | p = c;
439 | }
440 | }
441 |
442 | // заменить последнюю запятую символом. Если символ '\0' - удалить запятую. Если это не запятая - добавить символ
443 | void replaceComma(char sym) {
444 | int16_t len = s.length() - 1;
445 | if (s[len] == ',') {
446 | if (!sym) s.remove(len);
447 | else s[len] = sym;
448 | } else {
449 | if (sym) s += sym;
450 | }
451 | }
452 |
453 | private:
454 | bool _esc = true;
455 |
456 | void _addRaw(const Text& text, bool quot, bool esc) {
457 | if (quot) quotes();
458 | if (esc) {
459 | if (!s.reserve(s.length() + text.length())) return;
460 | escape(text);
461 | } else {
462 | text.addString(s);
463 | }
464 | if (quot) quotes();
465 | }
466 | void _addString(const Text& text) {
467 | _addRaw(text, true, _esc);
468 | comma();
469 | }
470 | };
471 |
472 | } // namespace gson
--------------------------------------------------------------------------------
/src/utils/types.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 |
4 | namespace gson {
5 |
6 | enum class Type : uint8_t {
7 | None,
8 | Object,
9 | Array,
10 | String,
11 | Int,
12 | Float,
13 | Bool,
14 | Null,
15 | };
16 |
17 | static const __FlashStringHelper* readType(Type t) {
18 | switch (t) {
19 | case Type::Object: return F("Object");
20 | case Type::Array: return F("Array");
21 | case Type::String: return F("String");
22 | case Type::Int: return F("Int");
23 | case Type::Float: return F("Float");
24 | case Type::Bool: return F("Bool");
25 | case Type::Null: return F("Null");
26 | default: return F("None");
27 | }
28 | }
29 |
30 | enum class Error : uint8_t {
31 | None,
32 | Alloc,
33 | TooDeep,
34 | NoParent,
35 | NotContainer,
36 | UnexComma,
37 | UnexColon,
38 | UnexToken,
39 | UnexQuotes,
40 | UnexOpen,
41 | UnexClose,
42 | UnknownToken,
43 | BrokenToken,
44 | BrokenString,
45 | BrokenContainer,
46 | EmptyKey,
47 | IndexOverflow,
48 | LongPacket,
49 | LongKey,
50 | EmptyString,
51 | };
52 |
53 | static const __FlashStringHelper* readError(Error e) {
54 | switch (e) {
55 | case Error::Alloc: return F("Alloc");
56 | case Error::TooDeep: return F("TooDeep");
57 | case Error::NoParent: return F("NoParent");
58 | case Error::NotContainer: return F("NotContainer");
59 | case Error::UnexComma: return F("UnexComma");
60 | case Error::UnexColon: return F("UnexColon");
61 | case Error::UnexToken: return F("UnexToken");
62 | case Error::UnexQuotes: return F("UnexQuotes");
63 | case Error::UnexOpen: return F("UnexOpen");
64 | case Error::UnexClose: return F("UnexClose");
65 | case Error::UnknownToken: return F("UnknownToken");
66 | case Error::BrokenToken: return F("BrokenToken");
67 | case Error::BrokenString: return F("BrokenString");
68 | case Error::BrokenContainer: return F("BrokenContainer");
69 | case Error::EmptyKey: return F("EmptyKey");
70 | case Error::IndexOverflow: return F("IndexOverflow");
71 | case Error::LongPacket: return F("LongPacket");
72 | case Error::LongKey: return F("LongKey");
73 | case Error::EmptyString: return F("EmptyString");
74 | default: return F("None");
75 | }
76 | }
77 |
78 | } // namespace gson
--------------------------------------------------------------------------------