-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnbor.txt
More file actions
1052 lines (1001 loc) · 69.5 KB
/
Copy pathnbor.txt
File metadata and controls
1052 lines (1001 loc) · 69.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
07.08.25
формат для кодирования 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)
только это нельзя #{} - надо #{) ибо '}' в чарсете и может встретиться в строке
===
12.08.25
Только я хотел было сказать (и выкатить версию), что можно и на сокращенном алфавит сделать, если отказаться от полной модели JSON, т.е. когда снаружи у нас только три символа !() и мы в качестве структур используем только списки - S-Expressions в смысле ("как" "в" "Lisp, например" ("или же" "можно" "чуть больше типов" 5 true) ("но списков достаточно" "для любых сложных структур")) и осталось только определить заново скалярные типы...
Как обнаружил, что в GSM и символа обратного апострофа (96) нет (Хабр съест как начало моноширинного в маркдауне, поэтому не привожу), то есть всего отсутствует уже 9 символов! А это из 95 печатных символов ASCII (без 127) при выкидывании пробела (а с ним совсем уж хрупко) оставит нам ровно 85. Ровно.
Но это всё же решаемо, если нам приемлема потеря:
* некоторой читаемости
* устойчивости к повреждениям
* эффективности кодирования уникода за пределами подмножества ASCII
* словарных вставок $var (впрочем, их можно вернуть с помощью например \var - если эскейпинг в GSM не включит UCS-2, один лишний символ не страшен, потому что это уже и так укороченная форма - в расчете что текст переменных много длинее)
* простота имплементации 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, к которому уже были претензии :) можно, но потребует затрат, и читабельность не писабельность, из-за длин уже только программно.
в GSM нет символов девяти символов `[\]^{|}~
Тогда 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
переиспользовать на верхнем уровне, но если для строк это норм (не будут
слишком длинные), то для чисел и номеров тегов вовсе не факт, поэтому
переиспользован [код] для положительных чисел (чтоб не плодить три реализации
чисел переменной длины, оставить только две)
--- внутри текстовой, попытка 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, если есть
продолжение:
0xxxxx
1xxxxx 0xxxxx
1xxxxx 1xxxxx 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 байта и еще
один, который в надежде что заюзан еще под другие элементы
===
16.08.25
для Service Name (16 байт) в muSCTP достаточно до D7FF и без пунктуации, т.е.
можно стартовать с 0x60 скажем, то есть можно разделить блоками по 11 бит:
часть дельта, часть абсолютные
D x = 1101 x -> 11010 = 26; 31-26=5 0b00101
11xxxx xbbbbb cccccc if in 3 sextets, so:
110010 1bbbbb cccccc = first absolute
111111 111111 111111 = U+D7FF last absolute
so:
00aaaa - delta +0..15
01aaaa - delta -1..-16
100aaa bbbbbb - delta to -512
101aaa bbbbbb - delta to +511
11xxxx xbbbbb cccccc - delta + about 10000 where x in 0..4
no, not good.
0..47 = delta -24..+23
11xxxx xbbbbb = 160 values when x in 0..4, -104..+103
0x4e00-BOCU1_REACH_NEG_2; BOCU1_REACH_NEG_2 = -10513
what if variable-length bits? pure static Huffman!
0, 7 bits - both plus-minus, start 0x40 centered gives ASCII
10, 12 bits
11000, 3 bits - select predefined window
1100100, 10 bits?..
1100101, 11 bits = first absolute
1111111, 11 bits = D7FF last absolute
not good in the middle
what if absolute without shift? 11 xx.. starts absolute, then
11 11011 - first which would U+D8000
so
0, 7 bits
10, 10 bits
11, 16 bits till D7FF - absolute
11 11011 - stop code (padding)
11 111, 3 bits - select predefined window
теперь бы это в целое число нибблов перевести
0aaa aaaa - 7 bits of delta
10aa aaaa aaaa - 10 bits of delta
110a aaaa bbbb bbbb - 13 bits of delta
1110 aaaa aaaa bbbb bbbb - absolute BMP
1110 1110 1ppp aaaa aaaa bbbb bbbb - 19 bits of 8 planes
1111 wwww - select predefined window
не получается ровно с BMP и на остальные не хватает бита, если их делать...
Using deltas from 0x40 middles of windows
0aaa aaaa - 7 bits of delta, -64..+63
10aa aaaa aaaa - 10 bits of delta, -576..-65,+64..+575
11xx aaaa bbbb bbbb - delta, -6720..-577,+576..+6719
1111 wwww - select predefined window, no character is output
Windows:
0 - 00C0 - (combined partial Latin-1 Supplement/Latin Extended-A)
1 - 03C0 - Greek
2 - 0440 - Cyrillic
3 - 0540 - Armenian
4 - 0640 - Arabic
5 - 0940 - Devanagari
6 - 1800 - Mongolian
7 - 3040 - Hiragana
8 - 30C0 - Katakana
9 - 4800 - CJK Unihan
a - 6000 - CJK Unihan
b - 7800 - CJK Unihan
c - 9000 - CJK Unihan
d - A800 - Yi, Hangul, Misc
e - C000 - Hangul
f - Reserved
So in worst case of very different CJK symbols it will be 3 bytes per char - as in UTF-8 for BMP
- is enough for ServiceName, but for NBOR need back zero window and E000+
(code F may be assigned for 20 bits of absolute in supplementary planes,
like UQU/UDX of SCSU, 28 bits still less than 4 UTF-8 bytes)
--- 22:00 What if now make original CBAR/NAIR of it?
0 - single nibble of number follows: -8..+7
1 - single nibble of atom follows
2 - single nibble of binary string length (in bytes) follows
3 - single nibble of text string length (in nibbles) follows
4 - open array '['
5 - open map '{'
6 - single nibble of tag follows
7 - break (close array/map) and padding if last odd
10xx yyyy
0 positive int 0..23 or 1,2,3,4,6,8 bytes
1 same for negative int -1-value
0 yyyy is added value, so 8..23 for posititve
1 yyyy is length of value 1,2,3,4,6,8 bytes
1zzz
1100 xxxx .... - character string N nibbles
1101 xxxx .... - binary string N bytes
0nnn nnnn - 0..127
10nn nnnn nnnn - 1023
111s ssss - CBOR Major Type 7, false/true/null/undefined and floats, e.g.
* 1111 1000 ssss ssss - simple 32..255
* 1111 1011 and 4 nibbles of half float
не, фигня получается, уже хуже CBOR для многих коротких, а еще атомы и теги не
--- а если переупорядочим, начиная как CBOR, т.е. 00..3F - числа, атомы и теги
0tt aaaaa
\
0 - positive
1 - negative
2 - atom
3 - tag
aaaaa = 110xx xxxx - 24..87
111nn - 2^n bytes follows
100a aaaa - text string length
0..23
110xx xxxx - 24..87 nibbles
111nn - n+1 nibbles of length follow
1010 aaaa - binary string length
0..15
10xx xxxx - 16..80 bytes
11nn - n+1 nibbles of length follow
b - open array '['
c - open map '{'
d - break (close array/map)
111s ssss - CBOR Major Type 7, false/true/null/undefined and floats
уже лучше, но не сильно
--- 00:00 так ведь та же проблема, которую решают VarNibbles, то есть надо на них!
0..15 - 16 values
16..255 - 240 values
256..4095 - 3840 values
...
0 - integer 0
1 - integer 1
2 - integer -1 (or 2 if QoS)
3 - atom 0
4 - tag 0
5 - false
6 - true
7 - null
8 - undefined
9 - empty binary string ''
10 - empty text string ""
11 - open atoms-interspersed binary string
12 - open atoms-interspersed text string
13 - open array '['
14 - open map '{'
f - break, close array/map/atom-interspersed-string
--
16 - uint 4 bytes in body
17 - nint 4 bytes in body
18 - atom 4 bytes in body
19 - tag 4 bytes in body
20 - uint 6 bytes in body
21 - nint 6 bytes in body
22 - atom 6 bytes in body
23 - tag 6 bytes in body
24 - Reserved
25 - half (2 bytes in body)
26 - float (4 байта в body)
27 - double (8 байт в body)
28 - uint 8 bytes in body
29 - nint 8 bytes in body
30 - atom 8 bytes in body
31 - tag 8 bytes in body
32+:
M = (N - 32) / 8 + 1
R = (N - 32) % 8:
0 - uint M
1 - negint -1-M
2 - binary string M bytes
3 - atom M
4 - text string 2*M nibbles (M bytes)
5 - text string 2*M + 1 nibbles
6 - tag M
7 - simple(M-1) to 255, then Reserved
===
17.08.25
Это можно и на VarSextets кстати... правда тогда пустые массивы (и менее 23 от
цифры в CBOR) станет занимать больше бит, чем в CBOR
5^3=125 а что если на пентадах? и даже переменной длины, чисто битстроки уже
правда тогда придется Unicode переделать
или вносить внутрь (если в %7, то для коротких не оч выходит), или тоже сделать на VSG, но скажем на другой таблице длин
т.е. код 126 для сигнатуры, код 125 как конец = дальше уникод или padding, и после терминации 1..5 отображаются уже на другие длины для уникода
да, без leading bits их взять, типа 7,10,14,21,4 по таблице - можно же каждый отдельно рассматривать
блэт, проблема во всех вариантах: возьмем числа 17,18,19 - это 3 байта в CBOR, но 4 в VarNibbles
вот для уникода это хорошо работает, потому что он просто числа, к тому же обычно сгруппированные в плане дельт хорошо
а что, если рекурсивно применить? пусть у нас есть массив чисел, назначим каждому "цвет" и закодируем цвета вне массива самих чисел тем же перемножением
* uint и его длина
* nint и его длина
* atom
* tag (и его длина?)
* длина бинарной
* длина unicode
* simple
* '[', '{', ')' и два открывающих для atoms-interspersed строк - вне цветов
самих чисел
итого 9 до структурных, которых еще 5, причем длин мало, их можно вынести в отдельные "вне цветов", а из simple можно false/true/null вынести тоже
если структуру выносить отдельно в форме "число элементов", то всего 4 выходит - просто /4 %4 в едином числе
без выноса в body? если pentads, 13 пентад = 65 бит по четный-нечетный можно оба типа интов закодить, и аналогичное разделение atom/tag
13^8/2^30 = 0.75970843527466 хуже чем 6*6*6/256=0.84375
хотя это как раз удобно для последних неполных
13^7 = 62748517 = 0x03bd7765
13^8 = 815730721 = 0x309f1021
1110... значит 7 значений
11110.. значит 6 значений и т.д., удобно вместо пэддинга-то
так это сначала красим элемент, а потом еще длину, т.е. на 8 чисел 20 будет 30 бит и еще 30 бит и потом 8 пентад = 100 бит vs 64 в CBOR
а если побольше взять? 5 на 32 бита, чтоб сразу и заголовок напрямую символами кодился?
а 85^5 больше 2^32 т.е. надо понижать если хотим чтоб без GSM бинарный мог тоже использоваться
это 82*85^4 значит в заголовке не более 82 значений, это 55 без simple/true/...float, мда
если брать длины в байтах в body по 4,6,8, то можно просто взять раскладку выше, переместив симплы в 24-й, а остальное на длины в массиве чисел
фактически их нужно только 6 на каждый тип, значит 6*8=48 кодов и еще 2 дать малым интегерам
хотя это длины до 8 нибблов, а на 4 байта уже отвели, значит еще больше дать
тогда ряд чисел 15,15,15,15,15 займет в CBOR 5 байт = 40 бит, а у нас 32 + 4*5 = 52 бит; а 19,20,21,22.. еще больше
ну да, оверхед же log2(82) ~= 6.358 бит
тогда понизить число символов? cbrt(2^16) =~ 40.3 уже меньше 5.33 бит
всё равно много будет
вообще это неправильная идея цветов массива чисел - они в этой теории должны быть максимального размера, по uint64_t - чтобы не было "или длина" для интов
цвета: uint, nint, binary, text, atom, tag, simple, '[', '{', atoms-interspersed binary string, atoms-interspersed text string
итого 11, 11^9 чуть больше 2^31 т.е. 9 штук на 32 бит - и это еще без учета оверхеда на компрессию нибблов
а их только методом битмапов, да и то, 00 00 00 00 00 00 00 01 станет 0<7>1 аж три ниббла (и 1 бит)
то есть можно было бы сказать, что к этим 11 надо добавить 5 и сунуть в сам поток, чтоб заюзать +80 значений следующего ниббла, но толку, эдакое восстановление лишь некоторое
а если пойти как во вчерашнем методе для Unicode? минимальный делать 1 байт, но в целом в нибблах
0xxx xmmm - 4 бит
10xx xxxx xmmm - 7 бит
110x xxxx xxxx xmmm - 10 бит
111y ymmm
где m - major type, y = 2,4,6,8 байт в body
хуже CBOR на 16..23, но лучше на 24...1023, приемлемый компромисс
только максимальная длина текстовых строк получается 512 байт, мало
1110 xxxx xxxx xxxx xmmm - 13 бит
1111 ymmm - y=4 or 8 bytes in body
а хотя зачем body? при выровненности по нибблу можно прямо тут пустить же аргументом!
т.е. это получается CBOR initial byte стал бывать пошире, и в body останутся только contents of bytestrings
в смысле и содержимое unicode тоже сдвигается сюда же в аргументы
mmm:
0 - uint
1 - nint
2 - bytestring: yy higher bit open or close, low if count:
0 - 2 bytes
1 - 4 bytes
2 - 6 bytes
3 - atom-interspersed open
3 - text string - same as byte string
TBD not so elegant here - no twice of nibble or =1 prohibited
TBD may be a mode where Unicode 000 (ASCIIZ) is terminator?
4 - array and map: count=x/2, is_map = x%2, yy: same as bytestring
5 - atom
6 - tag
7 - simple: rearranged first 32 (then > 32 as in CBOR):
0 - false
1 - true
2 - null
3 - undefined
4 - half (2 bytes in body)
5 - float (4 bytes in body)
6 - double (8 bytes in body)
7 - simple(0)
...
15 - simple(9)
...
24 - simple(19)
... reserved
32+ as in CBOR, 256-1023 reserved
yy=10 is BREAK - close opened array, map or atom-interspersed string
yy=11 is STOP, then padding 0 or 1 nibbles and body starts
yy 0..1 is reserved
--- 02:56 не, это слишком generic и не учитывает малые размеры пакетов
надо по 24 значения на uint, nint, tag для on-par CBOR - текстовые строки
могут терминироваться нулём, что максимум 2 байта (окно и сам ноль), так что
нет нужды им отводить длины более 2 байт
0xxx xxxx - 7 bits
uint 24
nint 24
bytestring 24
tag 24
0x60 atom 8
0x68 array 0..3
0x6c map 0..3
0x70 empty text string
0x71 NUL-terminated text string
0x72-7f text string 2-15 nibbles
10xx xxxx xxxx - 10 bits
110x xxxx xxxx xxxx - 13 bits
serial type: M = x / 8 and % 8:
0 - uint
1 - nint
2 - bytestring length
3 - atom
4 - text string 2*M nibbles (M bytes)
5 - text string 2*M + 1 nibbles
6 - tag M
7 - simple(M) till 255
1110 mmyy <N*2 nibbles>
0 - uint \
1 - nint > yy = 2/4/6/8 bytes
2 - tag /
3 :
0 - bytestring 2 bytes
1-3 atom yy = 2/3/4 bytes
1111 ssss
0 - open array [
1 - open map {
2 - open atom-interspersed bytestring
3 - bytestring length 3 bytes follows
4 - false
5 - true
6 - null
7 - undefined
8 - Reserved
9 - half (2 bytes in body)
a - float (4 bytes in body)
b - double (8 bytes in body)
если в Unicode переделать Windows, ведь дельта добивает же:
10aa aaaa aaaa - 10 bits of delta, -576..-65,+64..+575
11xx aaaa bbbb bbbb - delta, -6720..-577,+576..+6719
1111 wwww - select predefined window, no character is output
0 - 0040 - ASCII
1 - 1840 - Mongolian
2 - 3040 - Hiragana, Katakana
3 - 4840 - CJK Unihan
4 - 6040 - CJK Unihan
5 - 7840 - CJK Unihan
6 - 9040 - CJK Unihan
7 - A840 - Yi, Hangul, Misc
8 - C040 - Hangul
9 - F840 - from E000 to some in SMP
a - atom 1 byte
b - atom 2 bytes
c - atom 3 bytes
d - atom 4 bytes
e - 20 bits absolute value from supplementary planes
f - Reserved
===
18.08.25
проблема в том, что 6^3 имеет одинаковый оверхед на все варианты длин, к тому
же не выровненную по байту, а версии NBOR от 17.08 имеют его разным
в зависимости от длины - Хаффман же... что, если паковать префиксы отдельно,
в соответствии с вероятностями, которые обычно и учитываются в Golomb, Elias
и т.д.?
0 - 2 ниббла, 9 бит всего
10 - 1 ниббл, 6 бит всего
110 - 3 ниббла, 15 бит всего
...
тогда фактически занимаемый минимальный размер 6 бит - неплохо
лишь только байт занимает на 1 бит больше CBOR, для указания длин - много
00 - 1 ниббл, major тип и длина в bytestrings area
01 - 1 nibble, shortest values
10 - 2 nibbles, next values
110 - 3 nibbles, 15 bit total
...
фактически начало как VarSextets, но дальше длины включая оверхед отличаются:
6, 6, 10, 15, ... на нибблах
6, 6, 12, 18, ... а было бы на секстетах
это можно рассматривать как преимущество - лучше читаемость и меньше бит на меньшие значения
причем первые два по 1 нибблу как кодирующие 32, дальше еще 256, поэтому
в serial codes необязательно вымерять блоками по 16
0 - integer 0
1 - integer 1
2 - integer 2
3 - integer -3
4 - integer -2
5 - integer -1
6 - empty binary string ''
7 - empty text string ""
8 - switch into UTF-VN mode, exit if NUL character would be output
9 - empty array []
10 - empty map {}
11 - open atoms-interspersed binary string
12 - open atoms-interspersed text string
13 - open array '['
14 - open map '{'
15 - break, close array/map/atom-interspersed-string
16 - uint 4 bytes in body
17 - nint 4 bytes in body
18 - atom 4 bytes in body
19 - tag 4 bytes in body
20 - false
21 - true
22 - null
23 - undefined
24 - simple(0)
25 - 2 bytes in body: half
26 - 4 bytes in body: float
27 - 8 bytes in body: double
28 - uint 8 bytes in body
29 - nint 8 bytes in body
30 - atom 8 bytes in body
31 - tag 8 bytes in body
32+:
M = (N - 32) / 8 + 1
R = (N - 32) % 8:
0 - uint M
1 - negint -1-M
2 - binary string M bytes
3 - atom M
4 - text string 2*M nibbles (M bytes)
5 - text string 2*M + 1 nibbles
6 - tag M
7 - simple(M) to 255, then Reserved
TBD 22.08.25 array of short integers! and CBAR extended codes (FE)
1110 - 4 nibbles
111100 - 5 nibbles
111101 - 6 nibbles
111110 - 7 nibbles
111111 - STOP
итого 9 кодов? или меньше? если им назначить вероятности, можно посчитать для
вводного байта таблицу, чтобы не 6^3 было, а все 256 на комбинации, и не
требовалось разрывать битовые последовательности между блоками...
или не морочить голову, 216 для 3 по 6, и 40 распределить руками на 4 штуки
самых частых
* bbbb - 16 вариантов по 1 нибблу из 0 и 1
* tbbb и другие позиции t - с третьим нибблом, еще 24 комбинации
- не хватает стоп кодов... значит пэддинги условно 1-ниббловые 0-15 и общая
длина где-то в начале
Итого 2^20 + 2^16 + 2^12 + 2^8 + 32 значений - ну прям Unicode и 4 тыщи атомов
не, 2080 атомов: D800-DFFF и 32, а 2^8 и 2^12 на дельты
то есть центры окон по 0x80 как BOCU-1, но проще него
ну а почему б и не 2 варианта UTF-N и UTF-VN ?
первый для ServiceName и ограничен BMP, второй внутри NBOR
весь вопрос сугубо в том, окажется ли VarNibbles эффективнее?
а если просто два типа дать? это ж только один новый код отвести, ведь атомы