1 /**
2  * \file
3  * MonoDynamicStream
4  * Copyright 2016 Microsoft
5  *
6  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
7  */
8 
9 #include <config.h>
10 #include <glib.h>
11 
12 #include "mono/metadata/dynamic-stream-internals.h"
13 #include "mono/metadata/metadata-internals.h"
14 #include "mono/utils/checked-build.h"
15 #include "mono/utils/mono-error-internals.h"
16 
17 void
mono_dynstream_init(MonoDynamicStream * sh)18 mono_dynstream_init (MonoDynamicStream *sh)
19 {
20 	MONO_REQ_GC_NEUTRAL_MODE;
21 
22 	sh->index = 0;
23 	sh->alloc_size = 4096;
24 	sh->data = (char *)g_malloc (4096);
25 	sh->hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
26 	mono_dynstream_insert_string (sh, "");
27 }
28 
29 static void
make_room_in_stream(MonoDynamicStream * stream,int size)30 make_room_in_stream (MonoDynamicStream *stream, int size)
31 {
32 	MONO_REQ_GC_NEUTRAL_MODE;
33 
34 	if (size <= stream->alloc_size)
35 		return;
36 
37 	while (stream->alloc_size <= size) {
38 		if (stream->alloc_size < 4096)
39 			stream->alloc_size = 4096;
40 		else
41 			stream->alloc_size *= 2;
42 	}
43 
44 	stream->data = (char *)g_realloc (stream->data, stream->alloc_size);
45 }
46 
47 guint32
mono_dynstream_insert_string(MonoDynamicStream * sh,const char * str)48 mono_dynstream_insert_string (MonoDynamicStream *sh, const char *str)
49 {
50 	MONO_REQ_GC_NEUTRAL_MODE;
51 
52 	guint32 idx;
53 	guint32 len;
54 	gpointer oldkey, oldval;
55 
56 	if (g_hash_table_lookup_extended (sh->hash, str, &oldkey, &oldval))
57 		return GPOINTER_TO_UINT (oldval);
58 
59 	len = strlen (str) + 1;
60 	idx = sh->index;
61 
62 	make_room_in_stream (sh, idx + len);
63 
64 	/*
65 	 * We strdup the string even if we already copy them in sh->data
66 	 * so that the string pointers in the hash remain valid even if
67 	 * we need to realloc sh->data. We may want to avoid that later.
68 	 */
69 	g_hash_table_insert (sh->hash, g_strdup (str), GUINT_TO_POINTER (idx));
70 	memcpy (sh->data + idx, str, len);
71 	sh->index += len;
72 	return idx;
73 }
74 
75 guint32
mono_dynstream_insert_mstring(MonoDynamicStream * sh,MonoString * str,MonoError * error)76 mono_dynstream_insert_mstring (MonoDynamicStream *sh, MonoString *str, MonoError *error)
77 {
78 	MONO_REQ_GC_UNSAFE_MODE;
79 
80 	error_init (error);
81 	char *name = mono_string_to_utf8_checked (str, error);
82 	return_val_if_nok (error, -1);
83 	guint32 idx;
84 	idx = mono_dynstream_insert_string (sh, name);
85 	g_free (name);
86 	return idx;
87 }
88 
89 guint32
mono_dynstream_add_data(MonoDynamicStream * stream,const char * data,guint32 len)90 mono_dynstream_add_data (MonoDynamicStream *stream, const char *data, guint32 len)
91 {
92 	MONO_REQ_GC_NEUTRAL_MODE;
93 
94 	guint32 idx;
95 
96 	make_room_in_stream (stream, stream->index + len);
97 	memcpy (stream->data + stream->index, data, len);
98 	idx = stream->index;
99 	stream->index += len;
100 	/*
101 	 * align index? Not without adding an additional param that controls it since
102 	 * we may store a blob value in pieces.
103 	 */
104 	return idx;
105 }
106 
107 guint32
mono_dynstream_add_zero(MonoDynamicStream * stream,guint32 len)108 mono_dynstream_add_zero (MonoDynamicStream *stream, guint32 len)
109 {
110 	MONO_REQ_GC_NEUTRAL_MODE;
111 
112 	guint32 idx;
113 
114 	make_room_in_stream (stream, stream->index + len);
115 	memset (stream->data + stream->index, 0, len);
116 	idx = stream->index;
117 	stream->index += len;
118 	return idx;
119 }
120 
121 void
mono_dynstream_data_align(MonoDynamicStream * stream)122 mono_dynstream_data_align (MonoDynamicStream *stream)
123 {
124 	MONO_REQ_GC_NEUTRAL_MODE;
125 
126 	guint32 count = stream->index % 4;
127 
128 	/* we assume the stream data will be aligned */
129 	if (count)
130 		mono_dynstream_add_zero (stream, 4 - count);
131 }
132 
133