diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index a39e4cea..05011114 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -1,17 +1,8 @@ # zcbor v. 0.9.99 -* `zcbor_simple_*()` functions have been removed to avoid confusion about their use. - They are still in the C file because they are used by other functions. - Instead, use the specific functions for the currently supported simple values, i.e. - `zcbor_bool_*()`, `zcbor_nil_*()`, and `zcbor_undefined_*()`. - If a removed variant is strictly needed, add your own forward declaration in your code. - -* Code generation naming: - - * More C keywords are now capitalized to avoid naming collision. - You might have to capitalize some instances if your code was generated to have those names. - - * A fix was made to the naming of bstr elements with a .size specifier, which might mean that these elements change name in your code when you regenerate. +* `zcbor_new_state` and `zcbor_entry_function` are now deprecated in favor of `zcbor_state_init`and `zcbor_entry_func` respectively. + They still work as before, but will be removed in a few more releases. + The new functions take a struct as parameter to allow parameters to be added in the future without breaking compatibility. # zcbor v. 0.9.0 diff --git a/include/zcbor_common.h b/include/zcbor_common.h index d9c12bfb..00f36ff1 100644 --- a/include/zcbor_common.h +++ b/include/zcbor_common.h @@ -96,11 +96,13 @@ union { processed. */ }; uint8_t const *payload_bak; /**< Temporary backup of payload. */ - size_t elem_count; /**< The current element is part of a LIST or a MAP, - and this keeps count of how many elements are - expected. This will be checked before processing - and decremented if the element is correctly - processed. */ + size_t elem_count; /**< Keeps count of how many elements are expected/have been + encoded. This count is decremented for each element when + decoding, or incremented for each element when encoding. + When decoding, this is checked before decoding any element, + and decoding will fail if elem_count is 0. + When encoding, this value is eventually used to populate + the header of a list or map, when in canonical mode. */ uint8_t const *payload_end; /**< The end of the payload. This will be checked against payload before processing each element. */ @@ -323,23 +325,47 @@ bool zcbor_union_elem_code(zcbor_state_t *state); */ bool zcbor_union_end_code(zcbor_state_t *state); +/** Initialization parameters for zcbor_state_init() and zcbor_entry_func(). + * + * Note that members may be added to this struct, so initialize it in a way where + * such members will be set to 0, either via a designated initializer (params = {...}), + * or by memseting the struct to 0 before assigning the members. + */ +struct zcbor_state_init_params { + zcbor_state_t *states; // Memory used for the state, backup states, constant state, and flags. + size_t n_states; // Size of the states array. + const uint8_t *payload; // Note that in encoding, payload will be modified despite the `const`. + size_t payload_len; // Length in bytes of the payload buffer + size_t payload_len_out; // Will be written with the actual length used (encoded/decoded) + size_t elem_count; // The initial elem_count, i.e. how many elements are expected. Should be 0 for encoding. + uint8_t *flags; // Memory used for flags, when using ZCBOR_MAP_SMART_SEARCH and unordered maps. Ignored when ZCBOR_MAP_SMART_SEARCH is not defined. + size_t flags_bytes; // Length in bytes of the flags buffer. Or, if non-zero while flags is NULL, how much memory will be taken from the states array for use as flags. +}; + /** Initialize a state with backups. * As long as n_states is more than 1, one of the states in the array is used * as a struct zcbor_state_constant object. * If there is no struct zcbor_state_constant (n_states == 1), error codes are * not available. * This means that you get a state with (n_states - 2) backups. - * payload, payload_len, elem_count, and elem_state are used to initialize the first state. - * The elem_state is only needed for unordered maps, when ZCBOR_MAP_SMART_SEARCH is enabled. + * payload, payload_len, elem_count, and flags are used to initialize the first state. + * The flags is only needed for unordered maps, when ZCBOR_MAP_SMART_SEARCH is enabled. * It is ignored otherwise. */ + +void zcbor_state_init(struct zcbor_state_init_params *params); + +/** [DEPRECATED] Old variant of zcbor_state_init, kept for backwards compatibility. */ void zcbor_new_state(zcbor_state_t *state_array, size_t n_states, const uint8_t *payload, size_t payload_len, size_t elem_count, - uint8_t *elem_state, size_t elem_state_bytes); + uint8_t *flags, size_t flags_bytes); /** Do boilerplate entry function procedure. * Initialize states, call function, and check the result. */ +int zcbor_entry_func(zcbor_decoder_t func, void *result, struct zcbor_state_init_params *params); + +/** [DEPRECATED] Old variant of zcbor_entry_func, kept for backwards compatibility. */ int zcbor_entry_function(const uint8_t *payload, size_t payload_len, void *result, size_t *payload_len_out, zcbor_state_t *state, zcbor_decoder_t func, size_t n_states, size_t elem_count); @@ -505,10 +531,10 @@ float zcbor_float16_to_32(uint16_t input); */ uint16_t zcbor_float32_to_16(float input); -#ifdef ZCBOR_MAP_SMART_SEARCH #define ZCBOR_ROUND_UP(x, align) (((x) + (align) - 1) / (align) * (align)) #define ZCBOR_BITS_PER_BYTE 8 +#ifdef ZCBOR_MAP_SMART_SEARCH /** Calculate the number of bytes needed to hold @p num_flags 1 bit flags */ static inline size_t zcbor_flags_to_bytes(size_t num_flags) @@ -522,10 +548,15 @@ static inline size_t zcbor_flags_to_bytes(size_t num_flags) (ZCBOR_ROUND_UP(num_flags, sizeof(zcbor_state_t) * ZCBOR_BITS_PER_BYTE) \ / (sizeof(zcbor_state_t) * ZCBOR_BITS_PER_BYTE)) +#define ZCBOR_BYTES_TO_STATES(num_bytes) \ + (ZCBOR_ROUND_UP(num_bytes, sizeof(zcbor_state_t)) / (sizeof(zcbor_state_t))) + +#define ZCBOR_BYTE_STATES(n_bytes) ZCBOR_BYTES_TO_STATES(n_bytes) #define ZCBOR_FLAG_STATES(n_flags) ZCBOR_FLAGS_TO_STATES(n_flags) #else -#define ZCBOR_FLAG_STATES(n_flags) 0 +#define ZCBOR_BYTE_STATES(n_bytes) (n_bytes * 0) +#define ZCBOR_FLAG_STATES(n_flags) (n_flags * 0) #endif size_t strnlen(const char *, size_t); diff --git a/include/zcbor_decode.h b/include/zcbor_decode.h index 90319d21..ff0567f4 100644 --- a/include/zcbor_decode.h +++ b/include/zcbor_decode.h @@ -23,7 +23,7 @@ extern "C" { */ -/** See @ref zcbor_new_state() */ +/** [DEPRECATED] See @ref zcbor_new_state() and @ref zcbor_state_init() */ void zcbor_new_decode_state(zcbor_state_t *state_array, size_t n_states, const uint8_t *payload, size_t payload_len, size_t elem_count, uint8_t *elem_state, size_t elem_state_bytes); @@ -35,19 +35,27 @@ void zcbor_new_decode_state(zcbor_state_t *state_array, size_t n_states, * * @param[in] name The name of the new state variable. * @param[in] num_backups The number of backup slots to keep in the state. - * @param[in] payload The payload to work on. + * @param[in] payload_buf The payload to work on. * @param[in] payload_size The size (in bytes) of @p payload. - * @param[in] elem_count The starting elem_count (typically 1). + * @param[in] init_elem_count The starting elem_count (typically 1). * @param[in] n_flags For use if ZCBOR_MAP_SMART_SEARCH is enabled, ignored otherwise. * The total number of unordered map search flags needed. * I.e. the largest number of elements expected in an unordered map, * including elements in nested unordered maps. */ -#define ZCBOR_STATE_D(name, num_backups, payload, payload_size, elem_count, n_flags) \ +#define ZCBOR_STATE_D(name, num_backups, payload_buf, payload_size, init_elem_count, n_flags) \ zcbor_state_t name[((num_backups) + 2 + ZCBOR_FLAG_STATES(n_flags))]; \ do { \ - zcbor_new_decode_state(name, ZCBOR_ARRAY_SIZE(name), payload, payload_size, elem_count, \ - (uint8_t *)&name[(num_backups) + 1], ZCBOR_FLAG_STATES(n_flags) * sizeof(zcbor_state_t)); \ + struct zcbor_state_init_params params = { \ + .states = name, \ + .n_states = ZCBOR_ARRAY_SIZE(name), \ + .payload = payload_buf, \ + .payload_len = payload_size, \ + .elem_count = init_elem_count, \ + .flags = NULL, \ + .flags_bytes = ZCBOR_ROUND_UP(n_flags, ZCBOR_BITS_PER_BYTE) / ZCBOR_BITS_PER_BYTE, \ + }; \ + zcbor_state_init(¶ms); \ } while(0) diff --git a/include/zcbor_encode.h b/include/zcbor_encode.h index 9bd9383c..555659fb 100644 --- a/include/zcbor_encode.h +++ b/include/zcbor_encode.h @@ -35,14 +35,23 @@ void zcbor_new_encode_state(zcbor_state_t *state_array, size_t n_states, * * @param[in] name The name of the new state variable. * @param[in] num_backups The number of backup slots to keep in the state. - * @param[in] payload The payload to work on. + * @param[in] payload_buf The payload to work on. * @param[in] payload_size The size (in bytes) of @p payload. - * @param[in] elem_count The starting elem_count (typically 1). + * @param[in] init_elem_count The starting elem_count (typically 1). */ -#define ZCBOR_STATE_E(name, num_backups, payload, payload_size, elem_count) \ +#define ZCBOR_STATE_E(name, num_backups, payload_buf, payload_size, init_elem_count) \ zcbor_state_t name[((num_backups) + 2)]; \ do { \ - zcbor_new_encode_state(name, ZCBOR_ARRAY_SIZE(name), payload, payload_size, elem_count); \ + struct zcbor_state_init_params params = { \ + .states = name, \ + .n_states = ZCBOR_ARRAY_SIZE(name), \ + .payload = payload_buf, \ + .payload_len = payload_size, \ + .elem_count = init_elem_count, \ + .flags = NULL, \ + .flags_bytes = 0, \ + }; \ + zcbor_state_init(¶ms); \ } while(0) diff --git a/samples/pet/src/pet_decode.c b/samples/pet/src/pet_decode.c index b060958f..4800f2c2 100644 --- a/samples/pet/src/pet_decode.c +++ b/samples/pet/src/pet_decode.c @@ -64,6 +64,19 @@ int cbor_decode_Pet( { zcbor_state_t states[4]; - return zcbor_entry_function(payload, payload_len, (void *)result, payload_len_out, states, - (zcbor_decoder_t *)decode_Pet, sizeof(states) / sizeof(zcbor_state_t), 1); + struct zcbor_state_init_params params = { + .states = states, + .n_states = sizeof(states) / sizeof(zcbor_state_t), + .payload = payload, + .payload_len = payload_len, + .elem_count = 1, + }; + + int ret = zcbor_entry_func((zcbor_decoder_t *)decode_Pet, (void *)result, ¶ms); + + if (payload_len_out != NULL && ret == ZCBOR_SUCCESS) { + *payload_len_out = params.payload_len_out; + } + + return ret; } diff --git a/samples/pet/src/pet_encode.c b/samples/pet/src/pet_encode.c index 4f390c29..dac72da7 100644 --- a/samples/pet/src/pet_encode.c +++ b/samples/pet/src/pet_encode.c @@ -58,6 +58,19 @@ int cbor_encode_Pet( { zcbor_state_t states[4]; - return zcbor_entry_function(payload, payload_len, (void *)input, payload_len_out, states, - (zcbor_decoder_t *)encode_Pet, sizeof(states) / sizeof(zcbor_state_t), 1); + struct zcbor_state_init_params params = { + .states = states, + .n_states = sizeof(states) / sizeof(zcbor_state_t), + .payload = payload, + .payload_len = payload_len, + .elem_count = 1, + }; + + int ret = zcbor_entry_func((zcbor_decoder_t *)encode_Pet, (void *)input, ¶ms); + + if (payload_len_out != NULL && ret == ZCBOR_SUCCESS) { + *payload_len_out = params.payload_len_out; + } + + return ret; } diff --git a/scripts/update_version.py b/scripts/update_version.py index 716dab50..48dd8b64 100644 --- a/scripts/update_version.py +++ b/scripts/update_version.py @@ -15,15 +15,23 @@ p_MIGRATION_GUIDE = Path(p_root, 'MIGRATION_GUIDE.md') p_common_h = Path(p_root, 'include', 'zcbor_common.h') -def update_relnotes(p_relnotes, version, include_date=True): +RELEASE_NOTES_boilerplate = """ +Any new bugs, requests, or missing features should be reported as [Github issues](https://github.com/NordicSemiconductor/zcbor/issues). + +## Improvements: + +## Bugfixes: +""" + + +def update_relnotes(p_relnotes, version, boilerplate="", include_date=True): relnotes_contents = p_relnotes.read_text(encoding="utf-8") relnotes_lines = relnotes_contents.splitlines() if version not in relnotes_lines[0]: new_date = f" ({datetime.today().strftime('%Y-%m-%d')})" if include_date else "" relnotes_new_header = f"# zcbor v. {version}{new_date}\n" if ".99" not in relnotes_lines[0]: - prev_entry = match(r"\A# zcbor.*?(?=# zcbor v.)", relnotes_contents, S).group(0) - relnotes_contents = prev_entry + relnotes_contents + relnotes_contents = relnotes_new_header + boilerplate + '\n\n' + relnotes_contents relnotes_contents = sub(r".*?\n", relnotes_new_header, relnotes_contents, count=1) p_relnotes.write_text(relnotes_contents, encoding="utf-8") @@ -36,7 +44,7 @@ def update_relnotes(p_relnotes, version, include_date=True): (major, minor, bugfix) = version.split('.') p_VERSION.write_text(version, encoding="utf-8") - update_relnotes(p_RELEASE_NOTES, version) + update_relnotes(p_RELEASE_NOTES, version, boilerplate=RELEASE_NOTES_boilerplate) update_relnotes(p_MIGRATION_GUIDE, version, include_date=False) p_common_h_contents = p_common_h.read_text(encoding="utf-8") common_h_new_contents = sub(r"(#define ZCBOR_VERSION_MAJOR )\d+", f"\\g<1>{major}", p_common_h_contents) diff --git a/src/zcbor_common.c b/src/zcbor_common.c index 5bf959e6..0d81a598 100644 --- a/src/zcbor_common.c +++ b/src/zcbor_common.c @@ -131,33 +131,41 @@ bool zcbor_union_end_code(zcbor_state_t *state) return true; } -void zcbor_new_state(zcbor_state_t *state_array, size_t n_states, - const uint8_t *payload, size_t payload_len, size_t elem_count, - uint8_t *flags, size_t flags_bytes) +void zcbor_state_init(struct zcbor_state_init_params *params) { - state_array[0].payload = payload; - state_array[0].payload_end = payload + payload_len; - state_array[0].elem_count = elem_count; + zcbor_state_t *state_array = params->states; + +#ifdef ZCBOR_MAP_SMART_SEARCH + if (params->flags == NULL) { + size_t flag_state_index = + params->n_states - ZCBOR_BYTE_STATES(params->flags_bytes); + params->flags = (uint8_t *)&state_array[flag_state_index]; + params->n_states = flag_state_index; + } +#endif + + state_array[0].payload = params->payload; + state_array[0].payload_end = params->payload + params->payload_len; + state_array[0].elem_count = params->elem_count; state_array[0].payload_moved = false; state_array[0].decode_state.indefinite_length_array = false; #ifdef ZCBOR_MAP_SMART_SEARCH - state_array[0].decode_state.map_search_elem_state = flags; + state_array[0].decode_state.map_search_elem_state = params->flags; state_array[0].decode_state.map_elem_count = 0; #else state_array[0].decode_state.map_elems_processed = 0; - (void)flags; - (void)flags_bytes; #endif state_array[0].constant_state = NULL; - if (n_states < 2) { + if (params->n_states < 2) { return; } /* Use the last state as a struct zcbor_state_constant object. */ - state_array[0].constant_state = (struct zcbor_state_constant *)&state_array[n_states - 1]; + state_array[0].constant_state = + (struct zcbor_state_constant *)&state_array[params->n_states - 1]; state_array[0].constant_state->backup_list = NULL; - state_array[0].constant_state->num_backups = n_states - 2; + state_array[0].constant_state->num_backups = params->n_states - 2; state_array[0].constant_state->current_backup = 0; state_array[0].constant_state->error = ZCBOR_SUCCESS; #ifdef ZCBOR_STOP_ON_ERROR @@ -166,13 +174,32 @@ void zcbor_new_state(zcbor_state_t *state_array, size_t n_states, state_array[0].constant_state->enforce_canonical = ZCBOR_ENFORCE_CANONICAL_DEFAULT; state_array[0].constant_state->manually_process_elem = ZCBOR_MANUALLY_PROCESS_ELEM_DEFAULT; #ifdef ZCBOR_MAP_SMART_SEARCH - state_array[0].constant_state->map_search_elem_state_end = flags + flags_bytes; + state_array[0].constant_state->map_search_elem_state_end = + params->flags + params->flags_bytes; #endif - if (n_states > 2) { + if (params->n_states > 2) { state_array[0].constant_state->backup_list = &state_array[1]; } } + +void zcbor_new_state(zcbor_state_t *state_array, size_t n_states, + const uint8_t *payload, size_t payload_len, size_t elem_count, + uint8_t *flags, size_t flags_bytes) +{ + struct zcbor_state_init_params params = { + .states = state_array, + .n_states = n_states, + .payload = payload, + .payload_len = payload_len, + .elem_count = elem_count, + .flags = flags, + .flags_bytes = flags_bytes, + }; + + zcbor_state_init(¶ms); +} + void zcbor_update_state(zcbor_state_t *state, const uint8_t *payload, size_t payload_len) { @@ -299,11 +326,13 @@ size_t zcbor_remaining_str_len(zcbor_state_t *state) } -int zcbor_entry_function(const uint8_t *payload, size_t payload_len, - void *result, size_t *payload_len_out, zcbor_state_t *state, zcbor_decoder_t func, - size_t n_states, size_t elem_count) +int zcbor_entry_func(zcbor_decoder_t func, void *result, struct zcbor_state_init_params *params) { - zcbor_new_state(state, n_states, payload, payload_len, elem_count, NULL, 0); + zcbor_state_t *state = params->states; + + zcbor_state_init(params); + + state->constant_state->manually_process_elem = true; bool ret = func(state, result); @@ -314,14 +343,35 @@ int zcbor_entry_function(const uint8_t *payload, size_t payload_len, return err; } - if (payload_len_out != NULL) { - *payload_len_out = MIN(payload_len, - (size_t)state[0].payload - (size_t)payload); - } + params->payload_len_out = MIN(params->payload_len, + (size_t)state[0].payload - (size_t)params->payload); return ZCBOR_SUCCESS; } +int zcbor_entry_function(const uint8_t *payload, size_t payload_len, + void *result, size_t *payload_len_out, zcbor_state_t *state, zcbor_decoder_t func, + size_t n_states, size_t elem_count) +{ + struct zcbor_state_init_params params = { + .states = state, + .n_states = n_states, + .payload = payload, + .payload_len = payload_len, + .elem_count = elem_count, + .flags_bytes = 0, + }; + + int ret = zcbor_entry_func(func, result, ¶ms); + + if (payload_len_out != NULL && ret == ZCBOR_SUCCESS) { + *payload_len_out = params.payload_len_out; + } + + return ret; +} + + /* Float16: */ #define F16_SIGN_OFFS 15 /* Bit offset of the sign bit. */ #define F16_EXPO_OFFS 10 /* Bit offset of the exponent. */ diff --git a/src/zcbor_decode.c b/src/zcbor_decode.c index 1ced3e0b..85407968 100644 --- a/src/zcbor_decode.c +++ b/src/zcbor_decode.c @@ -1607,5 +1607,15 @@ void zcbor_new_decode_state(zcbor_state_t *state_array, size_t n_states, const uint8_t *payload, size_t payload_len, size_t elem_count, uint8_t *flags, size_t flags_bytes) { - zcbor_new_state(state_array, n_states, payload, payload_len, elem_count, flags, flags_bytes); + struct zcbor_state_init_params params = { + .states = state_array, + .n_states = n_states, + .payload = payload, + .payload_len = payload_len, + .elem_count = elem_count, + .flags = flags, + .flags_bytes = flags_bytes, + }; + + zcbor_state_init(¶ms); } diff --git a/src/zcbor_encode.c b/src/zcbor_encode.c index 1240fd9b..5730a082 100644 --- a/src/zcbor_encode.c +++ b/src/zcbor_encode.c @@ -599,5 +599,15 @@ bool zcbor_multi_encode(const size_t num_encode, zcbor_encoder_t encoder, void zcbor_new_encode_state(zcbor_state_t *state_array, size_t n_states, uint8_t *payload, size_t payload_len, size_t elem_count) { - zcbor_new_state(state_array, n_states, payload, payload_len, elem_count, NULL, 0); + struct zcbor_state_init_params params = { + .states = state_array, + .n_states = n_states, + .payload = payload, + .payload_len = payload_len, + .elem_count = elem_count, + .flags = NULL, + .flags_bytes = 0, + }; + + zcbor_state_init(¶ms); } diff --git a/tests/unit/test1_unit_tests/src/main.c b/tests/unit/test1_unit_tests/src/main.c index ca7f7f6f..9c53c4df 100644 --- a/tests/unit/test1_unit_tests/src/main.c +++ b/tests/unit/test1_unit_tests/src/main.c @@ -1196,7 +1196,7 @@ ZTEST(zcbor_unit_tests, test_unordered_map) { uint8_t payload[200]; ZCBOR_STATE_E(state_e, 2, payload, sizeof(payload), 0); - ZCBOR_STATE_D(state_d, 2, payload, sizeof(payload), 10, 40); + ZCBOR_STATE_D(state_d, 2, payload, sizeof(payload), 10, 50); struct zcbor_string str_result1; struct zcbor_string str_result2; struct zcbor_string str_result3; @@ -1644,4 +1644,22 @@ ZTEST(zcbor_unit_tests, test_simple_value_len) } +ZTEST(zcbor_unit_tests, test_flag_states) +{ +#ifndef ZCBOR_MAP_SMART_SEARCH + printf("Skip on builds with no SMART_SEARCH.\n"); +#else + zassert_equal(2, zcbor_flags_to_bytes(15), NULL); + zassert_equal(2, zcbor_flags_to_bytes(16), NULL); + zassert_equal(3, zcbor_flags_to_bytes(17), NULL); + zassert_equal(2, ZCBOR_FLAG_STATES(sizeof(zcbor_state_t) * 2 * ZCBOR_BITS_PER_BYTE - 1), NULL); + zassert_equal(2, ZCBOR_FLAG_STATES(sizeof(zcbor_state_t) * 2 * ZCBOR_BITS_PER_BYTE), NULL); + zassert_equal(3, ZCBOR_FLAG_STATES(sizeof(zcbor_state_t) * 2 * ZCBOR_BITS_PER_BYTE + 1), NULL); + zassert_equal(2, ZCBOR_BYTE_STATES(sizeof(zcbor_state_t) * 2 - 1), NULL); + zassert_equal(2, ZCBOR_BYTE_STATES(sizeof(zcbor_state_t) * 2), NULL); + zassert_equal(3, ZCBOR_BYTE_STATES(sizeof(zcbor_state_t) * 2 + 1), NULL); +#endif +} + + ZTEST_SUITE(zcbor_unit_tests, NULL, NULL, NULL, NULL, NULL); diff --git a/zcbor/zcbor.py b/zcbor/zcbor.py index a49bc2ec..93856aff 100755 --- a/zcbor/zcbor.py +++ b/zcbor/zcbor.py @@ -2874,9 +2874,21 @@ def render_entry_function(self, xcoder, mode): {{ zcbor_state_t states[{xcoder.num_backups() + 2}]; - return zcbor_entry_function(payload, payload_len, (void *){func_arg}, payload_len_out, states, - (zcbor_decoder_t *){func_name}, sizeof(states) / sizeof(zcbor_state_t), { - xcoder.list_counts()[1]}); + struct zcbor_state_init_params params = {{ + .states = states, + .n_states = sizeof(states) / sizeof(zcbor_state_t), + .payload = payload, + .payload_len = payload_len, + .elem_count = {xcoder.list_counts()[1]}, + }}; + + int ret = zcbor_entry_func((zcbor_decoder_t *){func_name}, (void *){func_arg}, ¶ms); + + if (payload_len_out != NULL && ret == ZCBOR_SUCCESS) {{ + *payload_len_out = params.payload_len_out; + }} + + return ret; }}""" def render_file_header(self, line_prefix):