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
Terms of Service

Privacy Policy

Cookie Policy