poe-svelte-ui-lib 1.61.0 → 1.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/protocols/cbor/base64.d.ts +2 -0
- package/dist/protocols/cbor/base64.js +18 -0
- package/dist/protocols/cbor/decoder.d.ts +33 -0
- package/dist/protocols/cbor/decoder.js +445 -0
- package/dist/protocols/cbor/encoder.d.ts +29 -0
- package/dist/protocols/cbor/encoder.js +354 -0
- package/dist/protocols/cbor/json.d.ts +12 -0
- package/dist/protocols/cbor/json.js +130 -0
- package/dist/protocols/cbor/types.d.ts +62 -0
- package/dist/protocols/cbor/types.js +67 -0
- package/dist/protocols/cbor/value.d.ts +12 -0
- package/dist/protocols/cbor/value.js +125 -0
- package/dist/protocols/crc.d.ts +2 -0
- package/dist/protocols/crc.js +20 -0
- package/dist/protocols/index.d.ts +8 -0
- package/dist/protocols/index.js +14 -0
- package/dist/protocols/packet.d.ts +31 -0
- package/dist/protocols/packet.js +105 -0
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -59,6 +59,8 @@ npm install poe-svelte-ui-lib
|
|
|
59
59
|
|
|
60
60
|
**Названия смарт-виджетов** — в словаре библиотеки на всех языках (WiFi, «Об устройстве», «Файловая система», «Стек и задачи», «RGB лента», «Пилотажный дисплей», «Карта»); пустой `label.name` показывает название на языке пользователя, `widgetTitle(type)` отдаёт его сразу на всех языках `{ ru, en, zh }` — так конструктор заполняет заголовок нового виджета.
|
|
61
61
|
|
|
62
|
+
**Ядро протоколов** — `poe-svelte-ui-lib/protocols` (без Svelte, работает и под Node без Vite): общий для DevCloud, WebApp и SYP-DevTerminal CBOR-кодек (RFC 8949) — `encodeCbor`/`decodeCbor`, `CborEncoder`/`CborDecoder`; `valueToCbor`/`cborToValue` — обычные значения, двоичные данные как `Uint8Array` (WebSocket, хранилище, телеметрия); `jsonToCbor`/`cborToJson` — JSON-совместимые значения, двоичные данные в base64, строки под ключами с `BIN` — байты (поля ввода терминала и отладки). Пакет `poe_cbor_pkt` `{ TrgBusID : [RetBusID, HA_Code, Payload, CRC16] }` (CAN, RS485): `encodePacket`/`decodePacket` (разбор с начала буфера — `complete` с длиной, `incomplete` для потока или `garbage`; объекты с одинаковым `TrgBusID` не склеиваются, неверный CRC — флаг `crcOk`), `objectsFor` (свой адрес и broadcast `0xFFFF`), `makeHaCode`/`splitHaCode`, `packetObjectCrc`; контрольные суммы `crc16Kermit` и `crc8Rohc`. Байты совпадают с `poe_cbor_pkt.c` прошивки — проверка `npm run TestProtocols` (`tests/protocols.test.mjs`, эталонные пакеты собраны прошивочным кодом). Дальше сюда переезжает разбор потоков POESerial/CAN/RS485 — своих копий протоколов в приложениях не держим.
|
|
63
|
+
|
|
62
64
|
**Шапка смарт-виджета** (`WidgetHeader`): значок, заголовок и сворачивание слева, «?» в правом краю; правая часть (режимы, «Обновить» и т. п.) стоит в той же строке, а если не помещается — уходит второй строкой под заголовок и там переносится.
|
|
63
65
|
|
|
64
66
|
**Подсказки смарт-виджетов** — `hints.ts`: у каждого виджета своя кнопка «?» в правом краю шапки (`WidgetHeader` с пропом `hints`), по умолчанию подсказки выключены, выбор оператора запоминается в браузере для виджетов этого типа (`hintsSetting("WidgetPFD")`, ключ `localStorage` `WidgetHints.<тип>`, пишется только по нажатию «?»), глобального переключателя нет. Элемент получает подсказку действием `use:hint={{ text, enabled: $hints }}` — всплывающее окно в стиле темы над элементом; тексты — в словаре библиотеки (`widget.*.hint.*`), только там, где смысл поля не очевиден.
|
|
@@ -141,7 +143,7 @@ npm run Formatting # prettier --write .
|
|
|
141
143
|
|
|
142
144
|
Связь настраивается на npmjs.com → пакет → Settings → Trusted Publisher: GitHub Actions, `Oleg-Perevyshin/ElementsUI`, workflow `npm_publish.yaml`, разрешение «npm publish». Новая связь подтверждается первой успешной публикацией и должна пройти её в течение 2 дней, иначе истекает. Существующую связь изменить нельзя — только удалить и создать заново.
|
|
143
145
|
|
|
144
|
-
**Обновление DevCloud — автоматически.** После публикации job `update-devcloud` того же workflow ждёт новую версию в npm, клонирует `xPOE-Studio/DevCloud` (Forgejo) по секрету `DEVCLOUD_ACCESS_TOKEN` (токен Forgejo с правом записи в репозиторий), ставит `poe-svelte-ui-lib@<версия>`, прогоняет `svelte-kit sync`, `prisma generate` (с заглушкой `DATABASE_URL`, к БД не подключается) и `svelte-check` и, если проверка прошла, пушит в `main` коммит
|
|
146
|
+
**Обновление DevCloud — автоматически.** После публикации job `update-devcloud` того же workflow ждёт новую версию в npm, клонирует `xPOE-Studio/DevCloud` (Forgejo) по секрету `DEVCLOUD_ACCESS_TOKEN` (токен Forgejo с правом записи в репозиторий), ставит `poe-svelte-ui-lib@<версия>`, прогоняет `svelte-kit sync`, `prisma generate` (с заглушкой `DATABASE_URL`, к БД не подключается) и `svelte-check` и, если проверка прошла, пушит в `main` коммит «Автообновление poe-svelte-ui-lib до <версия> — переразвёртывание облака [deploy]» с пояснением, что он создан автоматически при выпуске библиотеки, — облако разворачивается само. Вручную версию в облаке не поднимаем; изменения кода облака, которым нужна новая версия библиотеки, коммитятся без `[deploy]` до выпуска — их развернёт этот коммит. Если `svelte-check` упал, коммита нет, облако остаётся на прежней версии (смотрите лог job в GitHub Actions).
|
|
145
147
|
|
|
146
148
|
---
|
|
147
149
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* Преобразование массива байт в строку base64 */
|
|
2
|
+
export function bytesToBase64(data) {
|
|
3
|
+
let binary = "";
|
|
4
|
+
const len = data.length;
|
|
5
|
+
for (let i = 0; i < len; i++) {
|
|
6
|
+
binary += String.fromCharCode(data[i]);
|
|
7
|
+
}
|
|
8
|
+
return btoa(binary);
|
|
9
|
+
}
|
|
10
|
+
/* Преобразование строки base64 в массив байт Uint8Array */
|
|
11
|
+
export function base64ToBytes(data) {
|
|
12
|
+
const binaryString = atob(data);
|
|
13
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
14
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
15
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
16
|
+
}
|
|
17
|
+
return bytes;
|
|
18
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type CborValue } from "./types.js";
|
|
2
|
+
export declare class CborDecoder {
|
|
3
|
+
private state;
|
|
4
|
+
constructor(buffer: ArrayBuffer | Uint8Array);
|
|
5
|
+
get offset(): number;
|
|
6
|
+
decode(): CborValue;
|
|
7
|
+
skipValue(): boolean;
|
|
8
|
+
decodeBytes(): {
|
|
9
|
+
value: Uint8Array;
|
|
10
|
+
length: number;
|
|
11
|
+
} | null;
|
|
12
|
+
private readIndefiniteBytes;
|
|
13
|
+
private readIndefiniteString;
|
|
14
|
+
private decodeStringWithLength;
|
|
15
|
+
private decodeBytesWithLength;
|
|
16
|
+
private readHeader;
|
|
17
|
+
private decodeByType;
|
|
18
|
+
private skipByType;
|
|
19
|
+
private decodeArray;
|
|
20
|
+
private decodeMap;
|
|
21
|
+
private decodeTag;
|
|
22
|
+
private decodeSimple;
|
|
23
|
+
private readUint8;
|
|
24
|
+
private peekUint8;
|
|
25
|
+
private readUint16;
|
|
26
|
+
private readUint32;
|
|
27
|
+
private readUint64;
|
|
28
|
+
private hasBytes;
|
|
29
|
+
private bitsToFloat16;
|
|
30
|
+
private bitsToFloat32;
|
|
31
|
+
private bitsToFloat64;
|
|
32
|
+
}
|
|
33
|
+
export declare function decodeCbor(buffer: ArrayBuffer | Uint8Array): CborValue;
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
/* CBOR декодер - реализует декодирование бинарного формата CBOR (RFC 8949) */
|
|
2
|
+
import { CborMajorType, CborAdditionalInfo, CborSimpleValue, CborTag, CBOR_BREAK, CBOR_MAX_DEPTH, CborError, CborEndOfDataError, CborDepthLimitError, } from "./types.js";
|
|
3
|
+
/* Класс декодера CBOR */
|
|
4
|
+
export class CborDecoder {
|
|
5
|
+
state;
|
|
6
|
+
constructor(buffer) {
|
|
7
|
+
const uint8Array = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
|
|
8
|
+
this.state = {
|
|
9
|
+
buffer: new DataView(uint8Array.buffer, uint8Array.byteOffset, uint8Array.byteLength),
|
|
10
|
+
offset: 0,
|
|
11
|
+
length: uint8Array.byteLength,
|
|
12
|
+
depth: 0,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/* Сколько байт уже прочитано — граница значения при разборе потока по одному значению */
|
|
16
|
+
get offset() {
|
|
17
|
+
return this.state.offset;
|
|
18
|
+
}
|
|
19
|
+
/* Основной метод декодирования - декодирует одно значение CBOR */
|
|
20
|
+
decode() {
|
|
21
|
+
this.state.depth++;
|
|
22
|
+
if (this.state.depth > CBOR_MAX_DEPTH) {
|
|
23
|
+
this.state.depth--;
|
|
24
|
+
throw new CborDepthLimitError();
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const header = this.readHeader();
|
|
28
|
+
const value = this.decodeByType(header);
|
|
29
|
+
this.state.depth--;
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
this.state.depth--;
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/* Пропустить текущее значение CBOR */
|
|
38
|
+
skipValue() {
|
|
39
|
+
this.state.depth++;
|
|
40
|
+
if (this.state.depth > CBOR_MAX_DEPTH) {
|
|
41
|
+
this.state.depth--;
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const header = this.readHeader();
|
|
46
|
+
const result = this.skipByType(header);
|
|
47
|
+
this.state.depth--;
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
this.state.depth--;
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/* Декодировать байтовую строку (в т.ч. indefinite-length, через чанки) */
|
|
56
|
+
decodeBytes() {
|
|
57
|
+
const savedOffset = this.state.offset;
|
|
58
|
+
try {
|
|
59
|
+
const header = this.readHeader();
|
|
60
|
+
if (header.majorType !== CborMajorType.ByteString) {
|
|
61
|
+
this.state.offset = savedOffset;
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const result = header.value === BigInt(-1) ? this.readIndefiniteBytes() : this.decodeBytesWithLength(Number(header.value));
|
|
65
|
+
if (!result) {
|
|
66
|
+
this.state.offset = savedOffset;
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
this.state.offset = savedOffset;
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/* Читает чанки indefinite-length байтовой строки до BREAK и склеивает их (заголовок уже прочитан вызывающим) */
|
|
77
|
+
readIndefiniteBytes() {
|
|
78
|
+
const chunks = [];
|
|
79
|
+
let totalLength = 0;
|
|
80
|
+
while (this.state.offset < this.state.length) {
|
|
81
|
+
if (this.peekUint8() === CBOR_BREAK) {
|
|
82
|
+
this.readUint8();
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
const chunk = this.decodeBytes();
|
|
86
|
+
if (!chunk)
|
|
87
|
+
return null;
|
|
88
|
+
chunks.push(chunk.value);
|
|
89
|
+
totalLength += chunk.length;
|
|
90
|
+
}
|
|
91
|
+
const result = new Uint8Array(totalLength);
|
|
92
|
+
let offset = 0;
|
|
93
|
+
for (const chunk of chunks) {
|
|
94
|
+
result.set(chunk, offset);
|
|
95
|
+
offset += chunk.length;
|
|
96
|
+
}
|
|
97
|
+
return { value: result, length: totalLength };
|
|
98
|
+
}
|
|
99
|
+
/* Читает чанки indefinite-length текстовой строки до BREAK и склеивает их (заголовок уже прочитан вызывающим) */
|
|
100
|
+
readIndefiniteString() {
|
|
101
|
+
let value = "";
|
|
102
|
+
let totalLength = 0;
|
|
103
|
+
while (this.state.offset < this.state.length) {
|
|
104
|
+
if (this.peekUint8() === CBOR_BREAK) {
|
|
105
|
+
this.readUint8();
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
const chunkHeader = this.readHeader();
|
|
109
|
+
if (chunkHeader.majorType !== CborMajorType.Utf8String || chunkHeader.value === BigInt(-1))
|
|
110
|
+
return null;
|
|
111
|
+
const chunk = this.decodeStringWithLength(Number(chunkHeader.value));
|
|
112
|
+
if (!chunk)
|
|
113
|
+
return null;
|
|
114
|
+
value += chunk.value;
|
|
115
|
+
totalLength += chunk.length;
|
|
116
|
+
}
|
|
117
|
+
return { value, length: totalLength };
|
|
118
|
+
}
|
|
119
|
+
/* Декодировать строку с известной длиной (внутренний метод) */
|
|
120
|
+
decodeStringWithLength(length) {
|
|
121
|
+
try {
|
|
122
|
+
if (!this.hasBytes(length)) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
const bytes = new Uint8Array(this.state.buffer.buffer, this.state.buffer.byteOffset + this.state.offset, length);
|
|
126
|
+
const value = new TextDecoder().decode(bytes);
|
|
127
|
+
this.state.offset += length;
|
|
128
|
+
return { value, length };
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/* Декодировать байтовую строку с известной длиной (внутренний метод) */
|
|
135
|
+
decodeBytesWithLength(length) {
|
|
136
|
+
try {
|
|
137
|
+
if (!this.hasBytes(length)) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
const value = new Uint8Array(this.state.buffer.buffer, this.state.buffer.byteOffset + this.state.offset, length);
|
|
141
|
+
this.state.offset += length;
|
|
142
|
+
return { value, length };
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/* Прочитать заголовок CBOR (первый байт) */
|
|
149
|
+
readHeader() {
|
|
150
|
+
const first = this.readUint8();
|
|
151
|
+
const majorType = first >> 5;
|
|
152
|
+
const additional = first & 0x1f;
|
|
153
|
+
let value;
|
|
154
|
+
if (additional < CborAdditionalInfo.NumByte) {
|
|
155
|
+
/* Значение в дополнительных битах */
|
|
156
|
+
value = BigInt(additional);
|
|
157
|
+
}
|
|
158
|
+
else if (additional === CborAdditionalInfo.Indefinite) {
|
|
159
|
+
value = BigInt(-1);
|
|
160
|
+
}
|
|
161
|
+
else if (additional === CborAdditionalInfo.NumByte) {
|
|
162
|
+
value = BigInt(this.readUint8());
|
|
163
|
+
}
|
|
164
|
+
else if (additional === CborAdditionalInfo.Float16) {
|
|
165
|
+
value = BigInt(this.readUint16());
|
|
166
|
+
}
|
|
167
|
+
else if (additional === CborAdditionalInfo.Float32) {
|
|
168
|
+
value = BigInt(this.readUint32());
|
|
169
|
+
}
|
|
170
|
+
else if (additional === CborAdditionalInfo.Float64) {
|
|
171
|
+
value = this.readUint64();
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
throw new CborError(`Unknown additional info: ${additional}`);
|
|
175
|
+
}
|
|
176
|
+
return { majorType: majorType, additional, value };
|
|
177
|
+
}
|
|
178
|
+
/* Декодировать значение по типу заголовка */
|
|
179
|
+
decodeByType(header) {
|
|
180
|
+
switch (header.majorType) {
|
|
181
|
+
case CborMajorType.UnsignedInteger:
|
|
182
|
+
return Number(header.value);
|
|
183
|
+
case CborMajorType.NegativeInteger:
|
|
184
|
+
return -1 - Number(header.value);
|
|
185
|
+
case CborMajorType.ByteString: {
|
|
186
|
+
const bytesValue = header.value === BigInt(-1) ? this.readIndefiniteBytes() : this.decodeBytesWithLength(Number(header.value));
|
|
187
|
+
/* Не хватило байт — конец данных (в потоке это «ждать ещё»), а не испорченный CBOR */
|
|
188
|
+
if (!bytesValue)
|
|
189
|
+
throw new CborEndOfDataError();
|
|
190
|
+
return bytesValue.value;
|
|
191
|
+
}
|
|
192
|
+
case CborMajorType.Utf8String: {
|
|
193
|
+
const stringValue = header.value === BigInt(-1) ? this.readIndefiniteString() : this.decodeStringWithLength(Number(header.value));
|
|
194
|
+
if (!stringValue)
|
|
195
|
+
throw new CborEndOfDataError();
|
|
196
|
+
return stringValue.value;
|
|
197
|
+
}
|
|
198
|
+
case CborMajorType.Array:
|
|
199
|
+
return this.decodeArray(header);
|
|
200
|
+
case CborMajorType.Map:
|
|
201
|
+
return this.decodeMap(header);
|
|
202
|
+
case CborMajorType.Tag:
|
|
203
|
+
return this.decodeTag(header);
|
|
204
|
+
case CborMajorType.Simple:
|
|
205
|
+
return this.decodeSimple(header);
|
|
206
|
+
default:
|
|
207
|
+
throw new CborError(`Unknown major type: ${header.majorType}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/* Пропустить значение по типу */
|
|
211
|
+
skipByType(header) {
|
|
212
|
+
switch (header.majorType) {
|
|
213
|
+
case CborMajorType.UnsignedInteger:
|
|
214
|
+
case CborMajorType.NegativeInteger:
|
|
215
|
+
return true;
|
|
216
|
+
case CborMajorType.ByteString:
|
|
217
|
+
case CborMajorType.Utf8String:
|
|
218
|
+
if (header.value === BigInt(-1)) {
|
|
219
|
+
/* Indefinite length - пропускаем до break */
|
|
220
|
+
while (this.state.offset < this.state.length) {
|
|
221
|
+
if (this.peekUint8() === CBOR_BREAK) {
|
|
222
|
+
this.readUint8();
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
if (!this.skipValue())
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
return this.hasBytes(Number(header.value)) ? ((this.state.offset += Number(header.value)), true) : false;
|
|
231
|
+
case CborMajorType.Array:
|
|
232
|
+
if (header.value === BigInt(-1)) {
|
|
233
|
+
while (this.state.offset < this.state.length) {
|
|
234
|
+
if (this.peekUint8() === CBOR_BREAK) {
|
|
235
|
+
this.readUint8();
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
238
|
+
if (!this.skipValue())
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
for (let i = 0; i < Number(header.value); i++) {
|
|
244
|
+
if (!this.skipValue())
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
return true;
|
|
248
|
+
case CborMajorType.Map:
|
|
249
|
+
if (header.value === BigInt(-1)) {
|
|
250
|
+
while (this.state.offset < this.state.length) {
|
|
251
|
+
if (this.peekUint8() === CBOR_BREAK) {
|
|
252
|
+
this.readUint8();
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
if (!this.skipValue())
|
|
256
|
+
return false;
|
|
257
|
+
if (!this.skipValue())
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
for (let i = 0; i < Number(header.value); i++) {
|
|
263
|
+
if (!this.skipValue())
|
|
264
|
+
return false;
|
|
265
|
+
if (!this.skipValue())
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
return true;
|
|
269
|
+
case CborMajorType.Tag:
|
|
270
|
+
return this.skipValue();
|
|
271
|
+
case CborMajorType.Simple:
|
|
272
|
+
if (header.value >= BigInt(24) && header.value <= BigInt(27)) {
|
|
273
|
+
const bytes = [2, 4, 8][Number(header.value) - 24];
|
|
274
|
+
return this.hasBytes(bytes) ? ((this.state.offset += bytes), true) : false;
|
|
275
|
+
}
|
|
276
|
+
return true;
|
|
277
|
+
default:
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
/* Декодировать массив */
|
|
282
|
+
decodeArray(header) {
|
|
283
|
+
if (header.value === BigInt(-1)) {
|
|
284
|
+
/* Indefinite length */
|
|
285
|
+
const result = [];
|
|
286
|
+
while (this.state.offset < this.state.length) {
|
|
287
|
+
if (this.peekUint8() === CBOR_BREAK) {
|
|
288
|
+
this.readUint8();
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
result.push(this.decode());
|
|
292
|
+
}
|
|
293
|
+
return result;
|
|
294
|
+
}
|
|
295
|
+
const count = Number(header.value);
|
|
296
|
+
const result = [];
|
|
297
|
+
for (let i = 0; i < count; i++) {
|
|
298
|
+
result.push(this.decode());
|
|
299
|
+
}
|
|
300
|
+
return result;
|
|
301
|
+
}
|
|
302
|
+
/* Декодировать словарь (map) */
|
|
303
|
+
decodeMap(header) {
|
|
304
|
+
if (header.value === BigInt(-1)) {
|
|
305
|
+
/* Indefinite length */
|
|
306
|
+
const result = new Map();
|
|
307
|
+
while (this.state.offset < this.state.length) {
|
|
308
|
+
if (this.peekUint8() === CBOR_BREAK) {
|
|
309
|
+
this.readUint8();
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
const key = this.decode();
|
|
313
|
+
const value = this.decode();
|
|
314
|
+
result.set(key, value);
|
|
315
|
+
}
|
|
316
|
+
return result;
|
|
317
|
+
}
|
|
318
|
+
const count = Number(header.value);
|
|
319
|
+
const result = new Map();
|
|
320
|
+
for (let i = 0; i < count; i++) {
|
|
321
|
+
const key = this.decode();
|
|
322
|
+
const value = this.decode();
|
|
323
|
+
result.set(key, value);
|
|
324
|
+
}
|
|
325
|
+
return result;
|
|
326
|
+
}
|
|
327
|
+
/* Декодировать тег */
|
|
328
|
+
decodeTag(header) {
|
|
329
|
+
const tagValue = header.value;
|
|
330
|
+
const taggedValue = this.decode();
|
|
331
|
+
return new CborTag(Number(tagValue), taggedValue);
|
|
332
|
+
}
|
|
333
|
+
/* Декодировать простое значение */
|
|
334
|
+
decodeSimple(header) {
|
|
335
|
+
const additional = header.additional;
|
|
336
|
+
const value = header.value;
|
|
337
|
+
if (additional === CborSimpleValue.False)
|
|
338
|
+
return false;
|
|
339
|
+
if (additional === CborSimpleValue.True)
|
|
340
|
+
return true;
|
|
341
|
+
if (additional === CborSimpleValue.Null)
|
|
342
|
+
return null;
|
|
343
|
+
if (additional === CborSimpleValue.Undefined)
|
|
344
|
+
return null;
|
|
345
|
+
/* Float types - используем value из заголовка (оно уже содержит биты float) */
|
|
346
|
+
if (additional === CborAdditionalInfo.Float16) {
|
|
347
|
+
/* value содержит биты float16 */
|
|
348
|
+
return this.bitsToFloat16(Number(value));
|
|
349
|
+
}
|
|
350
|
+
if (additional === CborAdditionalInfo.Float32) {
|
|
351
|
+
/* value содержит биты float32 */
|
|
352
|
+
return this.bitsToFloat32(Number(value));
|
|
353
|
+
}
|
|
354
|
+
if (additional === CborAdditionalInfo.Float64) {
|
|
355
|
+
/* value содержит биты float64 */
|
|
356
|
+
return this.bitsToFloat64(value);
|
|
357
|
+
}
|
|
358
|
+
/* Simple value (0-19, 24-27) */
|
|
359
|
+
if (additional < 24) {
|
|
360
|
+
return Number(value);
|
|
361
|
+
}
|
|
362
|
+
return Number(value);
|
|
363
|
+
}
|
|
364
|
+
/* Вспомогательные методы чтения */
|
|
365
|
+
/* Прочитать 1 байт */
|
|
366
|
+
readUint8() {
|
|
367
|
+
if (this.state.offset >= this.state.length) {
|
|
368
|
+
throw new CborEndOfDataError();
|
|
369
|
+
}
|
|
370
|
+
return this.state.buffer.getUint8(this.state.offset++);
|
|
371
|
+
}
|
|
372
|
+
/* Подсмотреть 1 байт без продвижения */
|
|
373
|
+
peekUint8() {
|
|
374
|
+
if (this.state.offset >= this.state.length) {
|
|
375
|
+
throw new CborEndOfDataError();
|
|
376
|
+
}
|
|
377
|
+
return this.state.buffer.getUint8(this.state.offset);
|
|
378
|
+
}
|
|
379
|
+
/* Прочитать 2 байта (big-endian) */
|
|
380
|
+
readUint16() {
|
|
381
|
+
if (this.state.offset + 2 > this.state.length) {
|
|
382
|
+
throw new CborEndOfDataError();
|
|
383
|
+
}
|
|
384
|
+
const value = this.state.buffer.getUint16(this.state.offset, false);
|
|
385
|
+
this.state.offset += 2;
|
|
386
|
+
return value;
|
|
387
|
+
}
|
|
388
|
+
/* Прочитать 4 байта (big-endian) */
|
|
389
|
+
readUint32() {
|
|
390
|
+
if (this.state.offset + 4 > this.state.length) {
|
|
391
|
+
throw new CborEndOfDataError();
|
|
392
|
+
}
|
|
393
|
+
const value = this.state.buffer.getUint32(this.state.offset, false);
|
|
394
|
+
this.state.offset += 4;
|
|
395
|
+
return value;
|
|
396
|
+
}
|
|
397
|
+
/* Прочитать 8 байт (big-endian) */
|
|
398
|
+
readUint64() {
|
|
399
|
+
if (this.state.offset + 8 > this.state.length) {
|
|
400
|
+
throw new CborEndOfDataError();
|
|
401
|
+
}
|
|
402
|
+
const value = this.state.buffer.getBigUint64(this.state.offset, false);
|
|
403
|
+
this.state.offset += 8;
|
|
404
|
+
return value;
|
|
405
|
+
}
|
|
406
|
+
/* Проверить есть ли n байт в буфере */
|
|
407
|
+
hasBytes(n) {
|
|
408
|
+
return this.state.offset + n <= this.state.length;
|
|
409
|
+
}
|
|
410
|
+
/* Преобразовать биты в float16 */
|
|
411
|
+
bitsToFloat16(bits) {
|
|
412
|
+
const sign = (bits >> 15) & 0x1;
|
|
413
|
+
const exp = (bits >> 10) & 0x1f;
|
|
414
|
+
const mant = bits & 0x3ff;
|
|
415
|
+
if (exp === 0) {
|
|
416
|
+
return Math.pow(-1, sign) * Math.pow(2, -24) * mant;
|
|
417
|
+
}
|
|
418
|
+
if (exp === 31) {
|
|
419
|
+
if (mant === 0) {
|
|
420
|
+
return sign ? -Infinity : Infinity;
|
|
421
|
+
}
|
|
422
|
+
return NaN;
|
|
423
|
+
}
|
|
424
|
+
return Math.pow(-1, sign) * Math.pow(2, exp - 25) * (mant | 0x400);
|
|
425
|
+
}
|
|
426
|
+
/* Преобразовать биты в float32 */
|
|
427
|
+
bitsToFloat32(bits) {
|
|
428
|
+
const buffer = new ArrayBuffer(4);
|
|
429
|
+
const view = new DataView(buffer);
|
|
430
|
+
view.setUint32(0, bits, false);
|
|
431
|
+
return view.getFloat32(0, false);
|
|
432
|
+
}
|
|
433
|
+
/* Преобразовать биты в float64 */
|
|
434
|
+
bitsToFloat64(bits) {
|
|
435
|
+
const buffer = new ArrayBuffer(8);
|
|
436
|
+
const view = new DataView(buffer);
|
|
437
|
+
view.setBigUint64(0, bits, false);
|
|
438
|
+
return view.getFloat64(0, false);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
/* Утилита для декодирования CBOR из буфера */
|
|
442
|
+
export function decodeCbor(buffer) {
|
|
443
|
+
const decoder = new CborDecoder(buffer);
|
|
444
|
+
return decoder.decode();
|
|
445
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type CborValue } from "./types.js";
|
|
2
|
+
export declare class CborEncoder {
|
|
3
|
+
private buffer;
|
|
4
|
+
constructor(initialCapacity?: number);
|
|
5
|
+
getResult(): Uint8Array;
|
|
6
|
+
encodeNull(): this;
|
|
7
|
+
encodeBoolean(value: boolean): this;
|
|
8
|
+
encodeUnsigned(value: number | bigint): this;
|
|
9
|
+
encodeInt(value: number | bigint): this;
|
|
10
|
+
encodeFloat32(value: number): this;
|
|
11
|
+
encodeFloat64(value: number): this;
|
|
12
|
+
encodeNumber(value: number): this;
|
|
13
|
+
private canBeFloat16;
|
|
14
|
+
private float32ToFloat16;
|
|
15
|
+
private float16ToFloat32;
|
|
16
|
+
encodeFloat16(value: number): this;
|
|
17
|
+
encodeString(value: string): this;
|
|
18
|
+
encodeBytes(value: Uint8Array | number[]): this;
|
|
19
|
+
encodeArrayStart(count: number | null): this;
|
|
20
|
+
encodeMapStart(count: number | null): this;
|
|
21
|
+
encodeBreak(): this;
|
|
22
|
+
encodeTag(tag: number | bigint): this;
|
|
23
|
+
encode(value: CborValue): this;
|
|
24
|
+
private writeIndefiniteHeader;
|
|
25
|
+
private writeHeader;
|
|
26
|
+
private bigIntToBytes;
|
|
27
|
+
private writeBytes;
|
|
28
|
+
}
|
|
29
|
+
export declare function encodeCbor(value: CborValue): Uint8Array;
|