Sequence replacement operator?

Working on a little utility to generate 65C02 ROM images, I had this thought that it would be nice to be able to express a sub-array replacement as an array assignment, something like this:

var rom = [UInt8](repeating: 0xea, count: 32768)
rom[0x6000] <= [ 0x01, 0x02, 0x03 ]

The result of the above code would be equivalent to:

rom[0x6000] = 0x01
rom[0x6001] = 0x02
rom[0x6002] = 0x03

But I couldn't think of a way to implement this fictitious <= operator. I can see a way to do it like this:

rom <= (0x6000, [ 0x01, 0x02, 0x03 ])

but that's not really any better than replaceSubrange().

It may be silly to want to do this, it was just an idea I had, and made me wonder what was possible in Swift.

(Iā€™m also not sure how to implement something like this for generics like Array).

It might make more sense to make this a labelled subscript, giving you something like:

rom[from: 0x6000] = [0x01, 0x02, 0x02]

You'll have to decide some sensible behaviour for the subscript getter though (rom[from: 0x6000] == ?) and decide what to do at the bounds of the array, etc.

1 Like

That seems to be reasonable, but I wish I didn't have to define the getter.

extension
Data
{
	subscript(at inIndex: Int)
		-> [UInt8]
	{
		get
		{
			let b = [UInt8](self)
			return Array(b[inIndex..<b.count])
		}
		
		set(inValue)
		{
			let r = inIndex ..< (inIndex + inValue.count)
			self.replaceSubrange(r, with: inValue)
		}
	}
}

var d1 = Data(repeating: 0xea, count: 32768)
d1[at: 0x6000] = [UInt8]([0x1, 0x2, 0x3])

Now I gotta figure out how to avoid the [UInt8]() cast when using it.

You sure you need [UInt8]()?

d1[at: 0x6000] = [0x1, 0x2, 0x3]

This works fine for me.

If you really want to use the operator, it is possible

struct EditableArraySlice<T> {
	var buffer: [T]
	var range: Range<Int>
}

class ROM {
	var buffer: [UInt8] = [UInt8](repeating: 0, count: 1024 * 1024)
	subscript(index: Int) -> EditableArraySlice<UInt8> {
		get {
			return EditableArraySlice(buffer: buffer, range: index..<buffer.endIndex)
		}
		set {
			precondition(newValue.buffer.count == buffer.count)
			buffer = newValue.buffer
		}
	}
}

func <=<S: Sequence>(lhs: inout EditableArraySlice<S.Element>, rhs: S) {
	var iter = rhs.makeIterator()
	for index in lhs.range {
		guard let next = iter.next() else { break }
		lhs.buffer[index] = next
	}
}

Note: The above code causes the array to be copied each time you use the operator. If you're willing to use the not-yet-stable _modify, you can remove the array copy by replacing the set with this:

_modify {
	var tmp = EditableArraySlice(buffer: buffer, range: index..<buffer.endIndex)
	let oldCount = buffer.count
	buffer = []
	yield &tmp
	precondition(tmp.buffer.count == oldCount)
	buffer = tmp.buffer
}
1 Like