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
|
=================
Memory containers
=================
The HXmc series of functions provide scripting-like semantics for strings,
especially automatically resizing the buffer on demand. They can also be used
to store a binary block of data together with its length. (Hence the name: mc =
memory container.)
The benefit of using the HXmc functions is that one does not have to
meticulously watch buffer and string sizes anymore.
Improvement of string safety over time:
.. code-block:: c
/* Step 1 */
char buf[long_enough] = "helloworld";
if (strlen(buf) + strlen(".txt") < sizeof(buf))
strcat(s, ".txt"); /* may go over end of buffer */
/* Step 2 */
char buf[long_enough] = "helloworld";
strlcat(s, ".txt", sizeof(buf)); /* may truncate */
/* Step 3 */
hxmc_t *buf = HXmc_strinit("helloworld");
HXmc_strcat(&s, ".txt");
This makes it quite similar to the string operations (and append seems to be
the most commonly used one to me) supported in scripting languages that also do
without a size argument. The essential part of such memory containers is that
their internal (hidden) metadata structure contains the length of the memory
block in the container. For binary data this may be the norm, but for C-style
strings, the stored and auto-updated length field serves as an accelerator
cache. For more details, see ``HXmc_length``.
Of course, the automatic management of memory comes with a bit of overhead as
the string expands beyond its preallocated region. Such may be mitigated by
doing explicit (re)sizing.
Structural overview
===================
HXmc functions do not actually return a pointer to the memory container (e.g.
struct) itself, but a pointer to the data block. Conversely, input parameters
to HXmc functions will be the data block pointer. It is of type ``hxmc_t *``,
which is typedef'ed to ``char *`` and therefore has properties of a char
pointer. Pointer arithmetic is thus supported. It also means you can just pass
it to functions that take a ``char *`` without having to do a member access
like ``s.c_str()`` in C++. The drawback is that many functions operating on the
memory container need a ``hxmc_t **`` (a level-two indirection), because not
only does the memory block move, but also the memory container itself. This is
due to the implementation of the container metadata which immediately and
always precedes the writable memory block.
HXmc ensures that the data block is terminated by the \0 byte (unless you trash
it), so you do not have to, and of course, to be on the safe side. But, the
automatic \0 byte is not part of the region allocated by the user. That is,
when one uses the classic approach with ``malloc(4096)``, the user will have
control of 4096 bytes and has to stuff the \0 byte in there somehow on his own;
for strings this means the maximum string length is 4095. Requesting space for
a 4096-byte sized HXmc container gives you the possibility to use all 4096
bytes for the string, because HXmc provides a \0 byte.
By the way, ``hxmc_t`` is the only typedef in this entire library, to
distinguish it from regular ``char *`` that does not have a backing memory
cointainer.
Constructors, destructors
=========================
.. code-block:: c
#include <libHX/string.h>
hxmc_t *HXmc_strinit(const char *s);
hxmc_t *HXmc_meminit(const void *ptr, size_t size);
void HXmc_free(hxmc_t *s);
void HXmc_zvecfree(hxmc_t **s);
``HXmc_strinit``
Creates a new ``hxmc_t`` object from the supplied string and returns
it.
``HXmc_meminit``
Creates a new ``hxmc_t`` object from the supplied memory buffer of the
given size and returns it. ``HXmc_meminit(NULL, len)`` may be used to
obtain an empty container with a preallocated region of len bytes (zero
is accepted for ``len``).
``HXmc_free``
Frees the hxmc object.
``HXmc_zvecfree``
Frees all hxmc objects in the ``NULL``-terminated array, and finally
frees the array itself, similar to ``HX_zvecfree``.
Data manipulation
=================
Binary-based
------------
.. code-block:: c
hxmc_t *HXmc_trunc(hxmc_t **mc, size_t len);HXmc_trunc
hxmc_t *HXmc_setlen(hxmc_t **mc, size_t len);HXmc_setlen
hxmc_t *HXmc_memcpy(hxmc_t **mc, const void *ptr, size_t len);
hxmc_t *HXmc_memcat(hxmc_t **mc, const void *ptr, size_t len);
hxmc_t *HXmc_mempcat(hxmc_t **mc, const void *ptr, size_t len);
hxmc_t *HXmc_memins(hxmc_t **mc, size_t pos, const void *ptr, size_t len);
hxmc_t *HXmc_memdel(hxmc_t **mc, size_t pos, size_t len);
When ``ptr`` is ``NULL``, each call behaves as if ``len`` would be ``zero``.
Specifically, no undefined behavior will result of the use of ``NULL``.
``HXmc_trunc``
Truncates the container's data to ``len`` size. If ``len`` is greater
than the current data size of the container, the length is in fact not
updated, but a reallocation may be triggered, which can be used to do
explicit allocation.
``HXmc_setlen``
Sets the data length, doing a reallocation of the memory container if
needed. The newly available bytes are uninitialized. Make use of this
function when letting 3rd party functions write to the buffer, but it
should not be used with ``HXmc_str*``.
``HXmc_memcpy``
Truncates the container's data and copies ``len`` bytes from the memory
area pointed to by ``ptr`` to the container.
``HXmc_memcat``
Concatenates (appends) ``len`` bytes from the memory area pointed to by
``ptr`` to the container's data.
``HXmc_mempcat``
Prepends ``len`` bytes from the memory area pointed to by ``ptr`` to
the container's data.
``HXmc_memins``
Prepends ``len`` bytes from the memory area pointed to by ``ptr`` to
the ``pos``'th byte of the container's data.
``HXmc_memdel``
Deletes ``len`` bytes from the container beginning at position ``pos``.
In case of a memory allocation failure, the ``HXmc_*`` functions will return
``NULL``.
String-based
------------
The string-based functions correspond to their binary-based equivalents with a
len argument of strlen(s).
.. code-block:: c
hxmc_t *HXmc_strcpy(hxmc_t **mc, const char *s);
hxmc_t *HXmc_strcat(hxmc_t **mc, const char *s);
hxmc_t *HXmc_strpcat(hxmc_t **mc, const char *s);
hxmc_t *HXmc_strins(hxmc_t **mc, size_t pos, const char *s);
``HXmc_strcpy``
Copies the string pointed to by ``s`` into the memory container given
by ``mc``. If ``mc`` is ``NULL``, the memory container will be
deallocated, that is, ``*mc`` becomes ``NULL``.
From auxiliary sources
----------------------
.. code-block:: c
hxmc_t *HX_getl(hxmc_t **mc, FILE *fp);HX_getl
``HX_getl``
Reads the next line from ``fp`` and store the result in the container.
Returns ``NULL`` on error, or when end of file occurs while no
characters have been read.
Container properties
--------------------
.. code-block:: c
size_t HXmc_length(const hxmc_t **mc);
``HXmc_length``
Returns the length of the memory container. This is not always equal to
the actual string length. For example, if ``HX_chomp`` was used on an
MC-backed string, ``strlen`` will return less than ``HXmc_length`` if
newline control characters (``\r`` and ``\n``) were removed.
|