Search
Write a publication
Pull to refresh

Comments 65

Можно подробнее про вот это

для использования в условиях ограниченных каналов связи, таких как SMS, DTMF, и низкоскоростные и устаревшие каналы связи (например, 2G, голос, радиоканал)

Наверно формат со спецсимволами это в целом не про передачу голосом. Интересно, что вы имеете в виду под передачей по DTMF. Там вроде как 12 разных значений определено, как с его помощью передавать что-то текстоподобное?

Отличный вопрос. Я кодирую Base12

2. Области применения

Эммм... не подскажете, в какой из перечисленных под этим заголовком областей рассматриваемый формат хотя бы теоретически выигрывает у бинарного? Потому как читабельность в указанных областях никому нафиг не нужна (принимающей стороной ни в одной из указанных областей не будет человек), а по части компактности бинар выигрывает по определению..

Значения могут быть: ... упакованными base64

CJON устраняет кавычки, скобки и пробелы, тем самым экономя от 30% до 50% символов;

С учётом первой цитаты, вторая воспринимается как осознанное лукавство.

При блокировке моб инета отправка данных (текстовых) возможно только через sms.

Видимо, я отстал от жизни.

MMS уже совсем отменили? Вы уверены?

SMS работает в 2G сетях, а MMS нет.

Ошибка. MMS может работать через GPRS. И вообще ммс завязан на WAP

Автор пишет что, кодирует сообщение в Base12 для DTMF, почему тогда не использовать бинарный формат protobuf или messagepack и кодировать сообщение в base64 или base85 для передачи через SMS? если данные это числа с плавающей точкой, например координаты или другие показания датчиков, то выигрыш должен быть довольно большим

Ниже уже отвечал. думал об этом, но для поставленной задачи придется 1) хранить схемы данных на сервере; 2) увеличивает объём на треть при упаковке в base64; 3) делает сообщение полностью нечитаемым без инструментов; 4) при потере или искажении символа затрудняет восстановление части данных. CJON читается даже если половина данных повредилась.

Большая часть данных в CJON не упаковывается base64, т.к. соответствует кодировке GSM Bit-7 и передается компактно. А для большей оптимизации я уже реализовал на сервере справочник ключей, который для каждой предметной области, определяемой типом сообщения, хранится на сервере и перед отправкой заменяется в JSON, например temperature - t, latitude - l... так что упаковка будет лучше, чем в proto, а главное проще для реализации.

По сути это чуть более компактный YAML Flow. Без лишних кавычек и скобочек.

реализовал на сервере справочник ключей, который для каждой предметной области, определяемой типом сообщения, хранится на сервере и перед отправкой заменяется в JSON, например temperature - t

Хардкод вместо расширяемых словарей? Весело...

чуть более компактный YAML

В YAML из хорошего только модель данных (например, тип как строка, в CBOR вместо этого числовые теги, что неудобно), остальное лучше делать иначе...

А кто этот формат из полученной SMS декодировать будет? Всё-таки машина? Ну а смысл тогда...

напоминает формат применяемый в GDS-системах, типа EDIFACT

А ещё вопрос про сравнение - как насчет ANS.1 ?

Это чудесный распространённый машиночитаемый формат, но не для данной задачи. Еще раз уточню - задача передавать упакованные небольшие информационные пакеты (управляющие сигналы, статус устройств, команды) короткими сообщениями или посредством голосовых каналов (в том числе радио) с использованием модуляции DTMF на сервер. Когда отсутствует интернет канал

Только ASN.1, и этот ужас давно пора закопать. Там вариации парсеров уже на OID начинаются, не говоря уже обо всём остальном, что ведёт к бесконечной череде дыр в реализациях до сих пор...

В точку. CJON - это Франкенштейн различных форматов для решения конкретной задачи. :)

Говорят gzip давно изобрели. И даже zstd сделали.

Смысл сравнивать непожатый размер? Все жмется перед передачей.

По sms вы бинарный формат не передадите. У меня задача передавать данные по GSM и работать в пределах кодировки GSM 7-bit.

Кажется что передача данных по смс это настолько редкий и узкий кейс что стоит сделать свой формат для каждого конкретного случая.

Там все это не нужно. Передаем координаты: ну так и передайте lat:xxx lon:yyy и тому подобное

Возможно пропустил, но повторное просматривание текста не помогло. Как в CJON кодируются массивы объектов? В примере видел только вариант "массив однотипных элементов примитивов". Скажем, из примера выше про такси куда/откуда/тариф считается, что тариф один. Но например, цена за километр может отличаться от того, что точка два идёт, к примеру, через платный участок дороги и там к тарифу будет ещё доплата. Как это всё кодировать в CJON? В том же JSON просто можно список объектов прикрутить с опциональными полями: "{ways: [ { from: 1, to: 2}, {from: 1, to: 3, extra_pay: 200}], tarif: 2 }". Или такое предлагается всегда вбивать массив extra, даже если оно надо только одному элементу, а отправитель и получатель должны корректно интерпретировать полученный набор? Правда, в этом случае уже не факт, что CJON всегда будет короче JSON

Edit: про вложенные массивы тоже интересно, как их описывать. Если таковые вообще используются. Хотя, вложенные массивы можно реализовать через одномерные при условии, что клиент и сервер знают об этом. Это опять же сводится тогда к указанию типа сообщения и некторому усложнению кода сервера и клиента.

Все верно, только нотация JSON всегда требует наличия кавычек как минимум. Вложенность реализуется 2 способами: для небольшого количества объектов посредством сериализации ключей (taxi.tarif...) или как в JSON. Я стараюсь экономить каждый символ, поэтому это бывает важно.

Существует полубинарный формат bencode. Решает ту же задачу тем же способом.

Нет, он делался для задачи получения одинаковых хэшей от семантически одинакового содержимого. Минимизация там не ставилась (что и видно по десятичным числам).

Проще парсить: split('=') быстрее и надёжнее, чем обработка : без кавычек.

msg=~0KHRgtGD0LrQsNC70LjQvSDQkNC70LXQutGB0LXQuQ==

Всё же странно, что вы выбрали не двоеточие, а знак равенства, который используется в base64.

Долго думал над этим, но чтобы не запутать пользователей, что якобы это JSON, решил все-таки выбрать =. "==" в конце парсингу не мешает никак.

человекочитаемый формат....
человекочитаемый формат....

Не поленился посмотреть как будет выглядеть размер данных в zstd сжатом виде на дефолтных настройках:

jsonLen: 2710
cjonLen: 2519
Compressed json bytes: 1078
Compressed cjon bytes: 1133
Compressed json as base64 len: 1440

Т.е. в сыром виде cjon конечно немного меньше, но сжатию обычный json поддается лучше, а если есть ограничение по передаче только текста, то пожатый json в base64 имеет намного меньший размер (меньше почти в два раза) чем сырой cjon.

Все верно, почти. Задача, которую я решал в первую очередь - передавать относительно небольшие структуры с оптимизацией передачи в аналоговых сетях. Сложные и большие структуры передавать в таком виде, конечно же не нужно. По сути CJON - это немного очищенный и капельку оптимизированный JSON, позволяющий закодировать структуры данных в смски.

А теперь с CBOR сравните, и тоже со сжатием...

Сравнивал в рамках своего проекта. CBOR в сыром виде конечно на порядок компактнее json'а, но очень слабо поддается сжатию (видимо хорошо продуман и минимизирован). На выходе сжатый json получался +/- того же размера что и сжатый cbor (разница примерно +/-5%)

CJON устраняет кавычки, скобки и пробелы, тем самым экономя от 30% до 50% символов;

YAML делает тоже самое, при этом оставаясь читаемым для человека.

Не уверен, что CJON способен сравниться по читаемости с JSON или YAML даже близко. Давайте попробуем рассмотреть реальные prod-like примеры, а не "на кошках".

Ну а если готовы жертвовать читаемостью в угоду размера и скорости серилизации, то не вижу причин не использовать бинарные форматы вроде protobuf или avro

Очень правильное замечание - в точку. Признаться честно, я сперва хотел использовать yaml и просто упростил с учетом своей задачи:

yaml:
{L1,device:{id:station-17},log:{msg:"~0KHQuNGB0YLQtdC80LAg0L/QtdGA0LXQt9Cw0LPRgNGD0LbQtdC90LA=",level:warning,code:WX123},ts:"2025-07-29T11:12:00Z"}

cjon:
L1;device{id=station-17};log{msg=~0KHQuNGB0YLQtdC80LAg0L/QtdGA0LXQt9Cw0LPRgNGD0LbQtdC90LA=;level=warning;code=WX123};ts=2025-07-29T11:12:00Z

CJON по сравнению c YAML: не требует кавычек, скобок и отступов, работает в одной строке, оптимизирован под текстовую передачу в условиях ограниченного канала.

yaml:

{T1,u:bob,p:"~cGFzc3cwcmQ=",loc:[56.3,36.7],dev:{id:d42,st:ok},ev:[{t:start,c:1},{t:stop,c:2}]}

cjon:

T1;u=bob;p=~cGFzc3cwcmQ=;loc=56.3,36.7;dev{id=d42;st=ok};ev{{t=start;c=1};{t=stop;c=2}}

Зачем изобретать велосипед? В чем проблема использовать protobuf? Лучший бинарный формат. Если надо передавать в смс - завернуть его в base64. Читаемость у данного творения все равно сомнительная.

Думал об этом, но для поставленной задачи придется 1) хранить схемы данных на сервере; 2) увеличивает объём на треть при упаковке в base64; 3) делает сообщение полностью нечитаемым без инструментов; 4) при потере или искажении символа затрудняет восстановление части данных. CJON читается даже если половина данных повредилась.

