2 * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 /* A serialized object */
20 struct marshal_serialized
{
21 void *orig
; /* Original reference. Also enforce alignment. */
23 unsigned char object
[0];
26 struct marshal_info marshal_info__string
= {
27 .name
= "null string",
29 .pointers
= {{ .mi
= NULL
}},
31 struct marshal_info marshal_info__fstring
= {
32 .name
= "fixed string",
34 .pointers
= {{ .mi
= NULL
}},
37 /* List of already seen pointers */
39 TAILQ_ENTRY(ref
) next
;
42 TAILQ_HEAD(ref_l
, ref
);
44 /* Serialize the given object. */
46 _marshal_serialize(struct marshal_info
*mi
, void *unserialized
, void **input
,
47 int skip
, void *_refs
, int osize
)
49 /* Check if we have already serialized this one. */
50 struct ref_l
*refs
= _refs
;
52 refs
= calloc(1, sizeof(struct ref_l
));
54 LLOG_WARNX("unable to allocate memory for list of references");
60 TAILQ_FOREACH(cref
, refs
, next
) {
61 if (unserialized
== cref
->pointer
)
65 /* Handle special cases. */
67 if (!strcmp(mi
->name
, "null string"))
68 size
= strlen((char *)unserialized
) + 1;
69 else if (!strcmp(mi
->name
, "fixed string"))
72 /* Allocate serialized structure */
73 size_t len
= sizeof(struct marshal_serialized
) + (skip
?0:size
);
74 struct marshal_serialized
*serialized
= calloc(1, len
);
76 LLOG_WARNX("unable to allocate memory to serialize structure %s",
81 serialized
->orig
= unserialized
;
83 /* Append the new reference */
84 if (!(cref
= calloc(1, sizeof(struct ref
)))) {
85 LLOG_WARNX("unable to allocate memory for list of references");
90 cref
->pointer
= unserialized
;
91 TAILQ_INSERT_TAIL(refs
, cref
, next
);
93 /* First, serialize the main structure */
95 memcpy(serialized
->object
, unserialized
, size
);
97 /* Then, serialize inner structures */
98 struct marshal_subinfo
*current
;
99 for (current
= mi
->pointers
; current
->mi
; current
++) {
103 if (current
->kind
== pointer
) {
104 source
= *(void **)((unsigned char *)unserialized
+ current
->offset
);
105 if (source
== NULL
) continue;
107 source
= (void *)((unsigned char *)unserialized
+ current
->offset
);
108 memcpy(&osize
, (unsigned char*)unserialized
+ current
->offset2
, sizeof(int));
109 sublen
= _marshal_serialize(current
->mi
,
111 current
->kind
== substruct
, refs
, osize
);
113 LLOG_WARNX("unable to serialize substructure %s for %s",
114 current
->mi
->name
, mi
->name
);
118 if (sublen
== 0) continue; /* This was already serialized */
119 /* Append the result */
120 unsigned char *new = realloc(serialized
, len
+ sublen
);
122 LLOG_WARNX("unable to allocate more memory to serialize structure %s",
129 memcpy(new + len
, target
, sublen
);
132 serialized
= (struct marshal_serialized
*)new;
135 serialized
->size
= len
;
138 if (refs
&& !_refs
) {
139 struct ref
*cref
, *cref_next
;
140 for (cref
= TAILQ_FIRST(refs
);
143 cref_next
= TAILQ_NEXT(cref
, next
);
144 TAILQ_REMOVE(refs
, cref
, next
);
152 /* This structure is used to track memory allocation when serializing */
154 TAILQ_ENTRY(gc
) next
;
156 void *orig
; /* Original reference (not valid anymore !) */
158 TAILQ_HEAD(gc_l
, gc
);
161 marshal_alloc(struct gc_l
*pointers
, size_t len
, void *orig
)
163 void *result
= malloc(len
);
164 if (!result
) return NULL
;
165 struct gc
*gpointer
= NULL
;
166 if ((gpointer
= (struct gc
*)calloc(1,
167 sizeof(struct gc
))) == NULL
) {
171 gpointer
->pointer
= result
;
172 gpointer
->orig
= orig
;
173 TAILQ_INSERT_TAIL(pointers
, gpointer
, next
);
177 marshal_free(struct gc_l
*pointers
, int gconly
)
179 struct gc
*pointer
, *pointer_next
;
180 for (pointer
= TAILQ_FIRST(pointers
);
182 pointer
= pointer_next
) {
183 pointer_next
= TAILQ_NEXT(pointer
, next
);
184 TAILQ_REMOVE(pointers
, pointer
, next
);
186 free(pointer
->pointer
);
192 /* Unserialize the given object. */
194 _marshal_unserialize(struct marshal_info
*mi
, void *buffer
, size_t len
, void **output
,
195 void *_pointers
, int skip
, int osize
)
197 int total_len
= sizeof(struct marshal_serialized
) + (skip
?0:mi
->size
);
198 struct marshal_serialized
*serialized
= buffer
;
199 if (len
< sizeof(struct marshal_serialized
) || len
< total_len
) {
200 LLOG_WARNX("data to deserialize is too small for structure %s",
205 /* Initialize garbage collection */
206 struct gc_l
*pointers
= _pointers
;
208 pointers
= calloc(1, sizeof(struct gc_l
));
210 LLOG_WARNX("unable to allocate memory for garbage collection");
213 TAILQ_INIT(pointers
);
218 if (!strcmp(mi
->name
, "null string") || !strcmp(mi
->name
, "fixed string")) {
219 switch (mi
->name
[0]) {
220 case 'n': size
= strnlen((char *)serialized
->object
,
221 len
- sizeof(struct marshal_serialized
)) + 1; break;
222 case 'f': size
= osize
; break;
224 if (size
> len
- sizeof(struct marshal_serialized
)) {
225 LLOG_WARNX("data to deserialize contains a string too long");
227 goto unmarshal_error
;
232 /* First, the main structure */
234 if ((*output
= marshal_alloc(pointers
, size
, serialized
->orig
)) == NULL
) {
235 LLOG_WARNX("unable to allocate memory to unserialize structure %s",
238 goto unmarshal_error
;
240 memcpy(*output
, serialized
->object
, size
);
243 /* Then, each substructure */
244 struct marshal_subinfo
*current
;
245 for (current
= mi
->pointers
; current
->mi
; current
++) {
247 void *new = (unsigned char *)*output
+ current
->offset
;
248 if (current
->kind
== pointer
) {
249 if (*(void **)new == NULL
) continue;
251 /* Did we already see this reference? */
254 TAILQ_FOREACH(pointer
, pointers
, next
)
255 if (pointer
->orig
== *(void **)new) {
256 *(void **)((unsigned char *)*output
+
257 current
->offset
) = pointer
->pointer
;
261 if (already
) continue;
264 memcpy(&osize
, (unsigned char *)*output
+ current
->offset2
, sizeof(int));
265 sublen
= _marshal_unserialize(current
->mi
,
266 (unsigned char *)buffer
+ total_len
, len
- total_len
, &new, pointers
,
267 current
->kind
== substruct
, osize
);
269 LLOG_WARNX("unable to serialize substructure %s for %s",
270 current
->mi
->name
, mi
->name
);
272 goto unmarshal_error
;
274 /* Link the result */
275 if (current
->kind
== pointer
)
276 *(void **)((unsigned char *)*output
+ current
->offset
) = new;
281 if (pointers
&& !_pointers
) {
282 marshal_free(pointers
, (total_len
> 0));