Move packet info from cxfa_document_parser.cpp to xfa_basic_data.cpp.

Put it in the same file as the other generated table.

Introduce XFA_PACKETINFO struct to separate the results of a query
from the underlying table itself. Omit the hash from this struct
since it is an implementation detail that may someday go away. This
requires us to rehash a few strings, but the code that it calls with
this hash should someday get refactored to take a saner argument.

When looking up packets by name, do a final string comparison instead
of a hash comparison to avoid aliasing alternate forms of user input
to these names.

Add unit tests for API.

Change-Id: I7659ba79058591e1c0a4417c318cdcb696ba37c5
Reviewed-on: https://pdfium-review.googlesource.com/c/47231
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/xfa/fxfa/parser/cxfa_document_parser.cpp b/xfa/fxfa/parser/cxfa_document_parser.cpp
index 8c79bab..861a789 100644
--- a/xfa/fxfa/parser/cxfa_document_parser.cpp
+++ b/xfa/fxfa/parser/cxfa_document_parser.cpp
@@ -33,40 +33,6 @@
 
 namespace {
 
-struct PacketInfo {
-  uint32_t hash;
-  const wchar_t* name;
-  XFA_PacketType packet_type;
-  const wchar_t* uri;
-  uint32_t flags;
-};
-
-const PacketInfo PacketData[] = {
-#undef PCKT____
-#define PCKT____(a, b, c, d, e, f) \
-  {a, L##b, XFA_PacketType::c, d,  \
-   XFA_XDPPACKET_FLAGS_##e | XFA_XDPPACKET_FLAGS_##f},
-#include "xfa/fxfa/parser/packets.inc"
-#undef PCKT____
-};
-
-const PacketInfo* GetPacketByIndex(XFA_PacketType ePacket) {
-  return PacketData + static_cast<uint8_t>(ePacket);
-}
-
-const PacketInfo* GetPacketByName(const WideStringView& wsName) {
-  if (wsName.IsEmpty())
-    return nullptr;
-
-  uint32_t hash = FX_HashCode_GetW(wsName, false);
-  auto* elem = std::lower_bound(
-      std::begin(PacketData), std::end(PacketData), hash,
-      [](const PacketInfo& a, uint32_t hash) { return a.hash < hash; });
-  if (elem != std::end(PacketData) && elem->hash == hash)
-    return elem;
-  return nullptr;
-}
-
 CFX_XMLNode* GetDocumentNode(CFX_XMLNode* pRootNode) {
   for (CFX_XMLNode* pXMLNode = pRootNode->GetFirstChild(); pXMLNode;
        pXMLNode = pXMLNode->GetNextSibling()) {
@@ -177,23 +143,21 @@
 }
 
 CFX_XMLNode* GetDataSetsFromXDP(CFX_XMLNode* pXMLDocumentNode) {
-  const PacketInfo* datasets_packet =
-      GetPacketByIndex(XFA_PacketType::Datasets);
-  if (MatchNodeName(pXMLDocumentNode, datasets_packet->name,
-                    datasets_packet->uri, datasets_packet->flags)) {
+  XFA_PACKETINFO datasets_packet =
+      XFA_GetPacketByIndex(XFA_PacketType::Datasets);
+  if (MatchNodeName(pXMLDocumentNode, datasets_packet.name, datasets_packet.uri,
+                    datasets_packet.flags)) {
     return pXMLDocumentNode;
   }
-
-  const PacketInfo* packet = GetPacketByIndex(XFA_PacketType::Xdp);
-  if (!MatchNodeName(pXMLDocumentNode, packet->name, packet->uri,
-                     packet->flags)) {
+  XFA_PACKETINFO xdp_packet = XFA_GetPacketByIndex(XFA_PacketType::Xdp);
+  if (!MatchNodeName(pXMLDocumentNode, xdp_packet.name, xdp_packet.uri,
+                     xdp_packet.flags)) {
     return nullptr;
   }
-
   for (CFX_XMLNode* pDatasetsNode = pXMLDocumentNode->GetFirstChild();
        pDatasetsNode; pDatasetsNode = pDatasetsNode->GetNextSibling()) {
-    if (MatchNodeName(pDatasetsNode, datasets_packet->name,
-                      datasets_packet->uri, datasets_packet->flags)) {
+    if (MatchNodeName(pDatasetsNode, datasets_packet.name, datasets_packet.uri,
+                      datasets_packet.flags)) {
       return pDatasetsNode;
     }
   }
@@ -400,11 +364,9 @@
 
 CXFA_Node* CXFA_DocumentParser::ParseAsXDPPacket_XDP(
     CFX_XMLNode* pXMLDocumentNode) {
-  const PacketInfo* packet = GetPacketByIndex(XFA_PacketType::Xdp);
-  if (!MatchNodeName(pXMLDocumentNode, packet->name, packet->uri,
-                     packet->flags)) {
+  XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Xdp);
+  if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.flags))
     return nullptr;
-  }
 
   CXFA_Node* pXFARootNode =
       m_pFactory->CreateNode(XFA_PacketType::Xdp, XFA_Element::Xfa);
@@ -426,15 +388,16 @@
 
   CFX_XMLNode* pXMLConfigDOMRoot = nullptr;
   CXFA_Node* pXFAConfigDOMRoot = nullptr;
-  const PacketInfo* config_packet_info =
-      GetPacketByIndex(XFA_PacketType::Config);
+  XFA_PACKETINFO config_packet = XFA_GetPacketByIndex(XFA_PacketType::Config);
   for (CFX_XMLNode* pChildItem = pXMLDocumentNode->GetFirstChild(); pChildItem;
        pChildItem = pChildItem->GetNextSibling()) {
-    if (!MatchNodeName(pChildItem, config_packet_info->name,
-                       config_packet_info->uri, config_packet_info->flags)) {
+    if (!MatchNodeName(pChildItem, config_packet.name, config_packet.uri,
+                       config_packet.flags)) {
       continue;
     }
-    if (pXFARootNode->GetFirstChildByName(config_packet_info->hash))
+    // TODO(tsepez): make GetFirstChildByName() take a name.
+    uint32_t hash = FX_HashCode_GetW(config_packet.name, false);
+    if (pXFARootNode->GetFirstChildByName(hash))
       return nullptr;
 
     pXMLConfigDOMRoot = pChildItem;
@@ -453,16 +416,16 @@
       continue;
 
     WideString wsPacketName = pElement->GetLocalTagName();
-    const PacketInfo* pPacketInfo =
-        GetPacketByName(wsPacketName.AsStringView());
-    if (pPacketInfo && pPacketInfo->uri) {
-      if (!MatchNodeName(pElement, pPacketInfo->name, pPacketInfo->uri,
-                         pPacketInfo->flags)) {
-        pPacketInfo = nullptr;
-      }
+    Optional<XFA_PACKETINFO> packet_info =
+        XFA_GetPacketByName(wsPacketName.AsStringView());
+    if (packet_info.has_value() && packet_info.value().uri &&
+        !MatchNodeName(pElement, packet_info.value().name,
+                       packet_info.value().uri, packet_info.value().flags)) {
+      packet_info = {};
     }
-    XFA_PacketType ePacket =
-        pPacketInfo ? pPacketInfo->packet_type : XFA_PacketType::User;
+    XFA_PacketType ePacket = XFA_PacketType::User;
+    if (packet_info.has_value())
+      ePacket = packet_info.value().packet_type;
     if (ePacket == XFA_PacketType::Xdp)
       continue;
     if (ePacket == XFA_PacketType::Datasets) {
@@ -488,9 +451,10 @@
     } else {
       CXFA_Node* pPacketNode = ParseAsXDPPacket(pElement, ePacket);
       if (pPacketNode) {
-        if (pPacketInfo &&
-            (pPacketInfo->flags & XFA_XDPPACKET_FLAGS_SUPPORTONE) &&
-            pXFARootNode->GetFirstChildByName(pPacketInfo->hash)) {
+        if (packet_info.has_value() &&
+            (packet_info.value().flags & XFA_XDPPACKET_FLAGS_SUPPORTONE) &&
+            pXFARootNode->GetFirstChildByName(
+                FX_HashCode_GetW(packet_info.value().name, false))) {
           return nullptr;
         }
         pXFARootNode->InsertChild(pPacketNode, nullptr);
@@ -521,17 +485,16 @@
 
 CXFA_Node* CXFA_DocumentParser::ParseAsXDPPacket_Config(
     CFX_XMLNode* pXMLDocumentNode) {
-  const PacketInfo* packet = GetPacketByIndex(XFA_PacketType::Config);
-  if (!MatchNodeName(pXMLDocumentNode, packet->name, packet->uri,
-                     packet->flags)) {
+  XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Config);
+  if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.flags))
     return nullptr;
-  }
+
   CXFA_Node* pNode =
       m_pFactory->CreateNode(XFA_PacketType::Config, XFA_Element::Config);
   if (!pNode)
     return nullptr;
 
-  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet->name, false, false);
+  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet.name, false, false);
   if (!NormalLoader(pNode, pXMLDocumentNode, XFA_PacketType::Config, true))
     return nullptr;
 
@@ -541,18 +504,16 @@
 
 CXFA_Node* CXFA_DocumentParser::ParseAsXDPPacket_Template(
     CFX_XMLNode* pXMLDocumentNode) {
-  const PacketInfo* packet = GetPacketByIndex(XFA_PacketType::Template);
-  if (!MatchNodeName(pXMLDocumentNode, packet->name, packet->uri,
-                     packet->flags)) {
+  XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Template);
+  if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.flags))
     return nullptr;
-  }
 
   CXFA_Node* pNode =
       m_pFactory->CreateNode(XFA_PacketType::Template, XFA_Element::Template);
   if (!pNode)
     return nullptr;
 
-  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet->name, false, false);
+  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet.name, false, false);
 
   CFX_XMLElement* pXMLDocumentElement = ToXMLElement(pXMLDocumentNode);
   WideString wsNamespaceURI = pXMLDocumentElement->GetNamespaceURI();
@@ -570,18 +531,16 @@
 
 CXFA_Node* CXFA_DocumentParser::ParseAsXDPPacket_Form(
     CFX_XMLNode* pXMLDocumentNode) {
-  const PacketInfo* packet = GetPacketByIndex(XFA_PacketType::Form);
-  if (!MatchNodeName(pXMLDocumentNode, packet->name, packet->uri,
-                     packet->flags)) {
+  XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Form);
+  if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.flags))
     return nullptr;
-  }
 
   CXFA_Node* pNode =
       m_pFactory->CreateNode(XFA_PacketType::Form, XFA_Element::Form);
   if (!pNode)
     return nullptr;
 
-  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet->name, false, false);
+  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet.name, false, false);
   CXFA_Template* pTemplateRoot =
       m_pRootNode->GetFirstChildByClass<CXFA_Template>(XFA_Element::Template);
   CXFA_Subform* pTemplateChosen =