Большая часть данных в CJON не упаковывается base64 и передается компактно. А для большей оптимизации я уже реализовал на сервере справочник ключей, который для каждой предметной области, определяемой типом сообщения, хранится на сервере и перед отправкой заменяется в JSON, например temperature - t, latitude - l... так что упаковка будет лучше, чем в proto, а главное проще для реализации.

А в контексте вашей задачи точно нельзя передавать бинарные данные в SMS? Ставите в TP-DCS 8-bit data и вперёд, 140 полновесных байт.

Ну и если вдруг нельзя, то логичнее base128 организовать на базе 7-bit GSM alphabet, всё ж поэкономнее.

А в бинарном пейлоуде тогда можно будет и protobuf, и cbor, и компрессию...

Очень крутое предложение, да, это решение, но помимо SMS я хочу иметь альтернативную передачу данных по голосовому каналу в модулированном виде. Про тип смс честно не знал, буду экспериментировать. Спасибо большое. Про base128 я не очень понял вашу мысль? Я base64 кодирую только текст, который не влезает в 7-bit. Остальное не упаковываю никак - зачем, все уже соответствует кодировке. Поясните мысль, пожалуйста.

Про base128: у вас, как я понял, семибитный gsm alphabet сейчас используется, можно взять бинарный payload и распилить его по семибитным границам. Получится те же 140 байт, которые получились бы с TP-DCS 8-bit. Это лишние телодвижения, которые могут быть оправданы, когда например модем с какой-то стороны не умеет SMS PDU MODE.

Как я понял, при передаче по DTMF вы уже полученное семибитное пакуете в base12. Но ведь точно так же вы могли бы и 8bit бинарное паковать!

Так что правильный ответ на «почему не protobuf» всё же скорее «мне весело было придумать CJON» (и ничего в этом плохого не вижу), чем «protobuf не влезает в 8bit, а protobuf+base64 неэкономно)

Что значит зачем, для сжатия же. Например, если делать Variable-Length Integer на 7-битном алфавите, пусть старший (6-й) будет как бит продолжения. Тогда в двух символах можно закодировать число до 2047, тогда как в десятичном это аж 4 символа.

Лучший? Вот уж не надо тут. Один из худших.

Это крайне забавно. Я участвую в CBOR WG в IETF, причем как раз по вопросам дальнейшего сжатия (и так более компактного нежели JSON формата) - имелся как свой у них draft-cbor-packed с фокусом на распаковке constrained IoT-нодой без промежуточного буфера (там всё плохо с памятью как на данные, так и на код); так и я со своим CBAR, делавшимся из других вводных - он делался для транспортного (L4/L5, как TCP или QUIC) протокола muSCTP, предназначенного выживать на пакетах минимум 24 байта (в общем-то пересекается с озвученными в статье областями применения); базовая идея у обоих в общем-то одна - сжать повторяющиеся ключи. Сначала я пробовал на нибблах и секстетах свой формат слепить, потом решил, что на типичном куске 9 vs 10 байт (или 10 vs 11, уж не помню) выгода от экономии не перевешивает сложности имплементации, тестов и т.д. нового формата: куда лучше сделать специфический компрессор для common формата (т.е. приложения смогут пользоваться расширенной моделью данных JSON), а пару лет назад мне еще подсказали компактную либу (Compress::LZF), в смысле добавить два уровня (две фазы) паковки. Идут баталии в мэйллисте насчет недостатков cbor-packed, появляются новые идеи по хитростям увеличения компрессии на базе примера DNS-CBOR (да, в процессе драфт и такого RFC)...

И тут появляется какой-то пижон Сиджон, начисто игнорирует годы рисёрча людей, и заявляет - а давайте делать это ТЕКСТОМ, более того, в base64! BASE64, Карл!!! Я аж подавился и пролистал в начало статьи - это точно не первое апреля?!.. При этом в постановке задачи кроме очерчивания области нет толком вообще ничего, альтернативные форматы упомянуты - но самого сравнения-то нет! (а CBOR, между прочим, четко сформулировал иерархию целей и на каждый сравниваемый формат, в т.ч. MessagePack, который был ближе всего, потратил несколько абзацев, почему это не подходит).

Более того, в постановке задачи даже АЛФАВИТА нет! Из упоминания DTMF (что? он же явно не первичен как определяющий, а будет ниже слоем рекодинга, смысл-то) и SMS можно догадаться, что предполагается не 8-битный канал. Но валидный набор символов не озвучен (я вспоминаю одну странную штуку с алфавитом на 45, из QR-кодов штоль). Можно предположить, однако, что прекрасно подойдёт как минимум base85, а возможно что и наработки времён Fido по произвольным чарсетам (не помню как звали, creeper что ли). Наконец, снова смотреть на возможности канала - мне что-то попадалось насчет кодировки типа baseN, рассчитанной на UTF-8 строку (аналогично можно для неполного UCS-2 придумать).
Далее, человекочитаемым ему быть совершенно незачем, это тоже вредит сжатию. Ему нужно быть лишь человекоотлаживаемым (например, подбором удобных значения для hexdump'ов), а это другое. Нет, способность к восстановлению при повреждениях необязательно требует текстовости формата, её можно вполне обеспечить в бинарном (кстати, в полном 7-битном репертуаре там еще символы 0-31 есть, на минуточку) - это свойство даже у UTF-8 есть (хоть и на уровне отдельных символов, а не более объемлющего структурного формата).

В общем, уровня поиграться "смотрите как я пиарюсь очередным велосипедом" - может быть. В качестве серьезного формата - точно нет, не надо этому CJON засорять инфопространство - лучше направлять усилия туда, где люди уже занимаются проблемой.

Улыбнуло ваше возмущение. Я прекрасно понимаю и принимаю вашу критику, да я пижон, да я решаю проблему в лоб как умею и как мне проще, да невнятно описал задачу и формат описал недостаточно академично - простите, времени не было, а главное - рано, идею я не хочу чтобы увели.

Но ошибки в предложенном формате нет, как я выше написал, по сути это тот же YAML, только чуть упрощенный.

Но вы, несомненно, понимаете, что пишете и спасибо вам за работу и критику. Если вам что-то из вышесказанного пригодится в работе вашего уважаемого консорциума - вэлкам =) p.s. Название CJON же крутое, да?

Академичность хрен с ней, должна быть вся существенная информация для понимания, а не так что только в комментах люди начинают выяснять, причем тут DTMF и каков всё-таки алфавит (на что мы кстати ответа так и не получили). Например, спрашивается, кто мешает "по аналоговым сетям" отправлять не DTMF-писк, а собственно как модемы делают (ну да, будут 9600 в лучшем случае, но уж не 4 бита уже) ?

Но ошибки в предложенном формате нет

А мы не про ошибки говорим (хотя нижеследующее можно назвать и ими - ошибки проектирования), а про эффективность и поддержку возможностей. Например, base85 дает соотношение 5:4 в отличие от 4:3 у base64 - это уже серьезный выигрыш. Потом, что делать с этими сокращениями - lat и lon, кто из них достоин стать просто l? Вручную решать? Так тогда это не generic-формат, типа JSON, а протокол под конкретный прибор и задачу. Далее, что делать, когда в строках потребуются, ну хотя бы еще символы пунктуации, хотя бы те же = и ; что используются в синтаксисе? Эскейпинг начнем придумывать, с потерей эффективности?
Наконец, упоминался IoT, умный дом и подобное, вот давайте представим, умный счетчик должен передать по SMS показания, "ул. Бутырская, д. 42" - ну-ка, как он ПЕРЕДАСТ КИРИЛЛИЦУ ?!
То есть в этом формате не продумано толком ничего, что могло бы потребоваться на практике от general-purpose формата, такого как JSON (а примеры типа taxi намекают на замах повыше умных счетчиков), а уж тем более если подобное понадобилось бы в задачах типа обходов блокировок РКН (то есть вообще ничего предполагать нельзя, лучше поддержать как можно больше, исходя из постановки задачи в виде характеристики канала "доступен алфавит не более N символов").

уважаемого консорциума

IETF не консорциум ISC (который BIND например пишет), а [некоммерческая] организация, занимающаяся стандартами Интернета - выпускает RFC, включая собсно стандарты (например, CBOR помимо RFC 8949 имеет еще номер STD 94)/

Улыбнуло ваше возмущение [...] рано, идею я не хочу чтобы увели. [...] Если вам что-то из вышесказанного пригодится в работе [...] Название CJON же крутое, да?

На самом деле, ржач (а не улыбку), перемешанную с желанием набить морду, вызывает мотивация, которая здесь была показана явно - то есть автор прежде всего желает вы%@нуться, а не решить практическую широко очерченную задачу, которая сделает легче жизнь очень большого количества людей. Вы серьезно думаете, что изобрели нечто такое, до чего не додумались профессионалы, занимающиеся кодировками? Что в посте вообще что-то ценное есть? Если бы такое выкатил 12-летний ребёнок, или даже 10-классник лет 15 назад (до повальной доступности интернета и смартфонов у школьников), то да, мы бы похвалили "как круто ты придумал" (не идею или название, а личностную характеристику, сильно опережающего сверстников). В профессиональной среде же это скорее напоминает историю про парня из монгольской деревни в 1920-х, который пришел в город на машине для езды из выструганных из дерева колёс с перекладинами, на которые нужно было нажимать ногами - он собирался рассказывать, как это лечше лошади, но когда мимо него проехал автомобиль, а потом пролетел самолёт, упал в обморок.
Проблема здесь в том, что типично вот такие "Кулибины", не знающие толком нихрена, выкатывают это кривое и косое, а потом оно внедрено в большом количестве, и просто так эту говнягу не расширить и не пофиксить - так что нанятому за большие деньги разгребать профессионалу, или просто нормальному инженеру, нанятому вместо уволившегося студента-кулибина, приходится очень много плеваться.

