1 /*
2 Copyright (c) 2015-2021 The Khronos Group Inc.
3 Copyright (c) 2015-2021 Valve Corporation
4 Copyright (c) 2015-2021 LunarG, Inc.
5
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 THE SOFTWARE.
23 */
24
25 #include "loader_json.h"
26
27 #include <assert.h>
28 #include <float.h>
29 #include <limits.h>
30 #include <math.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include "cJSON.h"
36
37 #include "allocation.h"
38 #include "loader.h"
39 #include "log.h"
40
41 #if COMMON_UNIX_PLATFORMS
42 #include <fcntl.h>
43 #include <sys/stat.h>
44 #endif
45
46 #ifdef _WIN32
loader_read_entire_file(const struct loader_instance * inst,const char * filename,size_t * out_len,char ** out_buff)47 static VkResult loader_read_entire_file(const struct loader_instance *inst, const char *filename, size_t *out_len,
48 char **out_buff) {
49 HANDLE file_handle = INVALID_HANDLE_VALUE;
50 DWORD len = 0, read_len = 0;
51 VkResult res = VK_SUCCESS;
52 BOOL read_ok = false;
53
54 int filename_utf16_size = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
55 if (filename_utf16_size > 0) {
56 wchar_t *filename_utf16 = (wchar_t *)loader_stack_alloc(filename_utf16_size * sizeof(wchar_t));
57 if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filename_utf16, filename_utf16_size) == filename_utf16_size) {
58 file_handle =
59 CreateFileW(filename_utf16, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
60 }
61 }
62 if (INVALID_HANDLE_VALUE == file_handle) {
63 loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to open JSON file %s", filename);
64 res = VK_ERROR_INITIALIZATION_FAILED;
65 goto out;
66 }
67 len = GetFileSize(file_handle, NULL);
68 if (INVALID_FILE_SIZE == len) {
69 loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to read file size of JSON file %s", filename);
70 res = VK_ERROR_INITIALIZATION_FAILED;
71 goto out;
72 }
73 *out_buff = (char *)loader_instance_heap_calloc(inst, len + 1, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
74 if (NULL == *out_buff) {
75 loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to allocate memory to read JSON file %s", filename);
76 res = VK_ERROR_OUT_OF_HOST_MEMORY;
77 goto out;
78 }
79 read_ok = ReadFile(file_handle, *out_buff, len, &read_len, NULL);
80 if (len != read_len || false == read_ok) {
81 loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to read entire JSON file %s", filename);
82 res = VK_ERROR_INITIALIZATION_FAILED;
83 goto out;
84 }
85 *out_len = len + 1;
86 (*out_buff)[len] = '\0';
87
88 out:
89 if (INVALID_HANDLE_VALUE != file_handle) {
90 CloseHandle(file_handle);
91 }
92 return res;
93 }
94 #elif COMMON_UNIX_PLATFORMS
loader_read_entire_file(const struct loader_instance * inst,const char * filename,size_t * out_len,char ** out_buff)95 static VkResult loader_read_entire_file(const struct loader_instance *inst, const char *filename, size_t *out_len,
96 char **out_buff) {
97 FILE *file = NULL;
98 struct stat stats = {0};
99 VkResult res = VK_SUCCESS;
100
101 file = fopen(filename, "rb");
102 if (NULL == file) {
103 loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to open JSON file %s", filename);
104 res = VK_ERROR_INITIALIZATION_FAILED;
105 goto out;
106 }
107 if (-1 == fstat(fileno(file), &stats)) {
108 loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to read file size of JSON file %s", filename);
109 res = VK_ERROR_INITIALIZATION_FAILED;
110 goto out;
111 }
112 *out_buff = (char *)loader_instance_heap_calloc(inst, stats.st_size + 1, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
113 if (NULL == *out_buff) {
114 loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to allocate memory to read JSON file %s", filename);
115 res = VK_ERROR_OUT_OF_HOST_MEMORY;
116 goto out;
117 }
118 if (stats.st_size != (long int)fread(*out_buff, sizeof(char), stats.st_size, file)) {
119 loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to read entire JSON file %s", filename);
120 res = VK_ERROR_INITIALIZATION_FAILED;
121 goto out;
122 }
123 *out_len = stats.st_size + 1;
124 (*out_buff)[stats.st_size] = '\0';
125
126 out:
127 if (NULL != file) {
128 fclose(file);
129 }
130 return res;
131 }
132 #else
133 #warning fopen not available on this platform
loader_read_entire_file(const struct loader_instance * inst,const char * filename,size_t * out_len,char ** out_buff)134 VkResult loader_read_entire_file(const struct loader_instance *inst, const char *filename, size_t *out_len, char **out_buff) {
135 return VK_ERROR_INITIALIZATION_FAILED;
136 }
137 #endif
138
loader_get_json(const struct loader_instance * inst,const char * filename,cJSON ** json)139 TEST_FUNCTION_EXPORT VkResult loader_get_json(const struct loader_instance *inst, const char *filename, cJSON **json) {
140 char *json_buf = NULL;
141 VkResult res = VK_SUCCESS;
142
143 assert(json != NULL);
144
145 size_t json_len = 0;
146 *json = NULL;
147 res = loader_read_entire_file(inst, filename, &json_len, &json_buf);
148 if (VK_SUCCESS != res) {
149 goto out;
150 }
151 bool out_of_memory = false;
152 // Parse text from file
153 *json = loader_cJSON_ParseWithLength(inst ? &inst->alloc_callbacks : NULL, json_buf, json_len, &out_of_memory);
154 if (out_of_memory) {
155 loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Out of Memory error occurred while parsing JSON file %s.",
156 filename);
157 res = VK_ERROR_OUT_OF_HOST_MEMORY;
158 goto out;
159 } else if (*json == NULL) {
160 loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Invalid JSON file %s.", filename);
161 goto out;
162 }
163
164 out:
165 loader_instance_heap_free(inst, json_buf);
166 if (res != VK_SUCCESS && *json != NULL) {
167 loader_cJSON_Delete(*json);
168 *json = NULL;
169 }
170
171 return res;
172 }
173
loader_parse_json_string_to_existing_str(cJSON * object,const char * key,size_t out_str_len,char * out_string)174 VkResult loader_parse_json_string_to_existing_str(cJSON *object, const char *key, size_t out_str_len, char *out_string) {
175 if (NULL == key) {
176 return VK_ERROR_INITIALIZATION_FAILED;
177 }
178 cJSON *item = loader_cJSON_GetObjectItem(object, key);
179 if (NULL == item) {
180 return VK_ERROR_INITIALIZATION_FAILED;
181 }
182
183 if (item->type != cJSON_String || item->valuestring == NULL) {
184 return VK_ERROR_INITIALIZATION_FAILED;
185 }
186 bool out_of_memory = false;
187 bool success = loader_cJSON_PrintPreallocated(item, out_string, (int)out_str_len, cJSON_False);
188 if (out_of_memory) {
189 return VK_ERROR_OUT_OF_HOST_MEMORY;
190 }
191 if (!success) {
192 return VK_ERROR_INITIALIZATION_FAILED;
193 }
194 return VK_SUCCESS;
195 }
196
loader_parse_json_string(cJSON * object,const char * key,char ** out_string)197 VkResult loader_parse_json_string(cJSON *object, const char *key, char **out_string) {
198 if (NULL == key) {
199 return VK_ERROR_INITIALIZATION_FAILED;
200 }
201
202 cJSON *item = loader_cJSON_GetObjectItem(object, key);
203 if (NULL == item || NULL == item->valuestring) {
204 return VK_ERROR_INITIALIZATION_FAILED;
205 }
206
207 bool out_of_memory = false;
208 char *str = loader_cJSON_Print(item, &out_of_memory);
209 if (out_of_memory || NULL == str) {
210 return VK_ERROR_OUT_OF_HOST_MEMORY;
211 }
212 if (NULL != out_string) {
213 *out_string = str;
214 }
215 return VK_SUCCESS;
216 }
loader_parse_json_array_of_strings(const struct loader_instance * inst,cJSON * object,const char * key,struct loader_string_list * string_list)217 VkResult loader_parse_json_array_of_strings(const struct loader_instance *inst, cJSON *object, const char *key,
218 struct loader_string_list *string_list) {
219 if (NULL == key) {
220 return VK_ERROR_INITIALIZATION_FAILED;
221 }
222 cJSON *item = loader_cJSON_GetObjectItem(object, key);
223 if (NULL == item) {
224 return VK_ERROR_INITIALIZATION_FAILED;
225 }
226
227 uint32_t count = loader_cJSON_GetArraySize(item);
228 if (count == 0) {
229 return VK_SUCCESS;
230 }
231
232 VkResult res = create_string_list(inst, count, string_list);
233 if (VK_ERROR_OUT_OF_HOST_MEMORY == res) {
234 goto out;
235 }
236 cJSON *element = NULL;
237 cJSON_ArrayForEach(element, item) {
238 if (element->type != cJSON_String) {
239 return VK_ERROR_INITIALIZATION_FAILED;
240 }
241 bool out_of_memory = false;
242 char *out_data = loader_cJSON_Print(element, &out_of_memory);
243 if (out_of_memory) {
244 res = VK_ERROR_OUT_OF_HOST_MEMORY;
245 goto out;
246 }
247 res = append_str_to_string_list(inst, string_list, out_data);
248 if (VK_ERROR_OUT_OF_HOST_MEMORY == res) {
249 goto out;
250 }
251 }
252 out:
253 if (res == VK_ERROR_OUT_OF_HOST_MEMORY && NULL != string_list->list) {
254 free_string_list(inst, string_list);
255 }
256
257 return res;
258 }
259