1//
2// Copyright (c) Microsoft. All rights reserved.
3// Licensed under the MIT license. See LICENSE file in the project root for full license information.
4//
5
6//----------------------------------------------------------
7// MCList.h - MethodContext List utility class
8//----------------------------------------------------------
9
10#include "standardpch.h"
11#include "mclist.h"
12#include "logging.h"
13
14bool MCList::processArgAsMCL(char* input, int* count, int** list)
15{
16 // If it contains only '0-9', '-', ',' try to see it as a range list, else try to load as a file
17 bool isRangeList = true;
18
19 size_t len = strlen(input);
20
21 for (unsigned int i = 0; (i < len) && isRangeList; i++)
22 {
23 if ((input[i] != '-') && (input[i] != ',') && (!isdigit((unsigned char)input[i])))
24 isRangeList = false;
25 }
26
27 if (isRangeList)
28 {
29 // Count items
30 *count = 0;
31 unsigned rangeStart = 0;
32 bool inRange = false;
33 unsigned scratch = 0;
34 bool foundDigit = false;
35
36 char* tail = input + len;
37
38 for (char* head = input; head <= tail; head++)
39 {
40 scratch = 0;
41 foundDigit = false;
42 while ((head <= tail) && (isdigit((unsigned char)*head)))
43 {
44 scratch = (scratch * 10) + ((*head) - '0');
45 foundDigit = true;
46 head++;
47 }
48 if (foundDigit)
49 {
50 if (inRange)
51 {
52 inRange = false;
53 if (rangeStart >= scratch)
54 {
55 LogError("Invalid range in '%s'", input);
56 return false;
57 }
58 (*count) += scratch - rangeStart;
59 }
60 else
61 {
62 rangeStart = scratch;
63 (*count)++;
64 }
65 }
66 if (*head == '-')
67 inRange = true;
68 }
69
70 if (*count == 0)
71 {
72 LogError("Didn't find a list!");
73 return false;
74 }
75
76 inRange = false;
77 rangeStart = 0;
78
79 int* ll = new int[*count];
80 *list = ll;
81 int index = 0;
82 ll[index] = 0;
83
84 for (char* head = input; head <= tail; head++)
85 {
86 scratch = 0;
87 foundDigit = false;
88 while ((head <= tail) && (isdigit((unsigned char)*head)))
89 {
90 scratch = (scratch * 10) + ((*head) - '0');
91 foundDigit = true;
92 head++;
93 }
94 if (foundDigit)
95 {
96 if (inRange)
97 {
98 inRange = false;
99 for (unsigned int i = rangeStart + 1; i <= scratch; i++)
100 ll[index++] = i;
101 }
102 else
103 {
104 rangeStart = scratch;
105 ll[index++] = scratch;
106 }
107 }
108 if (*head == '-')
109 inRange = true;
110 }
111 if (inRange)
112 {
113 LogError("Found invalid external range in '%s'", input);
114 return false;
115 }
116 goto checkMCL;
117 }
118 else
119 {
120 char* lastdot = strrchr(input, '.');
121 if (lastdot != nullptr && _stricmp(lastdot, ".mcl") == 0)
122 {
123 // Read MCLFile
124 if (!getLineData(input, count, list))
125 return false;
126 if (*count >= 0)
127 goto checkMCL;
128 }
129 return false;
130 }
131
132checkMCL: // check that mcl list is increasing only
133 int* ll = (*list);
134 if (ll[0] == 0)
135 {
136 LogError("MCL list needs to start from 1!");
137 return false;
138 }
139 for (int i = 1; i < *count; i++)
140 {
141 if (ll[i - 1] >= ll[i])
142 {
143 LogError("MCL list must be increasing.. found %d -> %d", ll[i - 1], ll[i]);
144 return false;
145 }
146 }
147 return true;
148}
149
150// Returns true on success, false on failure.
151// On success, sets *pIndexCount to the number of indices read, and *pIndexes to a new array with all the indices read.
152// The caller must free the memory with delete[].
153/* static */
154bool MCList::getLineData(const char* nameOfInput, /* OUT */ int* pIndexCount, /* OUT */ int** pIndexes)
155{
156 HANDLE hFile = CreateFileA(nameOfInput, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
157 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
158 if (hFile == INVALID_HANDLE_VALUE)
159 {
160 LogError("Unable to open '%s'. GetLastError()=%u", nameOfInput, GetLastError());
161 return false;
162 }
163 LARGE_INTEGER DataTemp;
164 if (!GetFileSizeEx(hFile, &DataTemp))
165 {
166 LogError("GetFileSizeEx failed. GetLastError()=%u", GetLastError());
167 return false;
168 }
169
170 if (DataTemp.QuadPart > MAXMCLFILESIZE)
171 {
172 LogError("Size %d exceeds max size of %d", DataTemp.QuadPart, MAXMCLFILESIZE);
173 return false;
174 }
175
176 int sz = DataTemp.u.LowPart;
177 char* buff = new char[sz];
178 DWORD bytesRead;
179 if (ReadFile(hFile, buff, sz, &bytesRead, nullptr) == 0)
180 {
181 LogError("ReadFile failed. GetLastError()=%u", GetLastError());
182 delete[] buff;
183 return false;
184 }
185 if (!CloseHandle(hFile))
186 {
187 LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
188 delete[] buff;
189 return false;
190 }
191
192 // Count the lines. Note that the last line better be terminated by a newline.
193 int lineCount = 0;
194 for (int i = 0; i < sz; i++)
195 {
196 if (buff[i] == '\n')
197 {
198 lineCount++;
199 }
200 }
201
202 int* indexes = new int[lineCount];
203 int indexCount = 0;
204 int i = 0;
205 while (i < sz)
206 {
207 // seek the first number on the line. This will skip empty lines and lines with no digits.
208 while (!isdigit((unsigned char)buff[i]))
209 i++;
210 // read in the number
211 indexes[indexCount++] = atoi(&buff[i]);
212 // seek to the start of next line
213 while ((i < sz) && (buff[i] != '\n'))
214 i++;
215 i++;
216 }
217 delete[] buff;
218
219 *pIndexCount = indexCount;
220 *pIndexes = indexes;
221 return true;
222}
223
224void MCList::InitializeMCL(char* filename)
225{
226 hMCLFile = CreateFileA(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
227 if (hMCLFile == INVALID_HANDLE_VALUE)
228 {
229 LogError("Failed to open output file '%s'. GetLastError()=%u", filename, GetLastError());
230 }
231}
232
233void MCList::AddMethodToMCL(int methodIndex)
234{
235 if (hMCLFile != INVALID_HANDLE_VALUE)
236 {
237 char strMethodIndex[12];
238 DWORD charCount = 0;
239 DWORD bytesWritten = 0;
240
241 charCount = sprintf_s(strMethodIndex, sizeof(strMethodIndex), "%d\r\n", methodIndex);
242
243 if (!WriteFile(hMCLFile, strMethodIndex, charCount, &bytesWritten, nullptr) || bytesWritten != charCount)
244 {
245 LogError("Failed to write method index '%d'. GetLastError()=%u", strMethodIndex, GetLastError());
246 }
247 }
248}
249
250void MCList::CloseMCL()
251{
252 if (hMCLFile != INVALID_HANDLE_VALUE)
253 {
254 if (CloseHandle(hMCLFile) == 0)
255 {
256 LogError("CloseHandle failed. GetLastError()=%u", GetLastError());
257 }
258 }
259}
260