Поэтому лучше такое не поощрять, а давить в зародыше. Оно вредит индустрии. Вот, посмотрите в соседнем комменте, я за три дня сочинил формат, который может на шлюзе преобразовываться в нормальный JSON/CBOR, чтоб нормальная система с нормальными ключами дело имела (а конвертация происходила на шлюзе), т.е. поддерживает полное множество (в CBOR ж еще теги есть - как в YAML есть типы, которых нет в JSON), имеет reasonable читабельность ASCII-части текста и определенную устойчивость к повреждениями при всём при том (например, сбрасываем уникодное окно после каждого перевода строки, т.е. размер повреждений будет ограничен, и т.д.), и занимая гораздо меньший размер, нежели этот ваш Сиджон. Попробуйте мой пример перевести в свой CJON - да вы его даже выразить не сможете, как минимум из-за требования "без пробелов" (да, я знаю, что тайштамп лучше передавать числом, в CBOR для этого тег 1 прям в стандарте еще определен, а не позже индивидуально зареган - только обязательно найдутся данные, которые без пробелов и пунктуаций ну не выразите). А что сможете, будет занимать больше места из-за десятичной кодировки чисел и base64 для бинарных данных.

При всём при этом я не рассматриваю этот набросок формата как что-то "нереально крутое" уровня "идею уведут", хотя попутно изобрел еще один способ компрессии Unicode (на BOCU-1 кстати был патент, истекший несколько лет назад). Если надо, я могу это из наброска оформить в полноценную спеку и выпустить RFC. Первоапрельский. Хотя чем чёрт не шутит, может оно правда кому понадобится против РКН, ибо вполне может быть заюзано на практике.

