1 /*
2  Copyright (c) 2018, MIPI Alliance, Inc.
3  All rights reserved.
4 
5  Redistribution and use in source and binary forms, with or without
6  modification, are permitted provided that the following conditions
7  are met:
8 
9  * Redistributions of source code must retain the above copyright
10    notice, this list of conditions and the following disclaimer.
11 
12  * Redistributions in binary form must reproduce the above copyright
13    notice, this list of conditions and the following disclaimer in
14    the documentation and/or other materials provided with the
15    distribution.
16 
17  * Neither the name of the copyright holder nor the names of its
18    contributors may be used to endorse or promote products derived
19    from this software without specific prior written permission.
20 
21  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33 
34 /*
35  * Contributors:
36  * Norbert Schulz (Intel Corporation) - Initial API and implementation
37  */
38 
39 #include <iomanip>
40 #include <sstream>
41 #include <iostream>
42 #include <type_traits>
43 
44 #include "mipi_syst_collateral.h"
45 #include "mipi_syst_guid.h"
46 
47 MIPI_SYST_NAMESPACE_BEGIN
48 
49 const char tagCollateral[]     = "syst:Collateral";
50 const char tagClient[]         = "syst:Client";
51 const char tagGuids[]          = "syst:Guids";
52 const char tagGuid[]           = "syst:Guid";
53 const char tagBuilds[]         = "syst:Builds";
54 const char tagBuild[]          = "syst:Build";
55 const char tagCatalog32[]      = "syst:Catalog32";
56 const char tagCatalog64[]      = "syst:Catalog64";
57 const char tagSourceFiles[]    = "syst:SourceFiles";
58 const char tagFile[]           = "syst:File";
59 const char tagShort32[]        = "syst:Short32";
60 const char tagShort64[]        = "syst:Short64";
61 const char tagWrite[]          = "syst:Write";
62 const char tagProtocol[]       = "syst:Protocol";
63 const char tagModules[]        = "syst:Modules";
64 const char tagModule[]         = "syst:Module";
65 const char tagID[]             = "ID";
66 const char tagMask[]           = "Mask";
67 const char tagFileAttr[]       = "File";
68 const char tagLine[]           = "Line";
69 const char tagFormat[]         = "syst:Format";
70 
71 
parseXml(const std::string & filename)72 std::vector<collateral*> collateral::parseXml(const std::string& filename)
73 {
74 	pugi::xml_document doc;
75 	pugi::xml_parse_result scan_result(doc.load_file(filename.c_str()));
76 	std::stringstream sstr;
77 
78 	if (!scan_result) {
79 		sstr
80 			<< "XML parser error on file "
81 			<< filename
82 			<< " : "
83 			<< scan_result.description();
84 
85 		throw std::invalid_argument(sstr.str());
86 	}
87 
88 	std::vector<collateral*> result;
89 
90 	for(auto client(doc.child(tagCollateral).child(tagClient));
91 		client;
92 		client = client.next_sibling(tagClient))
93 	{
94 		result.push_back(new collateral(client, filename));
95 	}
96 	return result;
97 }
98 
99 
collateral(pugi::xml_node & node,const std::string & file)100 collateral::collateral(pugi::xml_node& node, const std::string& file) :
101 	m_file(file),
102 	m_name(node.attribute("Name").as_string())
103 {
104 	// Scan id <-> item mappings
105 	//
106 	parseMaskedItems(node.child(tagGuids), tagGuid, m_guids);
107 	parseMaskedItems(node.child(tagBuilds), tagBuild, m_builds);
108 	parseMaskedItems(node.child(tagSourceFiles), tagFile, m_files);
109 	parseMaskedItems(node.child(tagModules), tagModule, m_modules);
110 	parseMaskedItems(node.child(tagCatalog32), tagFormat, m_msgs32);
111 	parseMaskedItems(node.child(tagCatalog64), tagFormat, m_msgs64);
112 	parseMaskedItems(node.child(tagShort32), tagFormat, m_shorts32);
113 	parseMaskedItems(node.child(tagShort64), tagFormat, m_shorts64);
114 	parseMaskedItems(node.child(tagWrite), tagProtocol, m_writeTypes);
115 }
116 
match(const guid & g,uint64_t build) const117 bool collateral::match(const guid& g, uint64_t build) const
118 {
119 	for (auto& it : m_guids) {
120 		if ((g & it.mask()) == (it.key() & it.mask()))
121 		{
122 			if (build != 0x0) {
123 				// find collateral with matching build number
124 				//
125 				if (m_builds.find(build) != m_builds.end())
126 				{
127 					return true;
128 				}
129 			} else {
130 				return true;
131 			}
132 		}
133 	}
134 
135 	return false;
136 }
137 
getCatalogEntry(uint32_t id) const138 template<> const collateral::catalogentry<uint32_t> * collateral::getCatalogEntry<>(uint32_t id) const
139 {
140 	auto hit = m_msgs32.find(id);
141 	return (hit != m_msgs32.end()) ? &hit->value() : nullptr;
142 }
143 
getCatalogEntry(uint64_t id) const144 template<> const collateral::catalogentry<uint64_t> * collateral::getCatalogEntry<>(uint64_t id) const
145 {
146 	auto hit = m_msgs64.find(id);
147 	return (hit != m_msgs64.end()) ? &hit->value() : nullptr;
148 }
getSourceFile(uint32_t id) const149 const std::string * collateral::getSourceFile(uint32_t id) const
150 {
151 	auto hit = m_files.find(id);
152 	return (hit != m_files.end()) ? &hit->value() : nullptr;
153 }
154 
getWriteType(uint8_t id) const155 const std::string * collateral::getWriteType(uint8_t id) const
156 {
157 	auto hit(m_writeTypes.find(id));
158 	return (hit != m_writeTypes.end()) ? &hit->value() : nullptr;
159 }
160 
getShortEntry(uint32_t id) const161 template<> const collateral::catalogentry<uint32_t> * collateral::getShortEntry<>(uint32_t id) const
162 {
163 	auto hit(m_shorts32.find(id));
164 	return (hit != m_shorts32.end()) ? &hit->value() : nullptr;
165 }
getShortEntry(uint64_t id) const166 template<> const collateral::catalogentry<uint64_t> * collateral::getShortEntry<>(uint64_t id) const
167 {
168 	auto hit(m_shorts64.find(id));
169 	return (hit != m_shorts64.end()) ? &hit->value() : nullptr;
170 }
171 
parse(K & dest,const std::string & s)172 template<typename K> bool parse(K& dest, const std::string& s)
173 {
174 	return stringToNum<K>(s, dest);
175 }
176 
parse(guid & g,const std::string & s)177 template<> bool parse<guid>(guid& g, const std::string& s)
178 {
179 	return g.parse(s);
180 }
181 
nomask()182 template<typename K> K nomask() { return (K)-1; }
nomask()183 template<> guid nomask<guid>() { return guid("{ffffffff-ffff-ffff-ffff-ffffffffffff}"); }
184 
operator <<(std::ostream & os,const collateral::catalogentry<T> & p)185 template<typename T> std::ostream & operator<<(std::ostream &os, const collateral::catalogentry<T>& p)
186 {
187 	return os << p.msg;
188 }
189 
190 template<typename K, typename V>
parseMaskedItems(pugi::xml_node root,const char * tag,masked_vector<K,V> & dest)191 void collateral::parseMaskedItems(
192 	pugi::xml_node root, const char * tag, masked_vector<K, V>& dest)
193 {
194 	for (pugi::xml_node item(root.child(tag));
195 		item;
196 		item = item.next_sibling(tag))
197 	{
198 		masked_item<K, V> data;
199 
200 		std::string keyStr(item.attribute(tagID).as_string());
201 		std::string val(item.child_value());
202 
203 		if (!parse<K>(data.key(), keyStr)) {
204 			std::stringstream what;
205 			what <<
206 				"Malformed " << tag << "entry ID=\"" << keyStr <<
207 				"\" value =\"" << val << "\"" <<
208 				" for catalog " << m_name;
209 			throw std::invalid_argument(what.str());
210 		}
211 
212 		std::string maskStr(item.attribute(tagMask).as_string());
213 		if (!maskStr.empty()) {
214 			if (!parse<K>(data.mask(), maskStr)) {
215 				std::stringstream what;
216 				what <<
217 					"Malformed " << tag << "entry Mask=\"" << maskStr <<
218 					"\" value =\"" << val << "\"" <<
219 					" for catalog " << m_name;
220 				throw std::invalid_argument(what.str());
221 			}
222 		} else {
223 			data.mask() = nomask<K>();
224 		}
225 
226 		data.value() = val;
227 
228 		// scan option file/line data if value supports source positions
229 		//
230 		if (std::is_base_of<catalogentry<K>, V>::value) {
231 			catalogentry<K> * entry((catalogentry<K>*)&data.value());
232 			entry->mask = data.mask();
233 
234 			std::string file(item.attribute(tagFileAttr).as_string());
235 			std::string line(item.attribute(tagLine).as_string());
236 
237 			if (!file.empty()) {
238 				if (!parse(entry->m_file, file)) {
239 					std::stringstream what;
240 					what <<
241 						"Malformed File attribute " << file <<
242 						"in section " << tag <<
243 						" for catalog " << m_name;
244 					throw std::invalid_argument(what.str());
245 				}
246 			}
247 
248 			if (!line.empty()) {
249 				if (!parse(entry->m_line, line)) {
250 					std::stringstream what;
251 					what <<
252 						"Malformed Line attribute " << line <<
253 						"in section " << tag <<
254 						" for catalog " << m_name;
255 					throw std::invalid_argument(what.str());
256 				}
257 			}
258 		}
259 
260 		auto it = dest.find(data.key());
261 		if (it != dest.end() && it->value() != val) {
262 			std::cerr <<
263 				"Overwriting  ID" << data.key() << " -> " << val <<
264 				" old value: " << it->value() << std::endl;
265 			*it = data;
266 		}
267 		else {
268 			dest.push_back(data);
269 		}
270 	}
271 }
272 
273 /** Helper to parse a GUID "{...}" string into the binary representation.
274 * @param str GUID ascii string "{xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"
275 * @param guid resulting guid
276 * @return true if guid could be parserd
277 */
parse(const std::string & str)278 bool guid::parse(const std::string& str)
279 {
280 	if (
281 		(str.length() != sizeof("{00000000-0000-0000-0000-000000000000}")-1) ||
282 		str[0]!= '{' ||str[37] != '}' ||
283 		str[9] != '-' || str[14] != '-' || str[19] != '-' || str[24] != '-'
284 	   )
285 	{
286 		return false;
287 	}
288 
289 	// parse the byte values;
290 	//
291 	const char * p(str.c_str()+1);
292 
293 	for (int i(0); i < 16; ++i, p += 2) {
294 		if (i == 4 || i == 6|| i == 8 || i == 10) {
295 			p++;  // skip "-"
296 		}
297 		std::stringstream sstr(std::string(p, 2));
298 		uint16_t temp;
299 
300 		if (! (sstr >> std::hex >>temp)) {
301 			return false;
302 		}
303 		u.b[i] = (uint8_t)(temp);
304 	}
305 
306 	return true;
307 }
308 
operator <<(std::ostream & os,const guid & g)309 std::ostream& operator<<(std::ostream& os, const guid& g)
310 {
311 	auto flags(os.flags());
312 
313 
314 	os << '{';
315 
316 	for (int i(0); i < 16; ++i) {
317 		os <<  std::setfill('0') << std::noshowbase << std::hex << std::setw(2)
318 		   << (uint16_t)g.u.b[i];
319 
320 		if (i == 3 || i == 5|| i == 7 || i == 9) {
321 			os << '-';
322 		}
323 	}
324 
325 	os << '}';
326 
327 	os.flags(flags);
328 	return os;
329 }
330 
331 MIPI_SYST_NAMESPACE_END