Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
pb.h
00001 /* Common parts of the nanopb library. Most of these are quite low-level 00002 * stuff. For the high-level interface, see pb_encode.h and pb_decode.h. 00003 */ 00004 00005 #ifndef _PB_H_ 00006 #define _PB_H_ 00007 00008 /***************************************************************** 00009 * Nanopb compilation time options. You can change these here by * 00010 * uncommenting the lines, or on the compiler command line. * 00011 *****************************************************************/ 00012 00013 /* Define this if your CPU architecture is big endian, i.e. it 00014 * stores the most-significant byte first. */ 00015 /* #define __BIG_ENDIAN__ 1 */ 00016 00017 /* Increase the number of required fields that are tracked. 00018 * A compiler warning will tell if you need this. */ 00019 /* #define PB_MAX_REQUIRED_FIELDS 256 */ 00020 00021 /* Add support for tag numbers > 255 and fields larger than 255 bytes. */ 00022 /* #define PB_FIELD_16BIT 1 */ 00023 00024 /* Add support for tag numbers > 65536 and fields larger than 65536 bytes. */ 00025 /* #define PB_FIELD_32BIT 1 */ 00026 00027 /* Disable support for error messages in order to save some code space. */ 00028 /* #define PB_NO_ERRMSG 1 */ 00029 00030 /* Disable support for custom streams (support only memory buffers). */ 00031 /* #define PB_BUFFER_ONLY 1 */ 00032 00033 /* Switch back to the old-style callback function signature. 00034 * This was the default until nanopb-0.2.1. */ 00035 /* #define PB_OLD_CALLBACK_STYLE */ 00036 00037 00038 /****************************************************************** 00039 * You usually don't need to change anything below this line. * 00040 * Feel free to look around and use the defined macros, though. * 00041 ******************************************************************/ 00042 00043 00044 /* Version of the nanopb library. Just in case you want to check it in 00045 * your own program. */ 00046 #define NANOPB_VERSION nanopb-0.2.5 00047 00048 /* Include all the system headers needed by nanopb. You will need the 00049 * definitions of the following: 00050 * - strlen, memcpy, memset functions 00051 * - [u]int8_t, [u]int16_t, [u]int32_t, [u]int64_t 00052 * - size_t 00053 * - bool 00054 * 00055 * If you don't have the standard header files, you can instead provide 00056 * a custom header that defines or includes all this. In that case, 00057 * define PB_SYSTEM_HEADER to the path of this file. 00058 */ 00059 #ifdef PB_SYSTEM_HEADER 00060 #include PB_SYSTEM_HEADER 00061 #else 00062 #include <stdint.h> 00063 #include <stddef.h> 00064 #include <stdbool.h> 00065 #include <string.h> 00066 #endif 00067 00068 /* Macro for defining packed structures (compiler dependent). 00069 * This just reduces memory requirements, but is not required. 00070 */ 00071 #if defined(__GNUC__) || defined(__clang__) 00072 /* For GCC and clang */ 00073 # define PB_PACKED_STRUCT_START 00074 # define PB_PACKED_STRUCT_END 00075 # define pb_packed __attribute__((packed)) 00076 #elif defined(__ICCARM__) 00077 /* For IAR ARM compiler */ 00078 # define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)") 00079 # define PB_PACKED_STRUCT_END _Pragma("pack(pop)") 00080 # define pb_packed 00081 #elif defined(_MSC_VER) && (_MSC_VER >= 1500) 00082 /* For Microsoft Visual C++ */ 00083 # define PB_PACKED_STRUCT_START __pragma(pack(push, 1)) 00084 # define PB_PACKED_STRUCT_END __pragma(pack(pop)) 00085 # define pb_packed 00086 #else 00087 /* Unknown compiler */ 00088 # define PB_PACKED_STRUCT_START 00089 # define PB_PACKED_STRUCT_END 00090 # define pb_packed 00091 #endif 00092 00093 /* Handly macro for suppressing unreferenced-parameter compiler warnings. */ 00094 #ifndef UNUSED 00095 #define UNUSED(x) (void)(x) 00096 #endif 00097 00098 /* Compile-time assertion, used for checking compatible compilation options. 00099 * If this fails on your compiler for some reason, use #define STATIC_ASSERT 00100 * to disable it. */ 00101 #ifndef STATIC_ASSERT 00102 #define STATIC_ASSERT(COND,MSG) typedef char STATIC_ASSERT_MSG(MSG, __LINE__, __COUNTER__)[(COND)?1:-1]; 00103 #define STATIC_ASSERT_MSG(MSG, LINE, COUNTER) STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) 00104 #define STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) static_assertion_##MSG##LINE##COUNTER 00105 #endif 00106 00107 /* Number of required fields to keep track of. */ 00108 #ifndef PB_MAX_REQUIRED_FIELDS 00109 #define PB_MAX_REQUIRED_FIELDS 64 00110 #endif 00111 00112 #if PB_MAX_REQUIRED_FIELDS < 64 00113 #error You should not lower PB_MAX_REQUIRED_FIELDS from the default value (64). 00114 #endif 00115 00116 /* List of possible field types. These are used in the autogenerated code. 00117 * Least-significant 4 bits tell the scalar type 00118 * Most-significant 4 bits specify repeated/required/packed etc. 00119 */ 00120 00121 typedef uint8_t pb_type_t; 00122 00123 /**** Field data types ****/ 00124 00125 /* Numeric types */ 00126 #define PB_LTYPE_VARINT 0x00 /* int32, int64, enum, bool */ 00127 #define PB_LTYPE_UVARINT 0x01 /* uint32, uint64 */ 00128 #define PB_LTYPE_SVARINT 0x02 /* sint32, sint64 */ 00129 #define PB_LTYPE_FIXED32 0x03 /* fixed32, sfixed32, float */ 00130 #define PB_LTYPE_FIXED64 0x04 /* fixed64, sfixed64, double */ 00131 00132 /* Marker for last packable field type. */ 00133 #define PB_LTYPE_LAST_PACKABLE 0x04 00134 00135 /* Byte array with pre-allocated buffer. 00136 * data_size is the length of the allocated PB_BYTES_ARRAY structure. */ 00137 #define PB_LTYPE_BYTES 0x05 00138 00139 /* String with pre-allocated buffer. 00140 * data_size is the maximum length. */ 00141 #define PB_LTYPE_STRING 0x06 00142 00143 /* Submessage 00144 * submsg_fields is pointer to field descriptions */ 00145 #define PB_LTYPE_SUBMESSAGE 0x07 00146 00147 /* Extension pseudo-field 00148 * The field contains a pointer to pb_extension_t */ 00149 #define PB_LTYPE_EXTENSION 0x08 00150 00151 /* Number of declared LTYPES */ 00152 #define PB_LTYPES_COUNT 9 00153 #define PB_LTYPE_MASK 0x0F 00154 00155 /**** Field repetition rules ****/ 00156 00157 #define PB_HTYPE_REQUIRED 0x00 00158 #define PB_HTYPE_OPTIONAL 0x10 00159 #define PB_HTYPE_REPEATED 0x20 00160 #define PB_HTYPE_MASK 0x30 00161 00162 /**** Field allocation types ****/ 00163 00164 #define PB_ATYPE_STATIC 0x00 00165 #define PB_ATYPE_POINTER 0x80 00166 #define PB_ATYPE_CALLBACK 0x40 00167 #define PB_ATYPE_MASK 0xC0 00168 00169 #define PB_ATYPE(x) ((x) & PB_ATYPE_MASK) 00170 #define PB_HTYPE(x) ((x) & PB_HTYPE_MASK) 00171 #define PB_LTYPE(x) ((x) & PB_LTYPE_MASK) 00172 00173 /* Data type used for storing sizes of struct fields 00174 * and array counts. 00175 */ 00176 #if defined(PB_FIELD_32BIT) 00177 typedef uint32_t pb_size_t; 00178 typedef int32_t pb_ssize_t; 00179 #elif defined(PB_FIELD_16BIT) 00180 typedef uint16_t pb_size_t; 00181 typedef int16_t pb_ssize_t; 00182 #else 00183 typedef uint8_t pb_size_t; 00184 typedef int8_t pb_ssize_t; 00185 #endif 00186 00187 /* This structure is used in auto-generated constants 00188 * to specify struct fields. 00189 * You can change field sizes if you need structures 00190 * larger than 256 bytes or field tags larger than 256. 00191 * The compiler should complain if your .proto has such 00192 * structures. Fix that by defining PB_FIELD_16BIT or 00193 * PB_FIELD_32BIT. 00194 */ 00195 PB_PACKED_STRUCT_START 00196 typedef struct _pb_field_t pb_field_t; 00197 struct _pb_field_t { 00198 pb_size_t tag; 00199 pb_type_t type; 00200 pb_size_t data_offset; /* Offset of field data, relative to previous field. */ 00201 pb_ssize_t size_offset; /* Offset of array size or has-boolean, relative to data */ 00202 pb_size_t data_size; /* Data size in bytes for a single item */ 00203 pb_size_t array_size; /* Maximum number of entries in array */ 00204 00205 /* Field definitions for submessage 00206 * OR default value for all other non-array, non-callback types 00207 * If null, then field will zeroed. */ 00208 const void *ptr; 00209 } pb_packed; 00210 PB_PACKED_STRUCT_END 00211 00212 /* Make sure that the standard integer types are of the expected sizes. 00213 * All kinds of things may break otherwise.. atleast all fixed* types. */ 00214 STATIC_ASSERT(sizeof(int8_t) == 1, INT8_T_WRONG_SIZE) 00215 STATIC_ASSERT(sizeof(uint8_t) == 1, UINT8_T_WRONG_SIZE) 00216 STATIC_ASSERT(sizeof(int16_t) == 2, INT16_T_WRONG_SIZE) 00217 STATIC_ASSERT(sizeof(uint16_t) == 2, UINT16_T_WRONG_SIZE) 00218 STATIC_ASSERT(sizeof(int32_t) == 4, INT32_T_WRONG_SIZE) 00219 STATIC_ASSERT(sizeof(uint32_t) == 4, UINT32_T_WRONG_SIZE) 00220 STATIC_ASSERT(sizeof(int64_t) == 8, INT64_T_WRONG_SIZE) 00221 STATIC_ASSERT(sizeof(uint64_t) == 8, UINT64_T_WRONG_SIZE) 00222 00223 /* This structure is used for 'bytes' arrays. 00224 * It has the number of bytes in the beginning, and after that an array. 00225 * Note that actual structs used will have a different length of bytes array. 00226 */ 00227 struct _pb_bytes_array_t { 00228 size_t size; 00229 uint8_t bytes[1]; 00230 }; 00231 typedef struct _pb_bytes_array_t pb_bytes_array_t; 00232 00233 /* Same, except for pointer-type fields. There is no need to variable struct 00234 * length in this case. 00235 */ 00236 struct _pb_bytes_ptr_t { 00237 size_t size; 00238 uint8_t *bytes; 00239 }; 00240 typedef struct _pb_bytes_ptr_t pb_bytes_ptr_t; 00241 00242 /* This structure is used for giving the callback function. 00243 * It is stored in the message structure and filled in by the method that 00244 * calls pb_decode. 00245 * 00246 * The decoding callback will be given a limited-length stream 00247 * If the wire type was string, the length is the length of the string. 00248 * If the wire type was a varint/fixed32/fixed64, the length is the length 00249 * of the actual value. 00250 * The function may be called multiple times (especially for repeated types, 00251 * but also otherwise if the message happens to contain the field multiple 00252 * times.) 00253 * 00254 * The encoding callback will receive the actual output stream. 00255 * It should write all the data in one call, including the field tag and 00256 * wire type. It can write multiple fields. 00257 * 00258 * The callback can be null if you want to skip a field. 00259 */ 00260 typedef struct _pb_istream_t pb_istream_t; 00261 typedef struct _pb_ostream_t pb_ostream_t; 00262 typedef struct _pb_callback_t pb_callback_t; 00263 struct _pb_callback_t { 00264 #ifdef PB_OLD_CALLBACK_STYLE 00265 /* Deprecated since nanopb-0.2.1 */ 00266 union { 00267 bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void *arg); 00268 bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, const void *arg); 00269 } funcs; 00270 #else 00271 /* New function signature, which allows modifying arg contents in callback. */ 00272 union { 00273 bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg); 00274 bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg); 00275 } funcs; 00276 #endif 00277 00278 /* Free arg for use by callback */ 00279 void *arg; 00280 }; 00281 00282 /* Wire types. Library user needs these only in encoder callbacks. */ 00283 typedef enum { 00284 PB_WT_VARINT = 0, 00285 PB_WT_64BIT = 1, 00286 PB_WT_STRING = 2, 00287 PB_WT_32BIT = 5 00288 } pb_wire_type_t; 00289 00290 /* Structure for defining the handling of unknown/extension fields. 00291 * Usually the pb_extension_type_t structure is automatically generated, 00292 * while the pb_extension_t structure is created by the user. However, 00293 * if you want to catch all unknown fields, you can also create a custom 00294 * pb_extension_type_t with your own callback. 00295 */ 00296 typedef struct _pb_extension_type_t pb_extension_type_t; 00297 typedef struct _pb_extension_t pb_extension_t; 00298 struct _pb_extension_type_t { 00299 /* Called for each unknown field in the message. 00300 * If you handle the field, read off all of its data and return true. 00301 * If you do not handle the field, do not read anything and return true. 00302 * If you run into an error, return false. 00303 * Set to NULL for default handler. 00304 */ 00305 bool (*decode)(pb_istream_t *stream, pb_extension_t *extension, 00306 uint32_t tag, pb_wire_type_t wire_type); 00307 00308 /* Called once after all regular fields have been encoded. 00309 * If you have something to write, do so and return true. 00310 * If you do not have anything to write, just return true. 00311 * If you run into an error, return false. 00312 * Set to NULL for default handler. 00313 */ 00314 bool (*encode)(pb_ostream_t *stream, const pb_extension_t *extension); 00315 00316 /* Free field for use by the callback. */ 00317 const void *arg; 00318 }; 00319 00320 struct _pb_extension_t { 00321 /* Type describing the extension field. Usually you'll initialize 00322 * this to a pointer to the automatically generated structure. */ 00323 const pb_extension_type_t *type; 00324 00325 /* Destination for the decoded data. This must match the datatype 00326 * of the extension field. */ 00327 void *dest; 00328 00329 /* Pointer to the next extension handler, or NULL. 00330 * If this extension does not match a field, the next handler is 00331 * automatically called. */ 00332 pb_extension_t *next; 00333 }; 00334 00335 /* These macros are used to declare pb_field_t's in the constant array. */ 00336 /* Size of a structure member, in bytes. */ 00337 #define pb_membersize(st, m) (sizeof ((st*)0)->m) 00338 /* Number of entries in an array. */ 00339 #define pb_arraysize(st, m) (pb_membersize(st, m) / pb_membersize(st, m[0])) 00340 /* Delta from start of one member to the start of another member. */ 00341 #define pb_delta(st, m1, m2) ((int)offsetof(st, m1) - (int)offsetof(st, m2)) 00342 /* Marks the end of the field list */ 00343 #define PB_LAST_FIELD {0,(pb_type_t) 0,0,0,0,0,0} 00344 00345 /* Macros for filling in the data_offset field */ 00346 /* data_offset for first field in a message */ 00347 #define PB_DATAOFFSET_FIRST(st, m1, m2) (offsetof(st, m1)) 00348 /* data_offset for subsequent fields */ 00349 #define PB_DATAOFFSET_OTHER(st, m1, m2) (offsetof(st, m1) - offsetof(st, m2) - pb_membersize(st, m2)) 00350 /* Choose first/other based on m1 == m2 (deprecated, remains for backwards compatibility) */ 00351 #define PB_DATAOFFSET_CHOOSE(st, m1, m2) (int)(offsetof(st, m1) == offsetof(st, m2) \ 00352 ? PB_DATAOFFSET_FIRST(st, m1, m2) \ 00353 : PB_DATAOFFSET_OTHER(st, m1, m2)) 00354 00355 /* Required fields are the simplest. They just have delta (padding) from 00356 * previous field end, and the size of the field. Pointer is used for 00357 * submessages and default values. 00358 */ 00359 #define PB_REQUIRED_STATIC(tag, st, m, fd, ltype, ptr) \ 00360 {tag, PB_ATYPE_STATIC | PB_HTYPE_REQUIRED | ltype, \ 00361 fd, 0, pb_membersize(st, m), 0, ptr} 00362 00363 /* Optional fields add the delta to the has_ variable. */ 00364 #define PB_OPTIONAL_STATIC(tag, st, m, fd, ltype, ptr) \ 00365 {tag, PB_ATYPE_STATIC | PB_HTYPE_OPTIONAL | ltype, \ 00366 fd, \ 00367 pb_delta(st, has_ ## m, m), \ 00368 pb_membersize(st, m), 0, ptr} 00369 00370 /* Repeated fields have a _count field and also the maximum number of entries. */ 00371 #define PB_REPEATED_STATIC(tag, st, m, fd, ltype, ptr) \ 00372 {tag, PB_ATYPE_STATIC | PB_HTYPE_REPEATED | ltype, \ 00373 fd, \ 00374 pb_delta(st, m ## _count, m), \ 00375 pb_membersize(st, m[0]), \ 00376 pb_arraysize(st, m), ptr} 00377 00378 /* Allocated fields carry the size of the actual data, not the pointer */ 00379 #define PB_REQUIRED_POINTER(tag, st, m, fd, ltype, ptr) \ 00380 {tag, PB_ATYPE_POINTER | PB_HTYPE_REQUIRED | ltype, \ 00381 fd, 0, pb_membersize(st, m[0]), 0, ptr} 00382 00383 /* Optional fields don't need a has_ variable, as information would be redundant */ 00384 #define PB_OPTIONAL_POINTER(tag, st, m, fd, ltype, ptr) \ 00385 {tag, PB_ATYPE_POINTER | PB_HTYPE_OPTIONAL | ltype, \ 00386 fd, 0, pb_membersize(st, m[0]), 0, ptr} 00387 00388 /* Repeated fields have a _count field and a pointer to array of pointers */ 00389 #define PB_REPEATED_POINTER(tag, st, m, fd, ltype, ptr) \ 00390 {tag, PB_ATYPE_POINTER | PB_HTYPE_REPEATED | ltype, \ 00391 fd, pb_delta(st, m ## _count, m), \ 00392 pb_membersize(st, m[0]), 0, ptr} 00393 00394 /* Callbacks are much like required fields except with special datatype. */ 00395 #define PB_REQUIRED_CALLBACK(tag, st, m, fd, ltype, ptr) \ 00396 {tag, PB_ATYPE_CALLBACK | PB_HTYPE_REQUIRED | ltype, \ 00397 fd, 0, pb_membersize(st, m), 0, ptr} 00398 00399 #define PB_OPTIONAL_CALLBACK(tag, st, m, fd, ltype, ptr) \ 00400 {tag, PB_ATYPE_CALLBACK | PB_HTYPE_OPTIONAL | ltype, \ 00401 fd, 0, pb_membersize(st, m), 0, ptr} 00402 00403 #define PB_REPEATED_CALLBACK(tag, st, m, fd, ltype, ptr) \ 00404 {tag, PB_ATYPE_CALLBACK | PB_HTYPE_REPEATED | ltype, \ 00405 fd, 0, pb_membersize(st, m), 0, ptr} 00406 00407 /* Optional extensions don't have the has_ field, as that would be redundant. */ 00408 #define PB_OPTEXT_STATIC(tag, st, m, fd, ltype, ptr) \ 00409 {tag, PB_ATYPE_STATIC | PB_HTYPE_OPTIONAL | ltype, \ 00410 0, \ 00411 0, \ 00412 pb_membersize(st, m), 0, ptr} 00413 00414 #define PB_OPTEXT_CALLBACK(tag, st, m, fd, ltype, ptr) \ 00415 {tag, PB_ATYPE_CALLBACK | PB_HTYPE_OPTIONAL | ltype, \ 00416 0, 0, pb_membersize(st, m), 0, ptr} 00417 00418 /* The mapping from protobuf types to LTYPEs is done using these macros. */ 00419 #define PB_LTYPE_MAP_BOOL PB_LTYPE_VARINT 00420 #define PB_LTYPE_MAP_BYTES PB_LTYPE_BYTES 00421 #define PB_LTYPE_MAP_DOUBLE PB_LTYPE_FIXED64 00422 #define PB_LTYPE_MAP_ENUM PB_LTYPE_VARINT 00423 #define PB_LTYPE_MAP_FIXED32 PB_LTYPE_FIXED32 00424 #define PB_LTYPE_MAP_FIXED64 PB_LTYPE_FIXED64 00425 #define PB_LTYPE_MAP_FLOAT PB_LTYPE_FIXED32 00426 #define PB_LTYPE_MAP_INT32 PB_LTYPE_VARINT 00427 #define PB_LTYPE_MAP_INT64 PB_LTYPE_VARINT 00428 #define PB_LTYPE_MAP_MESSAGE PB_LTYPE_SUBMESSAGE 00429 #define PB_LTYPE_MAP_SFIXED32 PB_LTYPE_FIXED32 00430 #define PB_LTYPE_MAP_SFIXED64 PB_LTYPE_FIXED64 00431 #define PB_LTYPE_MAP_SINT32 PB_LTYPE_SVARINT 00432 #define PB_LTYPE_MAP_SINT64 PB_LTYPE_SVARINT 00433 #define PB_LTYPE_MAP_STRING PB_LTYPE_STRING 00434 #define PB_LTYPE_MAP_UINT32 PB_LTYPE_UVARINT 00435 #define PB_LTYPE_MAP_UINT64 PB_LTYPE_UVARINT 00436 #define PB_LTYPE_MAP_EXTENSION PB_LTYPE_EXTENSION 00437 00438 /* This is the actual macro used in field descriptions. 00439 * It takes these arguments: 00440 * - Field tag number 00441 * - Field type: BOOL, BYTES, DOUBLE, ENUM, FIXED32, FIXED64, 00442 * FLOAT, INT32, INT64, MESSAGE, SFIXED32, SFIXED64 00443 * SINT32, SINT64, STRING, UINT32, UINT64 or EXTENSION 00444 * - Field rules: REQUIRED, OPTIONAL or REPEATED 00445 * - Allocation: STATIC or CALLBACK 00446 * - Message name 00447 * - Field name 00448 * - Previous field name (or field name again for first field) 00449 * - Pointer to default value or submsg fields. 00450 */ 00451 00452 #define PB_FIELD(tag, type, rules, allocation, message, field, prevfield, ptr) \ 00453 PB_ ## rules ## _ ## allocation(tag, message, field, \ 00454 PB_DATAOFFSET_CHOOSE(message, field, prevfield), \ 00455 PB_LTYPE_MAP_ ## type, ptr) 00456 00457 /* This is a new version of the macro used by nanopb generator from 00458 * version 0.2.3 onwards. It avoids the use of a ternary expression in 00459 * the initialization, which confused some compilers. 00460 * 00461 * - Placement: FIRST or OTHER, depending on if this is the first field in structure. 00462 * 00463 */ 00464 #define PB_FIELD2(tag, type, rules, allocation, placement, message, field, prevfield, ptr) \ 00465 PB_ ## rules ## _ ## allocation(tag, message, field, \ 00466 PB_DATAOFFSET_ ## placement(message, field, prevfield), \ 00467 PB_LTYPE_MAP_ ## type, ptr) 00468 00469 00470 /* These macros are used for giving out error messages. 00471 * They are mostly a debugging aid; the main error information 00472 * is the true/false return value from functions. 00473 * Some code space can be saved by disabling the error 00474 * messages if not used. 00475 */ 00476 #ifdef PB_NO_ERRMSG 00477 #define PB_RETURN_ERROR(stream,msg) return false 00478 #define PB_GET_ERROR(stream) "(errmsg disabled)" 00479 #else 00480 #define PB_RETURN_ERROR(stream,msg) \ 00481 do {\ 00482 if ((stream)->errmsg == NULL) \ 00483 (stream)->errmsg = (msg); \ 00484 return false; \ 00485 } while(0) 00486 #define PB_GET_ERROR(stream) ((stream)->errmsg ? (stream)->errmsg : "(none)") 00487 #endif 00488 00489 #endif 00490
Generated on Thu Jul 14 2022 19:55:28 by
1.7.2