Продвинутые View над типизированными массивами 🤯🙈



Типизированные массивы и ArrayBuffer невероятно могущественные примитивы в JS. Они позволяют работать с бинарными типами данных, создавать новые примитивные типы (BCD числа, Int64/128, UTF-8 строки и т.д.), структуры данных (матрицы, эффективные сбалансированные деревья и т.д.), создать "свою" память для различных низкоуровневых задач.



Типизированные массивы могут буквально как на стероидах расширить возможности нашего JS. Одним словом - красота. С другой стороны, по началу может сбивать с толку, что типизированные массивы поддерживают только числовые типы.



Но проблема ли это? Кто хотя бы поверхностно изучал CS знает, что любые данные на компьютере фактически являются потоком чисел, или, просто, одним очень большим числом. А значит нам не составит труда расширить возможности типизированных массивов добавив к ним поддержку новых типов.



Вообще, сами типизированные массивы являются просто проекцией (view) над ArrayBuffer или SharedArrayBuffer. ArrayBuffer представляет собой поток байт без какого либо смысла и семантики. А типизированные массивы вводят контракт, как именно надо работать с этими байтами и как их интерпретировать.



Приведем пример.



const memory = new ArrayBuffer(1);



// Работаем с байтами с контрактом, что это U8

const arr1 = new Uint8Array(memory);



arr1[0] = 255;



// Работаем с байтами с контрактом, что это I8

const arr2 = new Int8Array(memory);



// Если смотреть на число 255 как на I8, то получается -1, т.к. крайне левый бит определяет знак

console.log(arr2[0]); // -1;



console.log(arr1[0]); // 255



console.log(arr1.buffer == arr2.buffer);




Что и следовало доказать. Типизированные массивы - это просто контракт над тем, как работать с байтами, а не какая-то "особенная" структура данных.



Более того, массивы являются фундаментальной структурой данных, а значит на их основе строятся многие другие структуры данных. Загляните под капот вектору или хеш-таблице и найдете там массив.



Вооружившись этими знаниями давайте сделаем следующее.



// Кортеж для задания RGB цвета

const Color = Structure.Tuple(Structure.U8, Structure.U8, Structure.U8);



// Структура

const Skills = new Structure({

singing: Structure.U8,

dancing: Structure.U8,

fighting: Structure.U8

});



// Структура содержащая в себе другие структуры

const Person = new Structure({

firstName: Structure.String('ASCII', 3),

lastName: Structure.String('ASCII', 4),

age: Structure.U(7),

skills: Skills,

color: Color

});



// Массив на 10 элементов для структур типа Person

const Persons = Structure.Array(Person, 10);



// Создаем массив типа Persons

const persons = Persons.new();



// Данный объект будет закодирован в поток байт согласно схеме и вставлен в ArrayBuffer

// которым владеет persons

persons.push({

firstName: 'Bob',

lastName: 'King',

age: 42,

skills: {singing: 100, dancing: 100, fighting: 50},

color: [255, 0, 200]

});



persons.push(bob);



// Извлечение данных из массива и полей структуры фактически чтение байт из ArrayBuffer их отображение на типы JS

console.log(persons.at(0).skills.singing); // 100



console.log(persons.buffer); // ArrayBuffer(...)




Помедитируем. Мы только что добавил в JS поддержку кортежей, структур, новых числовых типов и ASCII строк. Более того, мы можем делать массив таких вот структур. 🚀