public void mapTypes(PdbApplicator applicator) throws CancelledException {
		Objects.requireNonNull(applicator, "applicator cannot be null");

		TypeProgramInterface typeProgramInterface = applicator.getPdb().getTypeProgramInterface();
		if (typeProgramInterface == null) {
			return;
		}

		// Purely using these LinkedLists for FIFO queues for matching next available forward
		//  reference with next available definition.  Need a separate FIFO for composites vs.
		//  enums, as a label can be used for both a composite and for an enum.  But we do not
		//  need four FIFOs (which would be one each for forward reference and definition for
		//  each of composites and enums) because the fwdref and def use the same FIFO.
		//  Each FIFO will either have all forward references or all definitions at any given
		//  time.  If, for example it only has forward references and another forward reference
		//  is seen in the input stream, it is just pushed onto the FIFO, but if a definition is
		//  seen next in the input stream, then the that definition gets matched with the first
		//  record in the FIFO (which gets peeled off the FIFO).  This continues as long as
		//  more definitions come in the input stream.  If the FIFO empties of forward references,
		//  and another definition is found in the input stream, then the FIFO starts storing
		//  definitions instead.
		// All we need for the Deque is a simple FIFO.
		Map<SymbolPath, Deque<NumFwdRef>> compositeFIFOsByPath = new HashMap<>();
		Map<SymbolPath, Deque<NumFwdRef>> enumFIFOsByPath = new HashMap<>();

		// Map is used for combo of Composites and Enums, but the FIFOs above had to be
		//  separated (or get complicated in other ways by adding more to the FIFO values).
		map = new HashMap<>();

		int indexLimit = typeProgramInterface.getTypeIndexMaxExclusive();
		int indexNumber = typeProgramInterface.getTypeIndexMin();
		TaskMonitor monitor = applicator.getMonitor();
		monitor.initialize(indexLimit - indexNumber);
		monitor.setMessage("PDB: Mapping Complex Types...");
		while (indexNumber < indexLimit) {
			monitor.checkCancelled();
			// Getting explicit type with no worrying about remapping of TYPE to ITEM or ITEM
			// to TYPE as we could get using applicator.getPdb().getTypeRecord(recordNumber)
			// where recordNumber is a RecordNumber.  This is because we are not expecting
			// a remap for Complex types.
			AbstractMsType type = typeProgramInterface.getRandomAccessRecord(indexNumber);
			if (type instanceof AbstractCompositeMsType compositeType) {
				mapComplexTypesByPath(compositeFIFOsByPath, indexNumber, compositeType);
			}
			else if (type instanceof AbstractEnumMsType enumType) {
				mapComplexTypesByPath(enumFIFOsByPath, indexNumber, enumType);
			}
			indexNumber++;
			monitor.incrementProgress(1);
		}
	}
Returns map to alternate record number or argument record number if no map.  Result is
 RecordNumber of alternative record for the complex type.  Map is of fwdref to definition
 RecordNumbers.  The fwdref number is generally, but not always, the lower number
@param recordNumber the record number for which to do the lookup
@return the mapped record number or the original record number if no mapped entry
Ghidra/Features/PDB/src/main/java/ghidra/app/util/pdb/pdbapplicator/ComplexTypeMapper.java