protected void doSetState(long snap, Address start, Address end, TraceMemoryState state) {
		if (state == null) {
			throw new NullPointerException();
		}
		var l = new Object() {
			boolean changed;
		};
		new AddressRangeMapSetter<Entry<TraceAddressSnapRange, TraceMemoryState>, TraceMemoryState>() {
			@Override
			protected AddressRange getRange(Entry<TraceAddressSnapRange, TraceMemoryState> entry) {
				return entry.getKey().getRange();
			}

			@Override
			protected TraceMemoryState getValue(
					Entry<TraceAddressSnapRange, TraceMemoryState> entry) {
				return entry.getValue();
			}

			@Override
			protected void remove(Entry<TraceAddressSnapRange, TraceMemoryState> entry) {
				stateMapSpace.remove(entry);
			}

			@Override
			protected Iterable<Entry<TraceAddressSnapRange, TraceMemoryState>> getIntersecting(
					Address lower, Address upper) {
				return stateMapSpace
						.reduce(TraceAddressSnapRangeQuery.intersecting(lower, upper, snap, snap))
						.entries();
			}

			@Override
			protected Entry<TraceAddressSnapRange, TraceMemoryState> put(AddressRange range,
					TraceMemoryState value) {
				// This should not get called if the range is already the desired state
				l.changed = true;
				if (value != TraceMemoryState.UNKNOWN) {
					stateMapSpace.put(new ImmutableTraceAddressSnapRange(range, snap), value);
				}
				return null; // Don't need to return it
			}
		}.set(start, end, state);

		if (l.changed) {
			trace.setChanged(new TraceChangeRecord<>(TraceEvents.BYTES_STATE_CHANGED, this,
				new ImmutableTraceAddressSnapRange(start, end, snap, snap), state));
		}
	}
Implements {@link TraceMemorySpace} using a database-backed copy-on-write store.
Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/memory/DBTraceMemorySpace.java