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