Proxies
While writing up memory mapped peripherals
I needed to implement views on Array
instances. In the
QVM implementation
memory is represented using a Uint32Array
, which natively supports views,
but in the series examples here we’ve stuck with arrays for simplicity. I didn’t
want to create a cumbersome wrapper that didn’t support the natural indexing
notation of arrays, and so I stumbled upon Proxy
.
Here we wrap a given array in a proxy that translates array indices
from the “view space” (ranging from 0
to the length of the view) to
the underlying “array space”. The view begins at index base
and is
count
long, so when you read index i
from the view, you are
really reading index base + i
from the underlying array.
function view<T>(array: T[], base: number, count: number): T[] {
return new Proxy(array, {
get: (object, prop) => {
// Translate view[n] to array[base+n].
const index = parseInt(prop as any);
if(!isNaN(index)){
if(index >= 0 && index < count){
return object[base + index];
}
return;
}
// Return the view's length, not the underlying array's.
if(prop === 'length'){
return count;
}
return object[prop as any];
},
set: (object, prop, value) => {
// Translate view[n] to array[base+n].
const index = parseInt(prop as any);
if(!isNaN(index)){
if(index >= 0 && index < count){
object[base + index] = value;
return true;
}
return false;
}
// Disallow changing the view's length.
if(prop === 'length'){
return false;
}
object[prop as any] = value;
return true;
},
});
}
This view isn’t “safe”, of course – you can easily call e.g. view.forEach(...)
to read all
of the underlying array’s data. But it is convenient and serves the purpose of
translating between view space and array space well enough.