Branch data Line data Source code
1 : : #include "mem.h"
2 : : #include "mem_internal.h"
3 : :
4 : : /**
5 : : * @brief Finalize a direct string-buffer write by caching the visible length
6 : : *
7 : : * Use this helper after low-level code has written string payload directly into
8 : : * a descriptor's existing writable buffer. The descriptor must already be in
9 : : * string mode. On return the descriptor is always zero-terminated at
10 : : * @p written_length; @p flags only selects how the terminator is produced.
11 : : * With @ref WRITE_TERMINATOR_ALWAYS the helper always writes the zero
12 : : * terminator unconditionally. With @ref WRITE_TERMINATOR_IF_MISSING the
13 : : * helper first inspects the element at @p written_length and writes a
14 : : * terminator only when one is not already present. Either way
15 : : * @ref memory::string_length is then updated to @p written_length
16 : : *
17 : : * Small example:
18 : : * @code
19 : : * m_create(char,title,MEMORY_STRING);
20 : : * const char draft[] = "draft";
21 : : *
22 : : * if((TRIUMPH & m_resize(title,sizeof(draft))) == 0) { return FAILURE; }
23 : : *
24 : : * char *title_view = m_data(char,title);
25 : : * if(title_view == NULL) { return FAILURE; }
26 : : *
27 : : * memcpy(title_view,draft,sizeof(draft));
28 : : *
29 : : * if((TRIUMPH & m_finalize_string(title,sizeof(draft) - 1U)) == 0) { return FAILURE; }
30 : : * @endcode
31 : : *
32 : : * @param destination String descriptor whose direct write should become visible
33 : : * @param written_length Visible string length measured in whole elements
34 : : * @param flags Whether the helper should write the zero terminator unconditionally or only when it is missing
35 : : * @return `SUCCESS` on success; `FAILURE` otherwise
36 : : */
37 : 25230 : Return mem_finalize_string(
38 : : memory *destination,
39 : : size_t written_length,
40 : : TERMINATOR_WRITE_MODE flags)
41 : : {
42 : : /* Status returned by this function through provide()
43 : : Default value assumes successful completion */
44 : 25230 : Return status = SUCCESS;
45 : :
46 : : /* Byte offset of the terminator slot within the data buffer
47 : : Computed by mem_guarded_byte_size as written_length * single_element_size */
48 : 25230 : size_t terminator_offset = 0;
49 : :
50 : : /* Total bytes needed from the start of the buffer through the terminator
51 : : Computed as terminator_offset + single_element_size */
52 : 25230 : size_t required_bytes = 0;
53 : :
54 [ - + ]: 25230 : if(destination == NULL)
55 : : {
56 : 0 : report("Memory management; String write finalization destination must be non-NULL");
57 : 0 : status = FAILURE;
58 : : }
59 : :
60 [ + - - + ]: 25230 : if((TRIUMPH & status) && destination->single_element_size == 0)
61 : : {
62 : 0 : report("Memory management; String write finalization destination element size is zero");
63 : 0 : status = FAILURE;
64 : : }
65 : :
66 [ + - + - : 25230 : if((TRIUMPH & status) && destination->length > 0 && destination->data == NULL)
- + ]
67 : : {
68 : 0 : report("Memory management; Descriptor has non-zero length with NULL data pointer");
69 : 0 : status = FAILURE;
70 : : }
71 : :
72 [ + - ]: 25230 : if((TRIUMPH & status) &&
73 [ + - ]: 25230 : destination->actually_allocated_bytes > 0 &&
74 [ - + ]: 25230 : destination->data == NULL)
75 : : {
76 : 0 : report("Memory management; Descriptor has reserved bytes with NULL data pointer during string write finalization");
77 : 0 : status = FAILURE;
78 : : }
79 : :
80 [ + - + + ]: 25230 : if((TRIUMPH & status) && destination->is_string == false)
81 : : {
82 : 1 : report("Memory management; String write finalization requires a string descriptor");
83 : 1 : status = FAILURE;
84 : : }
85 : :
86 [ + + + + ]: 25230 : if((TRIUMPH & status) &&
87 [ - + ]: 1701 : flags != WRITE_TERMINATOR_IF_MISSING &&
88 : : flags != WRITE_TERMINATOR_ALWAYS)
89 : : {
90 : 0 : report("Memory management; Unknown string write finalization flag");
91 : 0 : status = FAILURE;
92 : : }
93 : :
94 [ + + ]: 25230 : if(TRIUMPH & status)
95 : : {
96 [ - + ]: 25229 : if(destination->length == 0)
97 : : {
98 : 0 : report("Memory management; String write finalization requires reserved space for the visible string and terminator");
99 : 0 : status = FAILURE;
100 [ - + ]: 25229 : } else if(written_length >= destination->length){
101 : 0 : report("Memory management; String write finalization visible length exceeds descriptor capacity");
102 : 0 : status = FAILURE;
103 : : }
104 : : }
105 : :
106 [ + + ]: 25230 : if(TRIUMPH & status)
107 : : {
108 : 25229 : run(mem_guarded_byte_size(destination,written_length,&terminator_offset));
109 : 25229 : run(mem_guarded_add(terminator_offset,destination->single_element_size,&required_bytes));
110 : :
111 [ + - - + ]: 25229 : if((TRIUMPH & status) && required_bytes > destination->actually_allocated_bytes)
112 : : {
113 : 0 : report("Memory management; String write finalization exceeds reserved capacity");
114 : 0 : status = FAILURE;
115 : : }
116 : : }
117 : :
118 [ + + ]: 25230 : if(TRIUMPH & status)
119 : : {
120 [ + + ]: 25229 : if(flags == WRITE_TERMINATOR_ALWAYS)
121 : : {
122 : 1701 : run(mem_write_zero_terminator(destination,written_length));
123 : : } else {
124 : : /* Read-only view of the raw buffer used to inspect
125 : : whether a zero terminator already exists at the expected slot */
126 : 23528 : const unsigned char *destination_data_view = (const unsigned char *)destination->data;
127 : :
128 [ + + ]: 23528 : if(mem_is_zero_element(destination_data_view + terminator_offset,destination->single_element_size) == false)
129 : : {
130 : 2 : run(mem_write_zero_terminator(destination,written_length));
131 : :
132 [ + - ]: 2 : if(TRIUMPH & status)
133 : : {
134 : 2 : telemetry_finalize_string_terminator_written_when_missing();
135 : : }
136 : : } else {
137 : 23526 : telemetry_finalize_string_terminator_already_present();
138 : : }
139 : : }
140 : : }
141 : :
142 [ + + ]: 25230 : if(TRIUMPH & status)
143 : : {
144 : 25229 : destination->string_length = written_length;
145 : : }
146 : :
147 : 25230 : provide(status);
148 : : }
|