├── .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 | [![latest](https://img.shields.io/github/v/release/GyverLibs/GSON.svg?color=brightgreen)](https://github.com/GyverLibs/GSON/releases/latest/download/GSON.zip) 2 | [![PIO](https://badges.registry.platformio.org/packages/gyverlibs/library/GSON.svg)](https://registry.platformio.org/libraries/gyverlibs/GSON) 3 | [![Foo](https://img.shields.io/badge/Website-AlexGyver.ru-blue.svg?style=flat-square)](https://alexgyver.ru/) 4 | [![Foo](https://img.shields.io/badge/%E2%82%BD%24%E2%82%AC%20%D0%9F%D0%BE%D0%B4%D0%B4%D0%B5%D1%80%D0%B6%D0%B0%D1%82%D1%8C-%D0%B0%D0%B2%D1%82%D0%BE%D1%80%D0%B0-orange.svg?style=flat-square)](https://alexgyver.ru/support_alex/) 5 | [![Foo](https://img.shields.io/badge/README-ENGLISH-blueviolet.svg?style=flat-square)](https://github-com.translate.goog/GyverLibs/GSON?_x_tr_sl=ru&_x_tr_tl=en) 6 | 7 | [![Foo](https://img.shields.io/badge/ПОДПИСАТЬСЯ-НА%20ОБНОВЛЕНИЯ-brightgreen.svg?style=social&logo=telegram&color=blue)](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 --------------------------------------------------------------------------------