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
/**
* @file lyb.h
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief Header for LYB format printer & parser
*
* Copyright (c) 2020 - 2025 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
;
;
/**
* LYB format
*
* Unlike XML or JSON, it is binary format so most data are represented in similar way but in binary.
* Some notable differences:
*
* - schema nodes are identified based on their hash instead of their string name. In case of collisions
* an array of hashes is created with each next hash one bit shorter until a unique sequence of all these
* hashes is found and then all of them are stored;
*
* - data are preceded with information about the used context in the form of context hash - the exact same
* context must be used for parsing the data to guarantee that all the schema nodes get the same hash;
*
* - tree structure is represented as a parent followed by all of its children, recursively;
*
* - numbers are of variable size using encoding similar to UTF-8 and of 2 kinds - count and size, each
* with a different use and corresponding efficient encoding of different value intervals.
*
* This is a short summary of the format:
* @verbatim
lyb_data = "lyb" lyb_header siblings <padding to a full byte>
lyb_header = LYB_HEADER_VERSION_NUM LYB_HEADER_HASH_ALG YANG-context-hash
siblings = (node_id node_content)* LYB_NODE_END
node_id = (LYB_NODE_CHILD schema_hash) | (LYB_NODE_EXT module schema_name) | (LYB_NODE_TOP module schema_hash)
schema-hash = <generated 1 B hash unique among siblings, more B in case of collisions>
module = <STRING module name> <module revision encoded on 2 B>
schema_name = <STRING schema node name>
node_content = opaq_content | leaflist_content | leaf_content | list_content | any_content | inner_content
opaq_content = <all members of an opaque node, details seen in lyb_print_node_opaq()>
leaflist_content = leaf_content+ LYB_METADATA_END_COUNT
leaf_content = node_header value
list_content = (node_header siblings)+ LYB_METADATA_END_COUNT
any_content = node_header any_value
inner_content = node_header siblings
node_header = <COUNT of metadata> metadata* <node flags on 2 b>
metadata = module <STRING metadata name> value
value = <SIZE of value if of variable size>? VALUE
any_value = (LYD_ANYDATA_DATATREE siblings) | (LYD_ANYDATA_STRING <STRING value>)
| (LYD_ANYDATA_XML <STRING XML value>) | (LYD_ANYDATA_JSON <STRING JSON value>)
STRING = <COUNT of characters> <character>*
COUNT = <variable size number encoding seen in lyb_write_count()>
SIZE = <variable size number encoding seen in lyb_write_size()>
VALUE = <data generated by type-specific print callbacks>
@endverbatim
*/
/**
* @brief LYB data node type
*/
;
/**< number of required bits required for all node types */
/**< number of required data node flag bits, fixed LYB size */
/**
* @brief LYB format printer context
*/
;
/**
* @brief LYB format parser context
*/
;
/**< current LYB format version */
/**< LYB format version reserved bit size */
/**< current LYB hash function */
/**< LYB hash algorithm reserved bit size */
/**< LYB shrinked flag reserved bit size */
/**< context hash reserved bit size (full hash is 32 b) */
/**< reserved count of metadata instances used for the last instance of (leaf-)list */
/**< reserved encoded number of metadata instances */
/**< number of required bits for reserved metadata instance count */
/**< opaque node format LY_VALUE_XML */
/**< opaque node format LY_VALUE_JSON */
/**< number of required bits for all LYB opaque formats */
/**
* LYB schema hash constants
*
* Hash is divided to collision ID and hash itself.
*
* @anchor collisionid
*
* First bits are collision ID until 1 is found. The rest is truncated 32b hash.
* 1xxx xxxx - collision ID 0 (no collisions)
* 01xx xxxx - collision ID 1 (collision ID 0 hash collided)
* 001x xxxx - collision ID 2 ...
*
* When finding a match for a unique schema (siblings) hash (sequence of hashes with increasing collision ID), the highest
* collision ID can be read from the last hash (LYB parser).
*
* To learn what is the highest collision ID of a hash that must be included in a unique schema (siblings) hash,
* collisions with all the preceding sibling schema hashes must be checked (LYB printer).
*/
/**< number of bits the whole hash will take (including hash collision ID) */
/**< masking 32b hash (collision ID 0) */
/**< type for storing the whole hash (used only internally, publicly defined directly) */
/**< need to move this first >> collision number (from 0) to get collision ID hash part */
/**< module revision as XXXX XXXX XXXX XXXX (2B) (year is offset from 2000)
* YYYY YYYM MMMD DDDD */
/**
* @brief Get single hash for a schema node to be used for LYB data. Read from cache, if possible.
*
* @param[in] node Node to hash.
* @param[in] collision_id Collision ID of the hash to generate, see @ref collisionid.
* @return Generated hash.
*/
LYB_HASH ;
/**
* @brief Module DFS callback filling all cached hashes of a schema node.
*/
LY_ERR ;
/**
* @brief Get a mask with specific number of rightmost bits set.
*
* @param[in] bit_count Number of bits to set in the mask.
* @return Bit mask.
*/
uint8_t ;
/**
* @brief Get a mask with specific number of leftmost bits set.
*
* @param[in] bit_count Number of bits to set in the mask.
* @return Bit mask.
*/
uint8_t ;
/**
* @brief Shift all bytes in an array to the right.
*
* @param[in] buf Buffer with bytes to shift.
* @param[in] count_bytes Count of bytes in @p buf.
* @param[in] shift Number of bits to shift.
*/
void ;
/**
* @brief Prepend bits to an array of bytes, starting at the first byte from right (left shift).
*
* @p byte_bits number of bits are lost in the last byte.
*
* @param[in] buf Buffer with bytes to prepend to.
* @param[in] count_bytes Count of bytes in @p buf.
* @param[in] byte Byte with the bits to prepend.
* @param[in] byte_bits Number of @p byte bits to prepend.
*/
void ;
/**
* @brief Truncate a hash to rightmost bits and make sure it is non-zero.
*
* If these bits are all zeroes, next bits are used.
*
* @param[in] hash Hash to truncate.
* @param[in] hash_bits Size of the truncate hash in bits.
* @return Truncated non-zero hash.
*/
uint32_t ;
/* LY_LYB_H_ */