Родил за 3 дня размышлений по теме, формат для передачи модели данных CBOR, т.е. конвертируемый из/в него без ограничений, по неполному 7-битному каналу - считаем, что нам доступны 95 печатных символов ASCII и неизвестное количество управляющих, т.е. может пробелы будет двоить, может только перевод строки доступен, или даже 0x7f недоступен (потому что в случаях что полных 128 символов на 7 бит, что алфавита сильно меньше - задача становится слишком простой и неинтересной, либо уменьшение ai в CBOR на 1 бита и base128, либо UTF-7 или Punycode, потому что на малых алфавитах ничего вменяемого уже не слепить). При этом обеспечивается:

  • сносная защита от повреждений (точнее возможности вручную восстановить)

  • значительная часть ASCII-символов (все буквы, часть пунктуации) в строках идёт as is, читаемо (кроме случаев первого символа при прыжках из более верхних страниц Unicode)

  • обеспечивается сжимаемость текста ЛУЧШЕ чем в UTF-8 в типовых случаев, например "Москва" займет 9 символов, а 12 байт в UTF-8

  • поддерживается дальнейшее уменьшение размера путем вставки атомов из моего пропозала CBAR, причем необязательно даже структурно законченных фрагментов, например $0 может раскрываться в "battery":{"status":"ok, а продолжение объекта будет уже в нашем тексте - т.е. нет необходимости вести жесткие фиксированные сокращения типа b и t или l, а хранить словарь (причем динамически согласовываемый, но это за рамками ) на конвертере и основной системе отдавать нормальные читаемые имена temperature, longitude, latitude в JSON (или лучше CBOR).

Поскольку в посте нет нормальной спецификации, я тоже не стал заморачиваться и вывалю поток рассуждений за три дня, включая отброшенные варианты. Самым сложным было придумать кодирование произвольного Unicode при требовании сохранять часть ASCII читаемым, читателю для понимания стоит предварительно ознакомиться со статьями ASCII85, SCSU и BOCU-1 в Википедии (ну и быть в курсе про CBOR, есть туториал на https://cborbook.com/introduction/introduction.html если что). В результате местами получилось даже элегантно, для 0x7f не потребовался отдельный escape-код, состояние unicode-кодера хранит ровно одно число (ну еще для нулевой страницы надо бы в 0x60 поднимать, пожалуй), переиспользуется один и тот же код для VarIntB85.

Пример кодирования - из вот такой диагностической нотации CBOR (кто не в курсе, она надмножество JSON, в круглых скобках тегированное значение (тег 0 зарегистрирован для дат) и в h'' можно задавать в хексдампе бинарные строки):

[0, 0("2013-03-21T20:04:00Z"), 42, "Hello, World!", false, h'00 00 00 01']

мы имели бы вот такой 46-байтный бинарь в CBOR:

00000000  86 00 c0 74 32 30 31 33  2d 30 33 2d 32 31 54 32  |...t2013-03-21T2|
00000010  30 3a 30 34 3a 30 30 5a  18 2a 6d 48 65 6c 6c 6f  |0:04:00Z.*mHello|
00000020  2c 20 57 6f 72 6c 64 21  f4 44 00 00 00 01        |, World!.D....|
0000002e

а в предлагаемом CBOR-TF7 получим 55 текстовых символов

(#*&*"2013-03-21T20:04:00Z)#T"Hello, World!h)!='****+))

Итак:

формат для кодирования CBOR для 7-битных каналов, где не все управляющие символы гарантированы, и необходима защита от повреждения
делим пространство: символы 33-41 как тип и 42-126 как тело в base85 (0-84), типы таким образом отделяют сами себя
!  - многоцелевой символ, эскейпинг и пр., см. ниже
"  - уникодная строка, самая сложная, ниже
#  - unsigned integer
!# - negative integer, -1-value
$  - half, float или double, т.е. следуют 3, 5 или 10 base85-символов
%  - key/value pairs, т.е. map (JSON object)
&  - номер тега
'  - бинарная строка
(  - начало массива, аналог '['
)  - завершение не примитивного типа: то есть окончание map, array или строк (не просто для надежности, а потому что компрессия атомами возможна)
Для текстовых строк применяем микс из подходов SCSU и BOCU-1, приправленный уменьшением размеров на 64. То есть по умолчанию окно на 0x40 и символы 64-127 представляют в латинице сами себя. Как в BOCU-1, символы 0x00-0x1f сбрасывают состояние в дефолтное, т.е. каждая новая строка заново - повреждение не превысит размеров строки (и сохраняем возможность текстового diff), пробел (0x20) кодирует сам себя и не сбрасывает состояние.
Для символов 42-63 наверное поступить как с пробелом (пусть всегда сами себя кодируют), хотя хотелось подумать насчет переключения окон ими, чтоб покороче.
Насчет '!' еще продумывать, он должен нести как simple values по крайней мере <= 23 (чтобы можно было cbor-packed), т.е. включая false/true/null/undefined, если это одним base85, то null получается !@ например
еще он должен нести паковку атомов аналогично CBAR (simples 32-255 наверное можно в !& отправить), и это должно работать как снаружи, так и внутри строк
если пунктуацию 42-63 всегда разрешать, то ! удлиняет команды свитча окон - сделать его контекстно-зависимым внутри строк и снаружи? вопросы к надежности при повреждениях
!! .. !) - эскейпинг внутри текстовой строки, чтобы не было необходимости включения нулевого окна
!* .. !; - simple(0)..simple(17) снаружи
!< .. !? - снаружи false, true, null, undefined: - 0xe0 + 40 + ai-значение в CBOR, e.g. false = 0xf4 - 0xe0 + 40 = 60 '<'
!@ .. !z - везде: атомы 0-58 CBAR
!{ .. !} - везде: следуют 1, 2 или 3 base85-символа, т.е. размер таблицы атомов 85^3 = 614125, можно к ним 58 прибавить (непринципиально), для коротких 
!~ внутри текстовых: low-level (т.е. на самом деле согласно текущему окну) эскейпинг 0x7f если текущий протокол его не поддерживает
!* .. !? внутри текстовых - самая интересная часть, смена текущего окна, кодирование разницы по принципу BOCU-1
окна блоками по 0x40, т.е. отбрасываем нижние 6 бит, например русская Б U+0411 находится в окне 0x400-0x43f, т.е. когда оно выбрано, имеет код 0x40 + 0x11 = 81 'Q'
коды переключения окон:
!*
!+
!,
!-
!.
!/
!0
!1
!2
!3    = -2
!4    = -1
!5    = +1
!6    = +2
!7    = +3
!8    = +4
!9    = +5
!: x  = +6 .. +91
!;
!<
!=
!> x x = +2957  .. +10181
!? x x = +10182 .. +17407
как-то не очень выходит для кириллицы, может переделать атомы на более короткий интервал...
===
08.08.25
надо просто поменять атомы на $ везде, а флоаты сунуть в ! как симплы
тогда, $ и первый символ после него рассматриваем из принципа 85=17*5 (то есть последние пятерки специальные)
* первые 75 значений - номера атомов как есть
* следующие 5 - следует еще один символ, e.g. $xx - это еще 5*85=425 значений, 
  итого 75+425=500 атомов в сумме для форм $n и $nn
* 'z' - следуют два base85-разряда: $znn
* '{' - запрещенная комбинация - потребители используют для расширений форму, оканчивающаюся '}' - по типу ${varname}
* '|' - следуют три base85-разряда: $|nnn (еще 85^3 = 614125 атомов)
* '}' - запрещенная комбинация (для проверки на случай повреждений)
* '~' - следуют четыре base85-разряда: $~nnnn
Полные 5 не делаем, чтобы в int32 constrained-имплементаций влезала сумма.
Значит, тогда на наружном уровне ! просто как байт-строка, которая, которая была бы от кодирования CBOR Major Type 7, если в том замаскировать старшие 3 бита в 0, примеры:
* 0x00 - simple(0)
* 0x14 - false: !=
* 0x17 - undefined: !@
* коды 24-31 невалидны в CBOR, их можно под что-нибудь зарезервировать
* 0x197c01 (f9 7c01 в CBOR) - half (fp16) с NaN-числом (и единицей в NaN payload) TODO пример енкодинга !xxxx
Остался Unicode. Необходимые команды:
* quote одного изолированного символа, вне окон - например евро или эмодзи какой
  - возможны несколько вариантов по длине числа, до 3 разрядов
* quote следующего символа из окна N, без переключения окна
* change окна на N
* define окна номер N и заодно сразу переключение на него
  - варианты += X, -= X и возможно := X
    - наверное := таки да - улучшение шансов при повреждениях
  - для каждого возможны несколько вариантов по длине числа
  - надо ли аналог OffsetTable[x] из SCSU ? у нас нет целого байта и даже полных 96...
Чтобы не морочиться, берем тот же подход, что для $ - то есть, ! кодирует число (и даже форма !{..} остается зарезервирована), и просто распределяем команды по числам
тогда придется эскейп !~ для 0x7f заменить, в терминале это ^? пусть будет !? тогда? типа зарезервировали один...
значит отводим quote одного изолированного символа далеко, где-то в форме !~nnnn
614125/69631 =~ 8.8197 значит для := берем в форме $|nnn и получаем 8 окон
614125-8*69631 = 57077 столько символов из BMP влезет для изолированного quote, т.е. по DEF5 - ну нормально, всё до суррогатов в !|nnn 5 символов и только потом 6 символов !~nnnn
у нас окна короткие, можно добавить еще curwin в варианты инкремента/декремента
- не, плохая идея, абсолютный номер окна лучше при повреждениях
Initial window positions:
0 - 0x4   - 0040 - ASCII second half
1 - 0x8   - 0080 - Latin-1 Supplement
2 - 0x10  - 0100 - Latin Extended-A
3 - 0x37  - 0370 - Greek
4 - 0x40  - 0400 - Cyrillic
5 - 0x53  - 0530 - Armenian
6 - 0x60  - 0600 - Arabic
7 - 0x304 - 3040 - Hiragana
===
09.08.25
можно плюнуть на эти усложнения и взять подход BOCU-1, правда модифицированный так, что собсно Binary Ordered теряется: середина окна 0xnnn20, т.е. для ASCII 0x60, и -32..+31 от 0x40 до 0x7F - т.е. кодировка ASCII совпадёт сама с собой
а дальше difference именно на символ, тогда "Москва" будет ! и два символа прыжка, потом "о" 1 символ, ! и 1 символ прыжка в 0x420 для "с", снова ! и 1 символ для "к", потом "ва" уже 2 символа
итого 9 символов - в SCSU-подходе могло быть 8, но это всё равно лучше, чем 12 в UTF-8, и кодировать просто
т.е. берём ! и число как с атомами, и просто четные-нечетные распределяем на плюс или минус, раз уж всё равно ordering потерян, зато простота имплементации; ну и 1 значение для 0x7f
и кстати эскейпинг символов '!'..')' тут натуральным способом делается, без дополнительных введений '!!', '!"' и этих символов не будет внутри строки - опять же надежность
стоп, $nn только 500 покрывает, для кириллицы надо более тысячи, значит придется переделать кодирование числа...
сколько нам нужно? от 64 вниз до '!' (33), пробел и управляющие незачем, их нам канал и так предоставляет, значит еще 31-32 вверх и вниз (и одно значение для 0x7f), т.е. 65 - как раз до пятерки добито
значит кодирование числа VarIntB85:
* 0-64 - сами значения разрядов, с '*' (42) по 'j' (106=42+64)
* следующие 15 'k'..'y': еще 15*85 значений, форма xx (добивает даже армянский, не только кириллицу)
* 'z' - следуют два base85-разряда: znn
* '{' и '}' запрещены - зарезервировано для других значения
* '|' - следуют три base85-разряда: |nnn (еще 85^3 = 614125 значений)
* '~' - следуют четыре base85-разряда: ~nnnn
Итого чуть меньше 26 бит.
Тогда для кодирования дельт получаем таблицу из VarIntB85:
0  = +31 сам собой выходит low-level escape of 0x7F (!* не меняет состояние, а трактуется как 0x7f, то есть эквивалентно +31)
1  = -33
2  = +32
3  = -34
4  = +33
5  = -35
6  = +34
...
63 = -64
64 = +63
...
Или формулой на Си:
delta = (value & 0x1 ? -1 : 1) * (31 + (value & 0x1 ? 2 : 0) + (value >> 1))
Вообще может скалярные типы тоже на VarIntB85 перевести? А то байтовые строки костылять из представления самого CBOR...
вообще да, надо: даже 1 байт при байтовой строке дает 2 символа, т.к. отрезаем-то по числу пэддинга (3 из 4 = 3 из 5)
ну а что, для мелких VarIntB85, а дальше можно в скобки, типа:
* #{} 4-8 байт и тег 2 - заодно унификация с BigInt в extended generic data model
* !#{} 4-8 байт и тег 3
правда так 4 байта занимают аж 7 символов после идентификатора типа
чёрт побери, 2^32-1 это |A`6*  - открывающая фигурная возможна, clash
ну можно #}xxxxx и !}xxxxx для 32 бит (и float)
!|hhh для half float, а значения от 255 до этого можно зарезервировать (по типу FE в CBAR)
только это нельзя #{} - надо #{) ибо '}' в чарсете и может встретиться в строке

Ничего себе короткое размышление за три дня. Спасибо большое. Вы прямо очень фундаментально подошли к проблеме и очень профессионально. Вспомнились времена бауманки 1-2 курса, когда мы горячие программисты днями и неделями решали подобные задачи просто потому что это было интересно. Очень круто. Только текст в блок кода зря поместили, задолбался горизонтально скроллить.

Почти убедили меня использовать base85, но:
В GSM 7‑бит многие символы из ASCII 33-126 либо отсутствуют, либо требуют ESC‑последовательности (и как следствие двойную длину): { } [ ] \ ^ ~ | и др. В SMS это критично: набор base85 должен опираться на пересечение разрешённых символов GSM‑7 без ESC, иначе мы теряем выигрыш или снова уходим в UCS‑2.

Ваше предложение отличное, но хочу заметить, что одним из главных критериев моего формата была - простота понимания. CJON читается «на глаз» и парсится простым сплитом, что сильно упрощает отладку в поле. TF7 - черный ящик. Такие ленивые профаны типа меня не захотят даже разбираться =) Я сторонник быстрых и простых достижений.

Про словарь идея отличная и именно для этого я внес типизирование блоков CJON, конечно же я буду для каждой предметной области, инфомационной системы и т.п. делать свой словарь сокращений и внедрять его. Как сделать безопасную синхронизацию на клиенте и сервере - это еще предстоит решить.

Выигрыш "Москва = 9 символов". Это статистический эффект; в худших кейсах кириллица всё равно потребует несколько знаков на букву и выигрыш может испариться.

Еще раз спасибо за размышления. Очень приятно. Предлагайте еще.

Только текст в блок кода зря поместили, задолбался горизонтально скроллить.

Иначе Хабр проинтерпретировал бы чего лишнее как разметку. Просто берется и помещается в текстовый редактор, который переносит строки, и всё сразу легко читается (собственно, из vim оно и было скопировано).

В GSM 7‑бит многие символы из ASCII 33-126 либо отсутствуют, либо требуют ESC‑последовательности (и как следствие двойную длину): { } [ ] \ ^ ~ | и др. В SMS это критично: набор base85 должен опираться на пересечение разрешённых символов GSM‑7 без ESC, иначе мы теряем выигрыш или снова уходим в UCS‑2.

Ну вот видите, как важно ставить задачу правильно, с алфавита надо было начинать. Однако в этом же вашем CJON тоже используются {} и ~ в примерах, так что совершенно неочевидно. Потом, почему пересечение-то? Я погуглил, https://en.wikipedia.org/wiki/GSM_03.38 показывает алфавит, и их всего ничего. Решение в лоб - попросту мапить 8 этих символов (а символы евро или перевода страницы нас не интересуют) на те, которые, которые есть в наборе. Например, на умляуты вон те, или греческие, хорошо различимые (раз уж типа читабельность) относительно других. Потому что ведь формат не должен ограничиваться только SMS (вон там еще DTMF звучал), а для конкретно SMS всё равно понадобится отдельный нижний уровень - они крайне ненадежны, в одну влезать не будет, стандартный протокол конкатенации нескольких частей крив и ограничен (8, ну 16 бит на идентификатор), так что аж нечто вроде аналога TCP может потребоваться.

Если это невозможно (кривой GSM-шлюз/API) и реально остается меньше, то всё опять же упрощается. Можно взять просто CBOR и сунуть его в base85 на доступных 88 символах. Можно взять UCS-2 и пустить по нему аналог base-122 или base32768 или base2048 - их для твитера делали, схожая задача (главное там, понятно, выбранный набор safe codepoints).

Ваше предложение отличное, но хочу заметить, что одним из главных критериев моего формата была - простота понимания. CJON читается «на глаз» и парсится простым сплитом, что сильно упрощает отладку в поле.

Это абсолютно неверный критерий. Отладка делается один раз (ну месяц), а используется потом годы, за которые уменьшенное число пакетов окупится вот даже чисто финансово (ведь SMS не бесплатны). В такой ситуации компактность должна стоять на первом месте, читаемость - ну можно на втором, а сложность реализации вообще не стоять, разве что на десятом (ну да, в экзотике типа разбиения 64 бит на 9 base139 разрядов (каждый из которых можно двумя DTMF-цифрами) можно сказать, что сложность здесь не дает выигрыша относительно base85 32-битных чисел - но это уже реально бездны). Вы, собсно, под "отладкой в поле" что имеете в виду? Какие конкретно стоят девайсы и софт? А то сдается мне, сложность на самом деле надумана

TF7 - черный ящик.

Ничего подобного, он дизайнился достаточно читаемым. Вот голый base85 сырой бинарщины (да как и голый base64) был бы действительно черным ящиком. А так 9 структурных знаков пунктуации учатся очень быстро (даже таблица ASCII выучивается сама собой, если достаточно долго работать, а тут уж). Содержимое текстовых строк остается читаемым в ASCII-подмножестве, короткие числа (один разряд base85) можно быстро научиться читать в уме, имея с собой распечатку ASCII-таблицы - блок же подряд идёт, просто вычесть 42 из символа и всё.

Такие ленивые профаны типа меня не захотят даже разбираться =) Я сторонник быстрых и простых достижений.

Вы то есть, хотите сказать, просто дармоед и самозванец, получающий зарплату зазря? Время "быстрых и простых достижений" в отрасли давно прошло - закон Мура встал. Особенно вот в таких сферах, где не-массовые вещи нужны (хотя бы мозгами). Да при серьезных применениях, с потерями SMS, когда формат дал 3 штуки вместо 2 и одна потерялась, быстро по башке настучат. И всё равно придётся стать человеком, то есть включить мозг и придумать сложное.

Про словарь идея отличная и именно для этого я внес типизирование блоков CJON, конечно же я буду для каждой предметной области, инфомационной системы и т.п. делать свой словарь сокращений и внедрять его. Как сделать безопасную синхронизацию на клиенте и сервере - это еще предстоит решить.

Что значит "безопасную" ? От кого защищаемся? Или типа sha256 словаря передавать слишком длинно, и ссым коллизий?..

Выигрыш "Москва = 9 символов". Это статистический эффект; в худших кейсах кириллица всё равно потребует несколько знаков на букву и выигрыш может испариться.

Для русского - только в случае специально подобранных слов, типа "перепердёж" или микса регистров в каждой букве - каждый займёт два символа, как в UTF-8, но в случае SMS это будет всё еще короче в битах из-за 7-битного представления. С другими языками на основе кириллицы, или тем более каком корейском или китайском - там да, займёт больше (даже BOCU-1 распухал на некоторых текстах, но для обычных алфавитов всё ж была компрессия). Но тут ничего не поделаешь, в 7 бит иначе не впихнуть - разве что придумать другой формат, который рассчитан на UCS-2. Однако в России-то зачем?..

Предлагайте еще.

Толку, если упираетесь в решения попроще и пораздутее?..

Да, вероятно вы правы относительно бинарных форматов и последующей сериализации с использованием алфавита GSM Bit-7. Для конкретной задачи это будет действительно важнее читаемости и простоты реализации.

CJON отложим в копилку недодуманных идей, авось где-то пригодится еще. MiniJSON же пригодился, а CJON эффективнее.

Спасибо.

Только я хотел было сказать (и выкатить версию), что можно и на сокращенном алфавит сделать, если отказаться от полной модели JSON, т.е. когда снаружи у нас только три символа !() и мы в качестве структур используем только списки - S-Expressions в смысле ("как" "в" "Lisp, например" ("или же" "можно" "чуть больше типов" 5 true) ("но списков достаточно" "для любых сложных структур")) и осталось только определить заново скалярные типы...
Как обнаружил, что в GSM и символа обратного апострофа (96) нет (Хабр съест как начало моноширинного в маркдауне, поэтому не привожу), то есть всего отсутствует уже 9 символов! А это из 95 печатных символов ASCII (без 127) при выкидывании пробела (а с ним совсем уж хрупко) оставит нам ровно 85. Ровно.

Но это всё же решаемо, если нам приемлема потеря:

  1. некоторой читаемости

  2. устойчивости к повреждениям

  3. эффективности кодирования уникода за пределами подмножества ASCII

  4. словарных вставок $var (впрочем, их можно вернуть с помощью например \var - если эскейпинг в GSM не включит UCS-2, один лишний символ не страшен, потому что это уже и так укороченная форма - в расчете что текст переменных много длинее)

  5. простота имплементации base85 теряется - вместо простого вычитания стартового символа (33 в оригинале, 42 в CBOR-TF7) придется иметь таблицу, раз у нас дырки в символах.

То есть используем принцип как в CBOR - стартовый байт кодирует небольшое количество значений самого типа либо длину в байтах последующего значения. В смысле придется строки без терминирующего символа иметь, указывая длину в символах (да, это еще и плохо дружит с $varname, но фиг бы с ним пока).
Начинаем так: пусть наш символ base85 - аналог байта, мы можем разделить например на 5 групп по 17 значений, в каждом дать допустим 0-15 на сами значения, еще один - длина следует в следующем символе, и последний - длина следует в двух следующих base85-символах (да, это строки на 7 с лишним килобайт всего, но в SMS длиннее на практике фиг получится).
Значит имеем типы: положительные числа, отрицательные, текстовые строки, бинарные строки, и "всё остальное", куда засунуть simple values и теги. Читаемость плохая, зато имплементировать проще.

Можно ли улучшить читаемость и вернуть модель данных хотя бы JSON? Да, если усложнить имплементацию и делать первый символ не по формуле, а таблицей, причем не одинаковых длин. Например, мы можем отвести:

  • числа 0-9 кодируют сами себя (это даже эффективнее CBOR-TF7)

  • буквы алфавита строки до длиной 26, большие бинарные, маленькие текстовые (или наоборот)

  • скажем, ! будет false и + true - тоже короче CBOR-TF7

  • насовать в пунктуацию дополнительных типов и вернуть старые - типа скобка ( старт массива (бывш. []), % старт мапы (бывш. {}) и ) терминирует любой элемент (это 0xFF в CBOR для indefinite-length)

  • если отрицательные редки, - может стать отдельным модификатором для следующего "обычного интегера" - правда, читаемость -0 под вопросом :)

  • ну с тегами и симплами придется что-то думать. Вероятно, не копировать часть тегов из CBOR "как есть", а тоже дать им свой формат, хоть как с датами, действительно.

Самая жопа, конечно, в том, что всё это станет лучше в отдельных местах за счет эффективности Уникода. Поскольку внутри текстовой строки у нас нет эскейпа на base85 символом извне её репертуара, да еще и надо избежать запрещенных в GSM - это делается ремапингом, например обратный 96 кодируем прямым апострофом, тильду долларом и т.п. - тут еще придется статистику собирать, какая пунктуация используется реже (в реальных данных), чтобы использовать её. И поэтому придется отказаться от подхода BOCU-1 и числа в VarBase85 на подход свитчинга окон SCSU. То есть, символы 64-127 (ремапленые) по-прежнему отображают окно, но пунктуации (постоянной) у нас осталось меньше, поэтому вместо диффа самого символа у нас будет абсолютный номер окна. Скажем, !wwQ куда-то в эмодзи: используем base26, и для признака конца номера просто букву в другом регистре. Так как:

  • символов в уникоде 0x10ffff >> 6 == 17407 окон на 64 символа

  • 26^3 = 17576 как раз хватает Для кириллицы верхний регистр и а-п 1024/64 = 16 т.е. !P бросит туда, но потом еще сама буква. Причем для конкретно "Москва" еще "М" 0x41e, в лоб это 0x5e, попадающий на ^ крышку - тут еще и ремап должен быть. А так: !Pxx!Qx!Pxxx - 12 получается (ну ладно, 7-битных, в отличие от UTF-8).

Как-то так. Сделать это читабельным уровня CBOR-TF7, к которому уже были претензии :) можно, но потребует затрат, и читабельность не писабельность, из-за длин уже только программно.

Я решаю сейчас задачу таким образом. Ввел операторы. Теперь у меня оператор определяет тип. "=" - ничего не меняем, считаем на парсинге, что там ничего запрещенного нет и транслируем как есть; ~ - GSM64 (я выбрал безопасный алфавит); # - целое base36; @ - unixtime base36 +/- смещение в четверти часа тоже base36; ^ - ISO дата в виде эпохи со смещение в base36; ! - boolean с одним из 4 значений (T/F/N/U). Если массив содержит значения без ключей и одного типа, то применим один оператор для всего массива, если хоть одно значение другого типа, то = и для каждого элемента, кроме соответсвующего = ставим его тип в виде оператора. От HEX в цифрах я отказался, для текстового формата плохо. Допилю пример - пришлю.

Так ведь тильда с крышкой не из безопасного GSM.

От HEX в цифрах я отказался, для текстового формата плохо

Это хекс или "неведомая е.ная х.йня" ? base36 ничем не лучше хекса в тексте-то.

Ну, примерно такую версию CBOR-GSM7 родил, булевы получились одним вообще символом, и целых два пунктуации оставлено для творчества расширений типа тех же дат, которым нужно сокращение прям на уровне парсера:

! false
" текстовая строка длиннее 26
# всегда один base85-разряд - simple values: 0-19, потом начиная с 32
$ атом/переменная CBAR: так же, как со строками:
% старт map ('{' в JSON)
& модификатор перед:
  # еще 85 simple values
  буквы - см. ниже как для строк
  0-9:;< относительный номер тега
  => абсолютный номер тега (4/8 байт)
' бинарная строка длиннее 26
( старт массива ('[' в JSON)
) закрытие массива или мапы
* null
+ true
, следуют 3 base85-разряда - half-float (fp16, 2 байта, 0xf9 ... в CBOR)
- модификатор следующего элемента:
  отрицательное если число; -0 зарезервирован
  -" пустая текстовая строка
  -' пустая бинарная строка
  -# последние 85 simple values
. следуют 5 base85-разрядов - float (fp32, 4 байта, 0xfa ... в CBOR)
/ следуют 10 base85-разрядов - double (fp16, 8 байт, 0xfb ... в CBOR)
0-9 числа 0-9
: следует 1 base85-разряд - числа 10-94
; следуют 2 base85-разряда - числа 95-7320
< следуют 3 base85-разряда - еще 614125 чисел
= следуют 5 base85-разрядов - uint32_t (тут уже без плюсований, с нуля для простоты)
> следуют 10 base85-разрядов - uint64_t (две пары по 4 байта)
? undefined
@ или null поместить сюда (^@ в терминалах), или зарезервировано или для какого-нибудь часто используемого типа данных
A-Z бинарная строка длиной 1-26
_ зарезервировано или для какого-нибудь часто используемого типа данных
a-z текстовая строка длиной 1-26

Для четырех " ' $ & чтоб не изобретать новый VarIntB85 используется такой формат:
* A-Z и a-z - два раза по 26
* символы от положительного числа до трех разрядов включительно - прибавить
  еще и их
* символы равно = и больше > тоже как положительное число, но абсолютный номер
  уже без всех заморочек со сложением
При этом для ' и " к полученному числу (если оно меньше размера "без заморочек")
прибавляется еще 26 от совсем коротких строк (в тегах и атомах не прибавляется).
То есть на примерах:
H8bytestr
Istr9bytes
"0This is 79-character string, 'coz 26+ 26 (A-Z) + 26 (a-z) + 0 (first) gives 79!

Внутри строк с атомами засада, $ применить нельзя, но рассчитывая на GSM
escape, можно какой \ или ~ ведь подставляется более длинная строка, поэтому
фиг с ним, с удлинением, в конкретно этом месте. Можно было бы base26
переиспользовать на верхнем уровне, но если для строк это норм (не будут
слишком длинные), то для чисел и номеров тегов вовсе не факт, поэтому
переиспользован [код] для положительных чисел (чтоб не плодить три реализации
чисел переменной длины, оставить только две)

Конечно, с ремаппингом всё хреново. Переделал на куски-чанки, местами VarBase64 появился, чтобы на уменьшенном алфавите. Нет, совсем красиво не выйдет, оверхед на строки минимум 3 символа стал вместо двух, но "Москва" уложилась в 10 хотя бы.

--- внутри текстовой, попытка 1:
мапим 33-36 !"#$ на 123-126 {|}~ и 41-44 )*+, на 91-94 [\]^ и 46 . на 96 `
не, по номерам не очень выходит, лучше по частоте... а еще же 0x7f

не, обойдемся без ремапа. Ведь в GSM не хватает как раз довольно редких
символов, поэтому делаем так:
+hhF - non-locking shift ровно одного символа, от +26 от начала текущего окна,
       чтобы +A было [, +B было \ и т.д. - и это позволит "Москва" писать без
       постоянной смены окна - просто выходим за рамки предыдущего
#cdF - номер окна с некоторым bias, то есть несколько коротких отводим на плюс
       и минус, потом абсолютный
#0   - сменить на окно из стэка, например когда #6 значит #0 станет #1, 
...    #1 станет #2, ... #5 станет #6, #7..9 останутся без изменений;
#9     #буквы смещает #0 -> #1 и т.д. выкидывая #9
И начальные значения для стека окон предефайнены:
0 -  0040 - ASCII second half
1 -  0080 - Latin-1 Supplement
2 -  00C0 - (combined partial Latin-1 Supplement/Latin Extended-A)
3 -  0100 - Latin Extended-A
4 -  0370 - Greek
5 -  0400 - Cyrillic
6 -  0530 - Armenian
7 -  0600 - Arabic
8 -  3040 - Hiragana
9 -  30A0 - Katakana
Значит "Москва" станет
   М о  с ква 
#5+B+bJ+bMzrp
дофига, значит способ с + не очень, всё-таки ремап нужен
--- ремап, попытка 2:
123-127 {|}~ на 59-63 ;<=>? и 91-96 [\]^` на 35-40 #$%^( кроме 95 _ 39 '
пусть !":)*,-. всегда значат сами себя, оставляем + и / для эскейпов
ну / понятно аналог \ в ЯП - эскейпит следующий символ в обычный
можно ли в + применить подход из CBOR-TF7 с полным base85 ?..
в теории можно, но хрупко как-то становится
применим как из " ' $ & на верхнем уровне: буквы A-Za-z и 0-9:;
чот некрасиво выходит...
--- попытка строк 3
применим подход indefinite length строк CBOR - куски длин просто конкатенируются
(они "как будто" в "массиве"), но каждый кусок имеет длину
VarBase64: стандартный алфавит A-Za-z0-9+/ и дальше:

0..47
1100aa bbbbbb                      - from 48 to 303  48 + 256 = 304
1101aa bbbbbb cccccc               - 304 ... 16687    304 + 2^14 = 16688
1110aa bbbbbb cccccc dddddd        - 20 bits as is (for simplicity)
1111aa bbbbbb cccccc dddddd eeeeee - 26 bits

применяется в нижеследующих вместо VarIntB85
" открывает текстовую строку и ) закрывает
' открывает бинарную строку и ) закрывает
внутри бинарных строк:
* VarBase64 - столько следует дальше символов КУСКА строки
* '$' VarBase64 - вставить атом CBAR по номеру
* $<name> - именованные CBAR, как ${z} в CBOR-TF7
внутри текстовых то же, что выше, и дополнительно:
* '#' VarBase64 - один произвольный символ Unicode, окно не меняется
* '_' - сброс в дефолтное ASCII-представление самими собой ("спецокно")
* '+' VarBase64 - инкремент окна
* '-' VarBase64 - декремент окна
* '=' VarBase64 - абсолютное значение окна
Окна считаются по границам выравнивания блоков Unicode, т.е. по 16, то есть
кириллица 0x40, можно даже 0x41 при желании. Внутри окна, если это не
ASCII-спецокно, все 85 символов просто пронумерованы и прибавляются к началу
окна. Таким образом, т.к. 85*3=255, покрывается сразу треть многих кодовых
страниц за раз. Итак, "Москва" будет 10 символов внутри "кавычек" '")':

.-- открывающая всей строки
|
|          .-- закрывающая всей строки
|          |
"=wQG=dleXV)
 |  |
 |  `--- длина 6 букв
 |
 `-- абсолютная страница 0x40, 64 - 48 = 16 'Q' в алфавите base64

и надо б еще эскейпинга для 123-127 {|}~^? и 91-96 [\]^` отсыпать покороче

теперь можно и на верхнем уровне переделать
'$' VarBase64 - номер атома, 26 бит с лихвой
'#' VarBase64 - simple values, они еще незареганы, незачем base85
0 - собственно 0
Положительные:
1 следует 1 base85-разряд
2 следуют 2 base85-разряда
3 следуют 3 base85-разряда
4 следуют 5 base85-разрядов - uint32_t (тут уже без плюсований, с нуля для простоты)
5 следуют 10 base85-разрядов - uint64_t (две пары по 4 байта)
Отрицательные:
- следует 1 base85-разряд
6 следуют 2 base85-разряда
7 следуют 3 base85-разряда
8 следуют 5 base85-разрядов - uint32_t (тут уже без плюсований, с нуля для простоты)
9 следуют 10 base85-разрядов - uint64_t (две пары по 4 байта)
Теги:
: следует 1 base85-разряд
; следуют 2 base85-разряда
< следуют 3 base85-разряда
= следуют 5 base85-разрядов - uint32_t (тут уже без плюсований, с нуля для простоты)
> следуют 10 base85-разрядов - uint64_t (две пары по 4 байта)
Получили & в резерве, а буквы будут переменными в словаре - для паковки/темплейтов,
как ${z} в CBOR-TF7 было
13.08.25
Если в простота важнее эффективности, и плевать на читабельность ASCII-части
уникодных строк, то можно сделать просто два вида текстовых строк (3-я бинарна).
Сначала определим Variable-indefinite-Length Base64 по принципу аналогичных
в ASN.1 DER и много где еще: старший бит 0, если последний, и 1, если есть
продолжение:
0xxxx
1xxxx 0xxxxx
1xxxx 1xxxx 0xxxxx
...и т.д. сколько угодно (сколько позволяет язык, в JS лимит меньше 64 бит).
NB! делаем детерминированный вариант, т.е. 100000 000000 не ноль, а 32 !
(подобное для 8-битных и 128 было, кажется, где-то внутри форматов git)
Читабельность диктует использовать модфицированную таблицу Base64 для этого,
например <a...z01234>A...Z56789 чтоб по регистру сразу видеть старший бит.
Символы + и / исключаем для совместимости с MQTT тогда заодно.
Далее, unsigned на этом всё, а в signed младший бит будет знаком, а само
число сдвигать на два, т.е.:
0 = 0
1 = -1
2 = +1
3 = -2
4 = +2
5 = -3
...
31 = -15
32 = 15
...
Далее в текстовых ASCII-строках используем символы как есть и какой-нибудь
пунктуации неиспользованный на верхнем уровне - по аналогии с \u00AF, но наш
переменный base64 после укоротит для большинства.
А в уникодных строках кодируем чисто диффы:
* начальное состояние = 48 (цифра '0')
* для каждого символа кодируем разницу с текущим запомненным и -> VILBase64
* если символ вышел < 48, принудительно запомнить как 48
* пробелы и другая ASCII-пунктуация (которую разрешил верхний формат) не
  меняют состояние (не запоминаем), а выводим саму себя
* переводы строк, табы и пр. <0x20, будь они енкожены сами собой или же
  диффом, сбрасывают состояние в начальное - ограничение повреждений по
  переводам строки
Таким образом, "Москва" потребует 3 разряда для первого прыжка в 'М', дальше 2
для 'о' и остальные укладываются в расстояние 15, получаем длину 9.
Достоинства:
* как и в UTF-8, границы символов визуально видны
* можно использовать для чисел на верхнем уровне, избавившись от символа на
  верхнем уровне, причем положительные и отрицательные сразу 
  - правда, потребует обязательный символ типа для строк, например ключей

55799 = (7,61,39) в 85, т.е. 4 символа '&1gQ' в CBOR-TF7
соответственно &xxx сигнатуры для всех трех вариантов

! false
" текстовая подстрока: Unicode
# зарезервирован: нельзя в MQTT, свой тип расширения в GSM
$ атом/переменная CBAR: номер VILBase64 или $(name)
% старт map ('{' в JSON)
& тег формы 3 base85-разряда - 614125 чисел (private tags 80000+ влезут)
' бинарная строка: 
  * VILBase64 указывает число base85-символов куска
  * '$' вставить атом CBAR по номеру VarBase64 или $(name)
  * заканчиваться должна ')', пустая строка: ')
( старт массива ('[' в JSON) либо имени для $(...)
) закрытие сложного элемента: массива, мапы, строки, имени
* true
+ зарезервирован: нельзя в MQTT, свой тип расширения в GSM
, следуют 3 base85-разряда - half (fp16, 2 байта после 0xf9 ... в CBOR)
- всегда один base85-разряд - simple values: 0-19 и 32-96
. следуют 5 base85-разрядов - float (fp32, 4 байта после 0xfa ... в CBOR)
/ зарезервирован: нельзя в MQTT, свой тип расширения в GSM
0-9 часть VILBase64
: тег VILBase64 как unsigned, эта форма короче для 1040 и ниже
; следуют 10 base85-разрядов - double (fp64, 8 байт после 0xfb ... в CBOR)
< часть VILBase64
= два вида:
  * снаружи simple values 97+VILBase64 unsigned
  * в ASCII-подстроке VILBase64 для эскейпа одного символа
> часть VILBase64
? undefined
@ null (^@ в терминалах)
A-Za-z часть VILBase64
_ текстовая подстрока: ASCII

Строки работают так: любой из вариантов открывает, они конкатенируются, 
) закрывает, например

   The string "Moscow" translates "Москва" which is 6*2=12 UTF-8 characters.

будет (TBD транслировать иксы в реальную)

   _The string =>bMoscow=' translates ='"XXxXxxxxx_=>b which is 6*2==12 UTF-8 characters.)

Эскейпинг в ASCII требуется для такой пунктуации и для сокращения вместо
VILBase64 может заменяться другой пунктуацией:

  " # $ ) + / = _
  ' % & . * : = -

Если на верхнем уровне мы имеем символ из VILBase64, он трактуется как
знаковое и соответственно - это число, то есть отдельный символ # не требуется,
а один символ может представить от -16 до +15, это лишь в полтора раз хуже чем
в CBOR -24..+23, на 2 символах даже на 1 бит лучше (на трех и более символах
преимущество конечно испаряется)

Общепринятый Base64	и вариант с числом секстетов:
o	40	101xxx	aaaaaa
w 	48 	1100xx	aaaaaa bbbbbb
x 	49 	110001
y 	50 	110010
z 	51 	110011	xx .. 14 бит
0 	52 	1101xx	aaaaaa bbbbbb cccccc
1 	53 	110101
2 	54 	110110
3 	55 	110111	xx .. 20 бит
4 	56 	111000	aaaaaa ... dddddd 4 = 24 бита
5 	57 	111001
6 	58 	111010
7 	59 	111011
8 	60 	111100
9 	61 	111101
+ 	62 	111110
/ 	63 	111111	aaaaaa ... kkkkkk 11 = 66 бит
===
14.08.25
Насчет идей, где увеличить эффективность за счет потери читаемости
и восстановимости... можно применить подход SQLite, где сначала идёт
заголовок, а потом тела строк или длинных чисел - он дает экономию
относительно "прям на месте", если элементы заголовка короче, чем байты
обычных (и бинарных строк). Допустим можно было бы так: 10 base85-разрядов
кодируют 64 бита, в которых старший 1 если header будет дальше еще и 0 если
последний (дальше тела строк), а оставшиеся 63 бита делятся на 9 по 7 бит,
скажем. Правда, выигрыш тут невелик, это ведь близко к наружному
кодированию...
Если перейти на бит-строки, то сначала мог бы идти заголовок из 6-битных,
потом согласно длинам в нём - 7-битные текстовые, и остаток на 8-битные
бинарноые строки; каждый элемент в заголовке - кодирует либо сам маленький
элемент, либо тип и его длину в теле (типа четных текстовых, нечетных
бинарных), но здесь опять уже вопрос сущностей переменной длины встанет (другое
дело, что на длинах SMS там много не надо).
Но такое усложнение не дает много выгоды - вот если бы у нас были переменной
длины бит-строки, то да, но это резкое усложнение кода: фактически писать
часть Huffman-компрессора...

Можно взять FSM-подход с единым алфавитом. Представим, что у нас есть машина
с байтом эдак на 23 бита, тогда мы могли бы уместить в один алфавит весь
Unicode, 256 бинарных байт и все наши специальные символы типов - начало
массива, мапы, их конец... Тогда единый кодер в стиле BOCU-1 просто прыгает
по единому алфавиту.
+ сравнительно просто реализовать - нет нескольких систем кодирования
+ для текстовой строки даже не нужен отдельный тип - мы просто прыгаем в её
  часть алфавита, что и открывает строку (меняет состояние автомата), а потом
  выпрыгиваем
+ в зависимости от данных, бинарные строки могут скомпрессироваться!
- но на плохих данных может наоборот разбухнуть
- нужно тщательно распиливать алфавит на куски для локализации близких частей
- поскольку интегеры, флоаты и теги, и даже атомы, слишком велики - придется
  лишь часть их делать отдельными символами, а остальное считать бинарными
  строками = вопрос, сколько дать на малые
- никакой читаемости и восстановимости (нужен CRC32) и простой конкатенации
  строк (нужен символ резета как в BOCU-1)
- кому непривычны конечные автоматы, может быть сложно делать проверку на
  ошибки, т.е. допустим после символы из байтовой строки пришел другой, мы
  выходим из стейта "float" и переходим в другой и тут мы должны проверить,
  что это корректной длины float, например
Таблицу кодирования желательно бы по принципу BOCU-1:
00 - 3 разряда, еще 614125
01 - 3 разряда, еще 614125
02 - 2 разряда, еще 85*2
03 - 2 разряда, еще 85*2
04 - 2 разряда, еще 85*2
05 - 1 разряда, еще 85
06 - 1 разряд, еще 85
07 - 1 разряд, еще 85
08 - 1 разряд, еще 85
09 - 1 разряд, еще 85
10 -32
...
40 -2
41 -1
42 +0
43 +1
44 +2
..
82 + 2 разряда, еще 85*2
83 + 3 разряда, еще 614125
84 + 3 разряда, еще 614125
85 Reset
тут бы упорядочение по алфавиту бы по-хорошему, как в BOCU-1 чтоб, но мне лень
считать
Вопрос где что ставить? ну например:
0-255 - бинарные байты
256-511 - короткие значения
512-D800+512 - значения до суррогатных пар, прибавоено
потом 256 simple values
и от конца суррогатных пар совпадают номерами с уникодом
--- другой вариант, когда base85 бинарщину на всё, а уже в ней мы делаем ...
Если бы у нас были 7-битные, то как распределить?
можно внутри диапазона по 16 первые 8 - сами значения, еще 8 - длина в байтах
но! этот байт длины будет находиться не сразу, а в body, а мы в хедере; тогда
0x : padding и специалы {[) false/true/null/undefined
     00 - padding (end of header)
     01 - half (2 байта в body)
     02 - float (4 байта в body)
     03 - double (8 байт в body)
     04 - false
     05 - true
     06 - null
     07 - undefined
     08 - пустой массив []
     09 - открытие массива '['
     0a - пустая мапа {}
     0b - открытие мапы '{'
     0c - пустая строка "" 
     0d - текстовая строка из 1 символа
     0e - текстовая строка из 2 символов
     0f - break, закрытие массивов и мапов
1x : атомы
2x : теги
3x : положительные инты
4x : отрицательные инты
5x : байтовые строки 
60-7f - кодирование VarSeptet
110aaaa bbbbbbb - 11 бит
1110aaa bbbbbbb ccccccc - 17 бит
1111aaa bbbbbbb ccccccc ddddddd - 24 бит
Т.е. после декода base85 в байты мы эти байты декодим по 8 септетов
в 7 байтах, каждый как число, которое может быть типом, а может быть символом
из текстовой строки. Если текстовая строка длиннее 2 символов, то число из
двух септетов стартует текстовую строку и содержит её длину - причем в числах,
а не септетах. Дальше внутри строки N чисел кодируют плюс или минус (как
обычно, четные-нечетные), поскольку 0x50=80, в односептетный влезает -40..+39
и значит "Москва" покрывается и займет 2 септета длины 60 06, 2 септета
в "М" из стартового 0x40 и еще 4 буквы по одному, итого 56 бит.
--- еще вариант в том же роде - на VarNibbles из muSCTP
какой из его вариантов выбрать - фиг знает, но в общем наполнение наверное для
однонибблового как выше, до 0x50 пожалуй тоже, а дальше раз у нас уже есть
число без Var-кодирования, то просто вычесть 0x50 для длины текстовой строки
и потом внутри неё четные-нечетные в плюс и минус как обычно; тогда "Москва"
5 6 два длины, три для "М", два для "о", 1 для "с", 1 для "к", 2 "в", 1 "а":
итого 12 нибблов! но так не со всякими словами повезёт конечно, и потом
наружный оверхед по 2 на 3 числа, т.е. тут было 7 чисел, это еще 2 байта и еще
один, который в надежде что заюзан еще под другие элементы

Насчет раздельного хедера (если вдруг), вот выдержки из muSCTP по VarNibbles - т.е. если работать с хексдампом, удобно для отладки (для base85 это применимо только если после декодинга мы как-то дальше работаем с полученными байтами, а не напрямую в base85); включая сегодняшнюю мысль:

TBD 13.02.25 23:00 https://arxiv.org/pdf/2111.12800 about tiny pointers
describes a way of storing bitmap/sizes of pointers, the idea of O(log N)
counts of sizes - may apply this, but VarNibbles array must be sorted for this
and to restore order permutation requires O(log(N!)) ~= O(N^2) space - too
much... however this reminds of Burrows–Wheeler and Move-to-front transforms
- storing a bitmap where each integer starts/ends (just concatenated bitmaps)
  is no different in space in continuation bit in each nibble (like
  SDNV/DER/OID for bytes); if represent as digits-with-commas and encode
  length separate - it's again not different from storing length directly in
  our VarNibble in overall storage, just gives possibility of non 2^N lengths
- huh, bzip2 is complex, two RLE steps...
TBD 14.02.25 01:30 what if instead of length in every integer apply zeroes
compression? while BWT and MTF may be to complex, RLE may be enough:
represent as a string of hex digits, convert every 2+ sequence of 0's to
two nibbles <0 count> or bare 0 if it was just one zero in original, and
then store additional bitmap: for every 0 in stream, bit 0 if it is bare 0
and bit 1 if it is followed by count nibble
* pro: it can also compress zeroes at ends of numbers
* con: worst case of alternating 0 and non-0 nibbles expands by bit per byte
  - may be mitigated by a flag in type byte (we anyway need to store length of
    bitmap) that is not compressed, so this will be better than 12.5% starting
    from 9 bytes
- 18.02.25 02:15 bitmap may be split "streaming": a bitmap byte, then chunk of
  nibbles, as 7 zeroes (including count if any) were seen, another bitmap
  byte, and so on (first bit is flag if compressed, then bitmap, else number
  of bytes) - this is almost same for decoder but simpler for encoder as
  there is no need to return to initial bytes which are variable-length (so
  encoded portion need to be memmove()ed a byte or two), because if after try
  to encode it's constant size to patch whether compression succeeded or not,
  and less to re-try, vs entire set
  - 19.02 00:15 byte is simple, but what if ended not on a byte boundary?
    issue a padding? make that padding real nibble whatever it be, even
    non-counted zero? or reduce bitmap/length to nibble itself - 3 zeroes max?
    - 00:20 then "non-compressed" variant of 8 nibbles has too much overhead,
      12.5% (though still better than CBOR)
    - 00:23 padding real nibble even if it is unbitmaped zero looks better,
      but also need to think about overall padding on stream termination...
      though simple if we got multiple of integer size unpacked, just ignore
      leftover
- 19.02.25 00:34 this scheme is better than all other VarNibbles on values
  more than byte, but worse than all other VarNibbles and even CBOR on
  particular datasets like long runs of values in at least 0-7 range: each
  uint32_t will become '0 <7> val' - that is, 3 nibbles for 1 = 150%!
  - 01:40 <type> <count> <vals> block, where <count> is of original integers,
    type is 0 for all-zeroes, 1/2/3 for that size in nibbles
    type 4..7 - single integer of <type> nibbles
    type 8,9,a,b - 1..4 full 32-bit uncompressed follow
    type <11xx xxxx> = bitmap as above, then take N nibbles from stream of
    "padding" to make it whole number of integers
- 14.08.25 if we are fine with shorter integers than uint32_t - 24 bit - then
  a following scheme could be used: initial byte of chunk contains three
  numbers, 6*6*6=216 - lengths of three following integers, 1 to 6 nibbles;
  if we encoding very specific data, like a number could be:
  * absolute
  * plus delta
  * minus delta
  then root(65536, 5) =~ 9.18 i.e. 2 bytes for 5 integers some of which are
  absolute and some deltas

https://stukalin.com/cbor.html - посмотрите, если не лениво пример со сравнением. Внизу в комбобоксе можно выбрать формат.

Как-то он неинтуитивно работает. Я ввёл JSON, потом решил потыкать кнопки и получил:
Ошибка GSM7 (HEX) → JSON: Неожиданный конец данных
(гм, что-то хабр не хочет вставлять скриншот из буфера)

И CBOR на странице вообще нет. Тесты (селф) два последних не прошли:
❌ CJON @# ISO +03:30 → +e
❌ CJON @# ISO -01:15 → -5

Прошу прощения, вношу правки и отлаживаю. Есть много изменений я вам пришлю, если интересно и опишу правила новые. Пока получается на практике короче упакованного CBOR за исключением массивов с большими числами, вот эту задачу сейчас решаю.

На странице я сразу показываю упакованный для передачи CBOR.

Так а где? CBOR не видать, на данный момент там 4 поля ввода, по порядку
JSON (вход)
GSM 7‑bit (HEX септетов)
CJON (из текущего JSON)
GSM 7‑bit (строка SMS)

https://stukalin.com/cbor.html - если интересно, посмотрите, доработал конвертер, изменил правила формирования полей. CBOR формирую и упаковываю. Показываю и сравниваю размеры. Опционально добавил формирование словаря ключей. CBOR в среднем на 3-5% компактнее, по после кодирования GSM64 становится больше и проигрывает CJON. На тестах постоянно возникает неоднозначность и приходится латать парсер. Сейчас более или менее стабильно работает. Думаю зафиксироваться и продолжить эксперименты с передачей живых данных.

GSM64 это что? Выглядит внутри строк там и чисел вроде как обычный base64. Вроде более-менее понятно, хотя комментарий про "групповые операторы" - что-то явно левое. Самая непонятка там с CBOR - надо бы показать оба этапа, сам CBOR в hex сначала, например. И для SMS-визуализации хекс наверное тоже, она же не совпадает с ASCII.

Примеры для выбора на странице недоделаны - в них меняется словарь, но в CJON получается всё тот же D1, никак не отражено. Где логи по селф-тестам, опять ж непонятно.

Насчет "операторов" (которые тоже неправильно названо, скорее это символы типа, что-то вроде). Явная проблема будет с массивами, потому что у них нет в примере разделителей. В своем вчерашнем я тоже ошибку нашел: + в словаре стандартного base64, так что надо бы для уникода поменять внутри строк...
Заодно вспомнил, что однажды я подходил к решению похожей задачи для MQTT - в нём топики есть UTF-8 строка до 64К, разделенная/слэшами/и символы + и # используются для указания подписки (один уровень / все подуровни), а кроме них запрещены еще контрольные символы 0x00..0x1F и 0x7F..0x8F. Но поскольку они аналоги URL, мне не нужно было изобретать полноценную кодировку - достаточно только скаляра на каждом уровне (без массивов/объектов, ведь сам путь уже можно считать массивом). Поэтому там достаточно первого символа для типа и я остановился чисто на словаре для base85, убрав символы, которые встречаются в строках языков программирования, для удобства хардкодинга/копипасты:

   base85 variant - exclude 0x7f and 10 chars:
    0  1   2   3   4   5   6   7   8   9
    sp "   #   $   '   +   /   \   {   }

Можно было бы это совместить с GSM, но увы, разные наборы...

Думаю зафиксироваться и продолжить эксперименты с передачей живых данных.

Про софт и оборудование расскажите - какие они, как там с эскейпами? Передачей греческих букв из алфавита что делают, не включают UCS?

1) GSM64=BASE64 только с немного измененным алфавитом, я заменил символы +/= на *-_ чтобы избежать ESC и передавать одним септетом.

2) Сперва добавил, потом убрал. CBOR без сортировки ключей.

3) Примеры от балды сделаны, просто похожие на что-то структуры собрал и все. Типы и словари для них буду позднее собирать, когда уже для решения конкретных задач буду использовать. На этапе прототипа это излишне.

