public AddressSetView getUndefinedRanges(AddressSetView set, boolean initializedMemoryOnly,
			TaskMonitor monitor) throws CancelledException {
		lock.acquire();
		try {

			monitor = TaskMonitor.dummyIfNull(monitor);
			Memory mem = program.getMemory();
			AddressSet searchSet;
			if (set == null) {
				searchSet = new AddressSet(
					initializedMemoryOnly ? mem.getLoadedAndInitializedAddressSet() : mem);
			}
			else if (set.isEmpty()) {
				return set;
			}
			else {
				searchSet = new AddressSet(set);
				searchSet = searchSet.intersect(
					initializedMemoryOnly ? mem.getLoadedAndInitializedAddressSet() : mem);
			}
			AddressSet resultSet = new AddressSet();

			for (AddressRange range : searchSet.getAddressRanges()) {

				monitor.checkCancelled();

				Address start = range.getMinAddress();
				Address rangeMax = range.getMaxAddress();

				if (start.getAddressSpace().getMinAddress().compareTo(start) < 0) {
					// Adjust start if offcut
					try {
						Instruction instr = getInstructionBefore(start);
						if (instr != null && instr.contains(start)) {
							start = instr.getMaxAddress().addNoWrap(1);
							if (start.compareTo(rangeMax) > 0) {
								continue; // next range
							}
						}
						else {
							Data data = getDefinedDataBefore(start);
							if (data != null && data.contains(start)) {
								start = data.getMaxAddress().addNoWrap(1);
								if (start.compareTo(rangeMax) > 0) {
									continue; // next range
								}
							}
						}
					}
					catch (AddressOverflowException e) {
						continue; // next range
					}
				}

				RecordIterator instIter = instAdapter.getRecords(start, rangeMax, true);
				RecordIterator dataIter = dataAdapter.getRecords(start, rangeMax, true);

				Address nextInstAddr = null;
				Address nextDataAddr = null;
				Address nextInstEndAddr = null;
				Address nextDataEndAddr = null;

				while (true) {

					if (nextInstAddr == null && instIter.hasNext()) {
						DBRecord nextInstRec = instIter.next();
						nextInstAddr = addrMap.decodeAddress(nextInstRec.getKey());
						nextInstEndAddr = nextInstAddr;
						int protoID = nextInstRec.getIntValue(InstDBAdapter.PROTO_ID_COL);
						InstructionPrototype proto = protoMgr.getPrototype(protoID);
						int len;
						if (proto != null) {
							len = InstructionDB.getLength(proto,
								nextInstRec.getByteValue(InstDBAdapter.FLAGS_COL));
						}
						else {
							len = nextInstAddr.getAddressSpace().getAddressableUnitSize();
						}
						if (len > 1) {
							try {
								nextInstEndAddr = nextInstAddr.addNoWrap(len - 1);
							}
							catch (AddressOverflowException e) {
								nextInstEndAddr = rangeMax;
							}
						}
					}

					if (nextDataAddr == null && dataIter.hasNext()) {
						DBRecord nextDataRec = dataIter.next();
						nextDataAddr = addrMap.decodeAddress(nextDataRec.getKey());
						nextDataEndAddr = nextDataAddr;
						DataDB data = getDataDB(nextDataRec);
						int len = data.getLength();
						if (len > 1) {
							try {
								nextDataEndAddr = nextDataAddr.addNoWrap(len - 1);
							}
							catch (AddressOverflowException e) {
								nextDataEndAddr = rangeMax;
							}
						}
					}

					if (nextInstAddr == null && nextDataAddr == null) {
						if (start.compareTo(rangeMax) <= 0) {
							resultSet.addRange(start, rangeMax);
						}
						break; // next range
					}

					// Decide which code unit is next: instruction or data and consume it
					Address nextDefinedAddr = nextInstAddr;
					Address nextDefinedEndAddr = nextInstEndAddr;
					if (nextInstAddr == null) {
						nextDefinedAddr = nextDataAddr;
						nextDefinedEndAddr = nextDataEndAddr;
					}
					if (nextInstAddr != null && nextDataAddr != null &&
						nextInstAddr.compareTo(nextDataAddr) > 0) {
						nextDefinedAddr = nextDataAddr;
						nextDefinedEndAddr = nextDataEndAddr;
						nextDataAddr = null; // consumed
					}
					else if (nextDefinedAddr == nextInstAddr) {
						nextInstAddr = null; // consumed
					}
					else {
						nextDataAddr = null; // consumed
					}

					//
					if (start.compareTo(nextDefinedAddr) < 0) {
						resultSet.addRange(start, nextDefinedAddr.subtract(1));
					}

					if (nextDefinedEndAddr.compareTo(rangeMax) >= 0) {
						break; // next range
					}
					start = nextDefinedEndAddr.add(1);
				}
			}
			return resultSet;
		}
		catch (IOException e) {
			dbError(e);
		}
		finally {
			lock.release();
		}
		return null; // will not happen
	}
Returns the defined data whose min address is less than or equal to the specified address 
and whose max address is greater than or equal to the specified address.
<pre>{@literal
data.minAddress() <= addr <= data.maxAddress()
}</pre>

@param addr the address to be contained
@return the defined data containing the address, null if a defined data does not exist
Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/code/CodeManager.java