@@ -604,16 +563,15 @@
 
 CXFA_Node* CXFA_DocumentParser::ParseAsXDPPacket_Data(
     CFX_XMLNode* pXMLDocumentNode) {
+  XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Datasets);
   CFX_XMLNode* pDatasetsXMLNode = GetDataSetsFromXDP(pXMLDocumentNode);
-  const PacketInfo* packet = GetPacketByIndex(XFA_PacketType::Datasets);
   if (pDatasetsXMLNode) {
     CXFA_Node* pNode = m_pFactory->CreateNode(XFA_PacketType::Datasets,
                                               XFA_Element::DataModel);
     if (!pNode)
       return nullptr;
 
-    pNode->JSObject()->SetCData(XFA_Attribute::Name, packet->name, false,
-                                false);
+    pNode->JSObject()->SetCData(XFA_Attribute::Name, packet.name, false, false);
     if (!DataLoader(pNode, pDatasetsXMLNode, false))
       return nullptr;
 
@@ -622,7 +580,7 @@
   }
 
   CFX_XMLNode* pDataXMLNode = nullptr;
-  if (MatchNodeName(pXMLDocumentNode, L"data", packet->uri, packet->flags)) {
+  if (MatchNodeName(pXMLDocumentNode, L"data", packet.uri, packet.flags)) {
     ToXMLElement(pXMLDocumentNode)->RemoveAttribute(L"xmlns:xfa");
     pDataXMLNode = pXMLDocumentNode;
   } else {
@@ -660,17 +618,15 @@
     CFX_XMLNode* pXMLDocumentNode,
     XFA_PacketType packet_type,
     XFA_Element element) {
-  const PacketInfo* packet = GetPacketByIndex(packet_type);
-  if (!MatchNodeName(pXMLDocumentNode, packet->name, packet->uri,
-                     packet->flags)) {
+  XFA_PACKETINFO packet = XFA_GetPacketByIndex(packet_type);
+  if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.flags))
     return nullptr;
-  }
 
   CXFA_Node* pNode = m_pFactory->CreateNode(packet_type, element);
   if (!pNode)
     return nullptr;
 