4) Про системы и оборудование позднее опишу, если вам интересно, отдельно скину, если все получится как задумано. Пока под NDA не могу разглашать.

Спасибо вам за помощь, много полезного от вас увидел и прочитал. Ну и формат прилично так доработан.

  1. Так ведь в GSM есть эти три символа.

  2. Ну как читать тогда...

  3. я смотрю, сейчас словарь из входных ключей делается - я мусор понаписывал, оно туда подобавлялось

  4. А мне NDA-подробности и не требуются, а исключительно вопрос алфавита и как будет выглядеть "отладка в поле".

По форматам щас еще в соседних ветках кину вариантов.

Думал про даты, для моей задачи даты действительно будут использоваться сплошь и рядом, поэтому спасибо за идею. Отбрасывая вашу неприязнь к изобретенным велосипедам, что скажете, если для дат добавить префикс, например так:

json:
{
"cjon": "L1",
"ts": "2024-09-06T12:32:00Z",
"start": "2024-09-06",
"birth": "06.09.24"
}

cjon:
L1;ts@1725609120;start^2024-09-06;birth_06.09.24

Ох, ну вот опять... Если передавать Unix time, так он всё равно человеком в уме не высчитается, смысл давать его десятичным, если можно хотя бы хекс?.. Я вообще так-то показывал теги CBOR, это обобщенный механизм - в основном используется для задания типов (но некоторыми расширяют сам формат). Вот их уже сколько порегистрировали:
https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
...там среди прочего, кстати, есть например тип географических координат (к примерам в посте) - т.е. такая пара чисел с типом заняла бы короче чем lat= и lon= и даже если оба до l= сократить.

Соответственно, тратить по целому пунктуационному символу на только один тип я нахожу слишком расточительным - пунктуации мало остается на расширение.
Потом, ну вот смысл в дефисах и точках в этой записи? Вы специально троллите самым неэффективным способом? Упорядоченный 240906 еще и сортируется лучше. Далее, если уж для дат отдельный формат, так месяц можно закодировать одной буквой - выучить 12 букв для месяцев для "сохранения читаемости" несложно.

Если вы все равно передаёте тип, и можно обеспечить строгую типизацию на приемнике и передатчике, то дальше ключи не нужны, только значения

Как дополнительную опцию можно сделать возможность передачи значений без ключей. Я подумаю над предложением. Что-то типа T1;A;B;B и парсить это как { "cjon": "T1", "k1": "A", "k2": "B", "k3" : "B" } и на сервере разбирать ключи и заменять их правильными. Пожалуй, это будет полезным в случаях плоских данных аля CSV. Подумаю, спасибо.

Sign up to leave a comment.

Articles