Search code examples
pythonlistslicesequencememoryview

How can I access a buffer or sequence in an unusual order?


I have a block of memory representing RGB values for a 8 row x 32 column matrix. When writing into this block of memory, it would be convenient to treat it as properly ordered. When reading from the memory (and pushing into a peripheral), due to the way the electronics are wired, every other column is reversed.

So the "correct" reading order due to the electronics should be something like:

0 Red, 0 Green, 0 Blue, 1 Red, 1 Green, 1 Blue... 7 Red, 7 Green, 7 Blue, 15 Red, 15 Green, 15 Blue, 14 Red ... 8 Blue, 16 Red ... 23 Blue ... 31 -> 24, 32 -> 39, etc.

(As a note, I can't just reverse the entire sequence of bytes 25-48, because then RGB would be in the wrong order).

I'm trying to avoid duplicating the memory and I want as fast a read as possible - I'm hoping to avoid interpreted address translation.

Is there any way to construct a sequence of memoryviews that can be addressed/sliced (read and write) like a single block? Or a clever sequence slicing that could reverse every other block of 24 bytes, and then every block of 3 bytes within that (to fix the RGB ordering)?

I'd like to learn about such methods if they exist, but recognize that they still might not be the right tools for the job. In the absence of clever methods requested above (or even in the event they exist but there's something better), how else might I address/organize my data so I can access it (read/write) effectively for both programming and display?


Solution

  • The memoryview object had a bunch of restrictions on what can be done with it, so using a wrapper for it to handle iterations/indexing in this "snaking" style is the best I could come up with, which would look something like this below. In this example the memory itself doesn't have to be duplicated or rewritten.

    import array
    
    
    class SnakeMemoryViewWrapper:
        def __init__(self, memview):
            self.memview = memview
            self.index = 0
    
        def __iter__(self):
            self.index = 0
            return self
    
        def __next__(self):
            if self.index == len(self.memview):
                raise StopIteration
    
            if int(self.index // 8) % 2:
                value = self.memview[(self.index // 8 + 1) * 8 - self.index % 8 - 1]
            else:
                value = self.memview[self.index]
            self.index += 1
            return value
    
        def __getitem__(self, index):
            if int(index // 8) % 2:
                index = (index // 8 + 1) * 8 - index % 8 - 1
            return self.memview[index]
    
    
    arr = array.array("i", range(32))
    memview = memoryview(arr)
    
    sm = SnakeMemoryViewWrapper(memview)
    
    print(list(sm))
    # [0, 1, 2, 3, 4, 5, 6, 7, 15, 14, 13, 12, 11, 10, 9, 8, 16, 17, 18, 19, 20, 21, 22, 23, 31, 30, 29, 28, 27, 26, 25, 24]
    print(sm[0], sm[8], sm[16], sm[24])
    # 0 15 16 31
    

    I'm not super clear on your exact needs, but I'm hoping a solution similar to this would work for you, the memoryview object itself is untouched, and accessible in this class, so you'd be able to manipulate that directly when needed.