526 lines
16 KiB
C#
526 lines
16 KiB
C#
// **********************************************************************************************************
|
|
// BytePackingXml.cs
|
|
// 5/18/2022
|
|
// NGI - Next Generation Interceptor
|
|
//
|
|
// Contract No. HQ0856-21-C-0003/1022000209
|
|
//
|
|
// THIS DOCUMENT DOES NOT CONTAIN TECHNOLOGY OR TECHNICAL DATA CONTROLLED UNDER EITHER THE U.S.
|
|
// INTERNATIONAL TRAFFIC IN ARMS REGULATIONS OR THE U.S. EXPORT ADMINISTRATION REGULATIONS.
|
|
//
|
|
// RAYTHEON PROPRIETARY: THIS DOCUMENT CONTAINS DATA OR INFORMATION PROPRIETARY TO RAYTHEON
|
|
// COMPANY AND IS RESTRICTED TO USE ONLY BY PERSONS AUTHORIZED BY RAYTHEON COMPANY IN WRITING TO USE IT.
|
|
// DISCLOSURE TO UNAUTHORIZED PERSONS WOULD LIKELY CAUSE SUBSTANTIAL COMPETITIVE HARM TO RAYTHEON
|
|
// COMPANY'S BUSINESS POSITION. NEITHER SAID DOCUMENT NOR ITS CONTENTS SHALL BE FURNISHED OR DISCLOSED
|
|
// TO OR COPIED OR USED BY PERSONS OUTSIDE RAYTHEON COMPANY WITHOUT THE EXPRESS WRITTEN APPROVAL OF
|
|
// RAYTHEON COMPANY.
|
|
//
|
|
// UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
|
|
//
|
|
// DESTRUCTION NOTICE: FOR CLASSIFIED DOCUMENTS FOLLOW THE PROCEDURES IN DOD 5220.22-M,
|
|
// NATIONAL INDUSTRIAL SECURITY PROGRAM OPERATING MANUAL, FEBRUARY 2006,
|
|
// INCORPORATING CHANGE 1, MARCH 28, 2013, CHAPTER 5, SECTION 7, OR DODM 5200.01-VOLUME 3,
|
|
// DOD INFORMATION SECURITY PROGRAM: PROTECTION OF CLASSIFIED INFORMATION, ENCLOSURE 3,
|
|
// SECTION 17. FOR CONTROLLED UNCLASSIFIED INFORMATION FOLLOW THE PROCEDURES IN DODM 5200.01-VOLUME 4,
|
|
// INFORMATION SECURITY PROGRAM: CONTROLLED UNCLASSIFIED INFORMATION.
|
|
//
|
|
// CONTROLLED BY: MISSILE DEFENSE AGENCY
|
|
// CONTROLLED BY: GROUND-BASED MIDCOURSE DEFENSE PROGRAM OFFICE
|
|
// CUI CATEGORY: CTI
|
|
// DISTRIBUTION/DISSEMINATION CONTROL: F
|
|
// POC: Alex Kravchenko (1118268)
|
|
// **********************************************************************************************************
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq.Expressions;
|
|
using System.Text.RegularExpressions;
|
|
using System.Xml;
|
|
using System.Xml.XPath;
|
|
|
|
namespace Raytheon.Common.Coe
|
|
{
|
|
// Takes an XML ICD and adds in nodes to represent the byte packing
|
|
// The padding added will be displayed in the tool as arrays of characters
|
|
internal class BytePackingXml : XmlDocument
|
|
{
|
|
private readonly int m_Packing = 0;
|
|
private readonly Dictionary<string, double> m_ConstMap;
|
|
private readonly Dictionary<string, int> m_SizeMap;
|
|
private readonly Dictionary<string, int> m_PadMap;
|
|
|
|
public BytePackingXml(string icdStr) : base()
|
|
{
|
|
// Create the dictionaries
|
|
m_ConstMap = new Dictionary<string, double>();
|
|
m_PadMap = new Dictionary<string, int>();
|
|
m_SizeMap = new Dictionary<string, int>();
|
|
|
|
// Create an XML document from the string
|
|
LoadXml(icdStr);
|
|
|
|
XPathNavigator node = CreateNavigator();
|
|
//Get the type of packing
|
|
XPathNodeIterator nodeset = node.Select("/interface/packing");
|
|
if (nodeset.MoveNext() == true)
|
|
{
|
|
if (!Parse.Try(nodeset.Current.Value.Trim(), out m_Packing))
|
|
{
|
|
switch (nodeset.Current.Value.Trim())
|
|
{
|
|
case "char":
|
|
case "1":
|
|
m_Packing = 1;
|
|
break;
|
|
case "short":
|
|
case "2":
|
|
m_Packing = 2;
|
|
break;
|
|
case "long":
|
|
case "4":
|
|
m_Packing = 4;
|
|
break;
|
|
case "double":
|
|
case "8":
|
|
default:
|
|
m_Packing = 8;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle all of the constants
|
|
nodeset = node.Select("/interface/constant|/interface/namespace/constant");
|
|
while (nodeset.MoveNext())
|
|
{
|
|
ProcessConstantNode(nodeset.Current);
|
|
}
|
|
|
|
nodeset = node.Select("/interface/typedef|/interface/namespace/typedef");
|
|
while (nodeset.MoveNext())
|
|
{
|
|
ProcessTypedefNode(nodeset.Current);
|
|
}
|
|
|
|
nodeset = node.Select("/interface/enum|/interface/namespace/enum");
|
|
while (nodeset.MoveNext())
|
|
{
|
|
ProcessEnumerationNode(nodeset.Current);
|
|
}
|
|
|
|
nodeset = node.Select("/interface/structure|/interface/message|/interface/namespace/structure|/interface/namespace/message");
|
|
while (nodeset.MoveNext())
|
|
{
|
|
ProcessStructureNode(nodeset.Current);
|
|
}
|
|
|
|
NormalizeIcdLabels();
|
|
}
|
|
|
|
// This function takes all of the messages in the ICD
|
|
// and converts the labels into decimal, so when we do
|
|
// a string lookup, they will all be in the same known format
|
|
private void NormalizeIcdLabels()
|
|
{
|
|
XPathNavigator navigator = CreateNavigator();
|
|
XPathNodeIterator nodeset;
|
|
|
|
nodeset = navigator.Select("/interface/message/label|/interface/namespace/message/label");
|
|
while (nodeset.MoveNext())
|
|
{
|
|
try
|
|
{
|
|
double dLabel = GetConstFromString(nodeset.Current.Value);
|
|
nodeset.Current.SetValue(((int)dLabel).ToString());
|
|
}
|
|
catch
|
|
{
|
|
throw new Exception("Message Label, " + nodeset.Current.Value + ", can not be converted to an integer");
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ProcessConstantNode(XPathNavigator node)
|
|
{
|
|
string name = "";
|
|
string constStr = "";
|
|
double constNum = 0;
|
|
|
|
if (node.MoveToChild("name", ""))
|
|
{
|
|
name = node.Value.Trim();
|
|
node.MoveToParent();
|
|
}
|
|
if (node.MoveToChild("value", ""))
|
|
{
|
|
constStr = node.Value.Trim();
|
|
if ((constStr.Length != 0) && (constStr[0] != '\"'))
|
|
{
|
|
constNum = GetConstFromString(constStr);
|
|
}
|
|
else
|
|
{
|
|
constNum = 0;
|
|
}
|
|
node.MoveToParent();
|
|
}
|
|
|
|
// Verify the correctnes of the <constant> tag
|
|
if ((name != "") && (constStr != ""))
|
|
{
|
|
AddItemToMap(m_ConstMap, name, constNum);
|
|
}
|
|
else
|
|
{
|
|
throw new Exception(
|
|
"ERROR: Constant Definition Incorrect - <name>:" + name +
|
|
" <value>:" + constStr);
|
|
}
|
|
}
|
|
|
|
private void ProcessTypedefNode(XPathNavigator node)
|
|
{
|
|
string name = "";
|
|
string type = "";
|
|
int typeSize = 0; // Size of the item
|
|
int typePad = 0; //Size of the largest item to pad to
|
|
|
|
if (node.MoveToChild("name", ""))
|
|
{
|
|
name = node.Value.Trim();
|
|
node.MoveToParent();
|
|
}
|
|
if (node.MoveToChild("value", ""))
|
|
{
|
|
type = node.Value.Trim();
|
|
GetSizeFromType(type, out typeSize, out typePad);
|
|
node.MoveToParent();
|
|
}
|
|
|
|
// Verify the correctnes of the <typedef> tag
|
|
if ((name != "") && (type != ""))
|
|
{
|
|
AddItemToMap(m_PadMap, name, typePad);
|
|
AddItemToMap(m_SizeMap, name, typeSize);
|
|
}
|
|
else
|
|
{
|
|
throw new Exception(
|
|
"ERROR: Typedef Definition Incorrect - <name>:" + name +
|
|
" <value>:" + type);
|
|
}
|
|
}
|
|
|
|
private void ProcessEnumerationNode(XPathNavigator node)
|
|
{
|
|
string name;
|
|
double constNum = 0;
|
|
var constStr = string.Empty;
|
|
|
|
if (node.MoveToChild("name", ""))
|
|
{
|
|
name = node.Value.Trim();
|
|
AddItemToMap(m_PadMap, name, 4);
|
|
AddItemToMap(m_SizeMap, name, 4);
|
|
node.MoveToParent();
|
|
}
|
|
else
|
|
{
|
|
throw new Exception("ERROR: Enumeration Definition Incorrect. No <name> tag present.");
|
|
}
|
|
|
|
XPathNodeIterator nodeSet = node.Select("item|enum_item");
|
|
while (nodeSet.MoveNext())
|
|
{
|
|
name = string.Empty;
|
|
|
|
if ((nodeSet.Current.MoveToChild("name", "")) ||
|
|
(nodeSet.Current.MoveToChild("item_name", "")))
|
|
{
|
|
name = nodeSet.Current.Value.Trim();
|
|
nodeSet.Current.MoveToParent();
|
|
}
|
|
if (nodeSet.Current.MoveToChild("value", ""))
|
|
{
|
|
constStr = nodeSet.Current.Value.Trim();
|
|
constNum = GetConstFromString(constStr);
|
|
nodeSet.Current.MoveToParent();
|
|
}
|
|
|
|
// Verify the correctnes of the <enum><item> tag
|
|
if ((name != "") && (constStr != ""))
|
|
{
|
|
AddItemToMap(m_ConstMap, name, constNum);
|
|
}
|
|
else
|
|
{
|
|
throw new Exception($"ERROR: Enumeration Item Definition Incorrect - <name>: {name} <value>: {constStr}");
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ProcessStructureNode(XPathNavigator node)
|
|
{
|
|
string name;
|
|
|
|
if (node.MoveToChild("name", ""))
|
|
{
|
|
name = node.Value.Trim();
|
|
node.MoveToParent();
|
|
}
|
|
else
|
|
{
|
|
throw new Exception("ERROR: Stucture/Message Definition Incorrect. No <name> tag present.");
|
|
}
|
|
|
|
int maxSize = 0;
|
|
int padCount = 0;
|
|
uint bitCount = 0; // Used to see how many bits have been processed.
|
|
int lastItemSize = 0;
|
|
|
|
var nodeSet = node.Select("item|struct_item|msg_item");
|
|
while (nodeSet.MoveNext())
|
|
{
|
|
GetItemSize(nodeSet.Current, out int padSize, out int itemSize, out int reps, out uint bits);
|
|
if ((lastItemSize != itemSize) || ((bitCount + bits) > (uint)(itemSize * 8)))
|
|
{
|
|
bitCount = 0; // Size changed or bit rollover
|
|
}
|
|
|
|
if (bitCount == 0)
|
|
{
|
|
padCount += AddPadding(node, nodeSet.Current, padSize, padCount);
|
|
|
|
// Set maxSize
|
|
if (padSize > maxSize)
|
|
{
|
|
maxSize = padSize;
|
|
}
|
|
|
|
// Keep up with the pad count
|
|
padCount += (itemSize * reps);
|
|
}
|
|
|
|
lastItemSize = itemSize;
|
|
bitCount += bits;
|
|
}
|
|
|
|
if (maxSize != 0)
|
|
{
|
|
// Add final padding
|
|
padCount += AddPadding(node, null, maxSize, padCount);
|
|
}
|
|
|
|
AddItemToMap(m_PadMap, name, maxSize);
|
|
AddItemToMap(m_SizeMap, name, padCount);
|
|
}
|
|
|
|
private void GetItemSize(XPathNavigator node, out int padSize, out int itemSize, out int reps, out uint bits)
|
|
{
|
|
string name = "";
|
|
|
|
if ((node.MoveToChild("name", "")) ||
|
|
(node.MoveToChild("item_name", "")))
|
|
{
|
|
name = node.Value.Trim();
|
|
node.MoveToParent();
|
|
}
|
|
|
|
itemSize = -1;
|
|
padSize = -1;
|
|
reps = 1;
|
|
bits = 0;
|
|
|
|
var nodeSet = node.Select("type");
|
|
while (nodeSet.MoveNext())
|
|
{
|
|
GetSizeFromType(nodeSet.Current.Value.Trim(), out padSize, out itemSize);
|
|
}
|
|
|
|
nodeSet = node.Select("bits");
|
|
if (nodeSet.MoveNext())
|
|
{
|
|
bits = (uint)GetConstFromString(nodeSet.Current.Value.Trim());
|
|
}
|
|
|
|
nodeSet = node.Select("arrayLength|imageWidth|imageHeight");
|
|
while (nodeSet.MoveNext())
|
|
{
|
|
try
|
|
{
|
|
reps *= (int)GetConstFromString(nodeSet.Current.Value.Trim());
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw new Exception
|
|
(e.Message + " item name = \"" + name + "\", tag = <" +
|
|
nodeSet.Current.Name + ">.");
|
|
|
|
}
|
|
}
|
|
|
|
if ((itemSize == -1) || (padSize == -1))
|
|
{
|
|
throw new Exception
|
|
("ERROR: Item named " + name + "does not contain a <type> tag.");
|
|
}
|
|
|
|
if (bits == 0)
|
|
bits = (uint)padSize * 8;
|
|
}
|
|
|
|
private double GetConstFromString(string constStr)
|
|
{
|
|
// remove namespace
|
|
constStr = Regex.Replace(constStr, @"[^:]+::([^:]+)", "$1");
|
|
if ((constStr.Length > 0) && (constStr[0] == '\''))
|
|
{
|
|
byte charData = (byte)constStr[1];
|
|
constStr = charData.ToString();
|
|
}
|
|
|
|
if (Parse.Try(constStr, out double data) == false)
|
|
{
|
|
if (Parse.Try(constStr, out int iData) == false)
|
|
{
|
|
try
|
|
{
|
|
data = m_ConstMap[constStr];
|
|
}
|
|
catch
|
|
{
|
|
throw new Exception("ERROR: ConstantValue - \"" + constStr + "\" does not resolve to a number.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
data = (double)iData;
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
|
|
private void AddItemToMap(Dictionary<string, int> map, string name, int value)
|
|
{
|
|
if (map.ContainsKey(name))
|
|
{
|
|
throw new Exception("ERROR: Element " + name + " is defined multiple times.");
|
|
}
|
|
else
|
|
{
|
|
map.Add(name, value);
|
|
}
|
|
}
|
|
|
|
private void AddItemToMap(Dictionary<string, double> map, string name, double value)
|
|
{
|
|
if (map.ContainsKey(name))
|
|
{
|
|
throw new Exception("ERROR: Element " + name + " is defined multiple times.");
|
|
}
|
|
else
|
|
{
|
|
map.Add(name, value);
|
|
}
|
|
}
|
|
|
|
private void GetSizeFromType(string type, out int typePad, out int typeSize)
|
|
{
|
|
// Remove all whitespace
|
|
type = type.Replace(" ", "");
|
|
// remove namespace
|
|
type = Regex.Replace(type, @"[^:]+::([^:]+)", "$1");
|
|
|
|
switch (type)
|
|
{
|
|
case "uint8_t":
|
|
type = "char";
|
|
break;
|
|
case "uint16_t":
|
|
case "int16_t":
|
|
type = "short";
|
|
break;
|
|
case "uint32_t":
|
|
case "int32_t":
|
|
type = "int";
|
|
break;
|
|
case "uint64_t":
|
|
case "int64_t":
|
|
type = "double";
|
|
break;
|
|
}
|
|
|
|
if ((type == "char"))
|
|
{
|
|
typePad = 1;
|
|
typeSize = 1;
|
|
}
|
|
else if ((type == "short"))
|
|
{
|
|
typePad = 2;
|
|
typeSize = 2;
|
|
}
|
|
else if ((Regex.IsMatch(type, @"unsigned", RegexOptions.IgnoreCase)) || (type == "int") || (type == "float") ||
|
|
(type == "boolean") || (type == "address"))
|
|
{
|
|
typePad = 4;
|
|
typeSize = 4;
|
|
}
|
|
else if ((type == "double"))
|
|
{
|
|
typePad = 8;
|
|
typeSize = 8;
|
|
}
|
|
else // The type is complex and has already been defined
|
|
{
|
|
try
|
|
{
|
|
typePad = m_PadMap[type];
|
|
typeSize = m_SizeMap[type];
|
|
}
|
|
catch
|
|
{
|
|
throw new Exception("ERROR: <type> - " + type + " used without being defined.");
|
|
}
|
|
}
|
|
}
|
|
|
|
private int AddPadding(XPathNavigator ParentElement, XPathNavigator CurrentElement, int padSize, int padCount)
|
|
{
|
|
int padAdd = 0;
|
|
int padTo = padSize;
|
|
|
|
if (m_Packing < padSize)
|
|
{
|
|
padTo = m_Packing;
|
|
}
|
|
|
|
if (m_Packing > 0 && padTo != 0 && (padCount % padTo != 0))
|
|
{
|
|
padAdd = padTo - (padCount % padTo);
|
|
InsertPaddingNode(ParentElement, CurrentElement, padAdd);
|
|
}
|
|
|
|
return padAdd;
|
|
}
|
|
|
|
private void InsertPaddingNode(XPathNavigator ParentElement, XPathNavigator CurrentElement, int padAdd)
|
|
{
|
|
string pad = "<item>" +
|
|
"<name>" + Message.PADDING_ITEM_NAME + "</name>" +
|
|
"<type>char</type>" +
|
|
"<arrayLength>" + padAdd + "</arrayLength>" +
|
|
"<instruType>S</instruType>" +
|
|
"</item>";
|
|
if (CurrentElement != null)
|
|
{
|
|
CurrentElement.InsertBefore(pad);
|
|
}
|
|
else // End padding
|
|
{
|
|
ParentElement.AppendChild(pad);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|