-  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet->name, false, false);
+  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet.name, false, false);
   if (!NormalLoader(pNode, pXMLDocumentNode, packet_type, true))
     return nullptr;
 
@@ -680,9 +636,8 @@
 
 CXFA_Node* CXFA_DocumentParser::ParseAsXDPPacket_Xdc(
     CFX_XMLNode* pXMLDocumentNode) {
-  const PacketInfo* packet = GetPacketByIndex(XFA_PacketType::Xdc);
-  if (!MatchNodeName(pXMLDocumentNode, packet->name, packet->uri,
-                     packet->flags))
+  XFA_PACKETINFO packet = XFA_GetPacketByIndex(XFA_PacketType::Xdc);
+  if (!MatchNodeName(pXMLDocumentNode, packet.name, packet.uri, packet.flags))
     return nullptr;
 
   CXFA_Node* pNode =
@@ -690,7 +645,7 @@
   if (!pNode)
     return nullptr;
 
-  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet->name, false, false);
+  pNode->JSObject()->SetCData(XFA_Attribute::Name, packet.name, false, false);
   pNode->SetXMLMappingNode(pXMLDocumentNode);
   return pNode;
 }
diff --git a/xfa/fxfa/parser/xfa_basic_data.cpp b/xfa/fxfa/parser/xfa_basic_data.cpp
index 6c53325..655c11f 100644
--- a/xfa/fxfa/parser/xfa_basic_data.cpp
+++ b/xfa/fxfa/parser/xfa_basic_data.cpp
@@ -153,6 +153,23 @@
 
 namespace {
 
+struct PacketRecord {
+  uint32_t hash;
+  const wchar_t* name;
+  XFA_PacketType packet_type;
+  const wchar_t* uri;
+  uint32_t flags;
+};
+
+const PacketRecord g_PacketTable[] = {
+#undef PCKT____
+#define PCKT____(a, b, c, d, e, f) \
+  {a, L##b, XFA_PacketType::c, d,  \
+   XFA_XDPPACKET_FLAGS_##e | XFA_XDPPACKET_FLAGS_##f},
+#include "xfa/fxfa/parser/packets.inc"
+#undef PCKT____
+};
+
 struct ElementRecord {
   uint32_t hash;  // Hashed as wide string.
   XFA_Element element;
@@ -211,6 +228,21 @@
 
 }  // namespace
 
+XFA_PACKETINFO XFA_GetPacketByIndex(XFA_PacketType ePacket) {
+  const PacketRecord* pRecord = &g_PacketTable[static_cast<uint8_t>(ePacket)];
+  return {pRecord->name, pRecord->packet_type, pRecord->uri, pRecord->flags};
+}
+
+Optional<XFA_PACKETINFO> XFA_GetPacketByName(const WideStringView& wsName) {
+  uint32_t hash = FX_HashCode_GetW(wsName, false);
+  auto* elem = std::lower_bound(
+      std::begin(g_PacketTable), std::end(g_PacketTable), hash,
+      [](const PacketRecord& a, uint32_t hash) { return a.hash < hash; });
+  if (elem != std::end(g_PacketTable) && elem->name == wsName)
+    return XFA_GetPacketByIndex(elem->packet_type);
+  return {};
+}
+
 ByteStringView XFA_ElementToName(XFA_Element elem) {
   return g_ElementTable[static_cast<size_t>(elem)].name;
 }
diff --git a/xfa/fxfa/parser/xfa_basic_data.h b/xfa/fxfa/parser/xfa_basic_data.h
index d17e512..da892d6 100644
--- a/xfa/fxfa/parser/xfa_basic_data.h
+++ b/xfa/fxfa/parser/xfa_basic_data.h
@@ -14,6 +14,13 @@
 #include "third_party/base/optional.h"
 #include "xfa/fxfa/fxfa_basic.h"
 
+struct XFA_PACKETINFO {
+  const wchar_t* name;
+  XFA_PacketType packet_type;
+  const wchar_t* uri;
+  uint32_t flags;
+};
+
 struct XFA_ATTRIBUTEINFO {
   XFA_Attribute attribute;
   XFA_ScriptType eValueType;
@@ -25,6 +32,9 @@
   XFA_ATTRIBUTE_CALLBACK callback = nullptr;
 };
 
+XFA_PACKETINFO XFA_GetPacketByIndex(XFA_PacketType ePacket);
+Optional<XFA_PACKETINFO> XFA_GetPacketByName(const WideStringView& wsName);
+
 ByteStringView XFA_ElementToName(XFA_Element elem);
 XFA_Element XFA_GetElementByName(const WideString& name);
 
diff --git a/xfa/fxfa/parser/xfa_basic_data_unittest.cpp b/xfa/fxfa/parser/xfa_basic_data_unittest.cpp
index d2a2872..b9307d5 100644
--- a/xfa/fxfa/parser/xfa_basic_data_unittest.cpp
+++ b/xfa/fxfa/parser/xfa_basic_data_unittest.cpp
@@ -8,6 +8,30 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 
+TEST(XFABasicDataTest, GetPacketByName) {
+  Optional<XFA_PACKETINFO> result = XFA_GetPacketByName(L"");
+  EXPECT_FALSE(result.has_value());
+
+  result = XFA_GetPacketByName(L"nonesuch");
+  EXPECT_FALSE(result.has_value());
+
+  result = XFA_GetPacketByName(L"datasets");
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(XFA_PacketType::Datasets, result.value().packet_type);
+
+  result = XFA_GetPacketByName(L"sourceSet");
+  ASSERT_TRUE(result.has_value());
+  EXPECT_EQ(XFA_PacketType::SourceSet, result.value().packet_type);
+}
+
+TEST(XFABasicDataTest, PacketToName) {
+  XFA_PACKETINFO result = XFA_GetPacketByIndex(XFA_PacketType::Datasets);
+  EXPECT_STREQ(L"datasets", result.name);
+
+  result = XFA_GetPacketByIndex(XFA_PacketType::ConnectionSet);
+  EXPECT_STREQ(L"connectionSet", result.name);
+}
+
 TEST(XFABasicDataTest, GetElementByName) {
   EXPECT_EQ(XFA_Element::Unknown, XFA_GetElementByName(L""));
   EXPECT_EQ(XFA_Element::Unknown, XFA_GetElementByName(L"nonesuch"));
@@ -46,7 +70,7 @@
   EXPECT_EQ(XFA_Attribute::DecipherOnly, result.value().attribute);
 }
 
-TEST(XFABasicDataTest, AttributeToNamee) {
+TEST(XFABasicDataTest, AttributeToName) {
   EXPECT_EQ("spaceBelow", XFA_AttributeToName(XFA_Attribute::SpaceBelow));
   EXPECT_EQ("decipherOnly", XFA_AttributeToName(XFA_Attribute::DecipherOnly));
 }