{"id":25925302,"url":"https://github.com/umamiappearance/mutarjs","last_synced_at":"2025-10-29T23:37:45.133Z","repository":{"id":57305717,"uuid":"412458732","full_name":"UmamiAppearance/MutarJS","owner":"UmamiAppearance","description":"Toolkit and constructor to manipulate typed arrays.","archived":false,"fork":false,"pushed_at":"2023-07-05T20:03:24.000Z","size":473,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-27T18:12:49.318Z","etag":null,"topics":["array","binary","bytes","typed-array","uint8array"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/UmamiAppearance.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-10-01T12:30:03.000Z","updated_at":"2023-01-31T19:11:30.000Z","dependencies_parsed_at":"2024-10-22T19:32:09.383Z","dependency_job_id":null,"html_url":"https://github.com/UmamiAppearance/MutarJS","commit_stats":{"total_commits":135,"total_committers":3,"mean_commits":45.0,"dds":"0.029629629629629672","last_synced_commit":"76a2ee759ddc673901c073d548b5d30faf4547d6"},"previous_names":["umamiappearance/mutabletypedarrayjs"],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/UmamiAppearance%2FMutarJS","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/UmamiAppearance%2FMutarJS/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/UmamiAppearance%2FMutarJS/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/UmamiAppearance%2FMutarJS/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/UmamiAppearance","download_url":"https://codeload.github.com/UmamiAppearance/MutarJS/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241723132,"owners_count":20009412,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["array","binary","bytes","typed-array","uint8array"],"created_at":"2025-03-03T18:47:53.958Z","updated_at":"2025-10-29T23:37:45.041Z","avatar_url":"https://github.com/UmamiAppearance.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Mutar\n**Mu**table**T**yped**Ar**ray - _/mu'taɾ/, spanish for to change, mutate_\n\n[![License](https://img.shields.io/github/license/UmamiAppearance/MutarJS?color=239911\u0026style=for-the-badge)](./LICENSE)\n[![npm](https://img.shields.io/npm/v/mutar?color=%23009911\u0026style=for-the-badge)](https://www.npmjs.com/package/mutar)\n\n\n**Mutar** is a toolkit to interact with typed arrays and modify them (or let's say a kit to emulate modification) and a constructor for a special object. It is a very convenient way to handle binary data. If constructed, the array behaves pretty much as a regular array. You can concatenate, pop, shift, unshift... On top of that the type can be changed from - let's say - Uint8 to Float64. \n\n#### Mutability\nIn reality _TypedArrays_ are not mutable in terms of growing and shrinking. To emulate mutability a new array with the desired properties is created. This comes to a price of course. Every time the array length \"changes\", a new TypedArray is allocated in memory. Keep that in mind when using it, if this is critical to you.\n\n#### Endianness\n**Mutar** objects are designed to be aware of endianness. If not specified, the the endianness of the system is used, which is most likely little endian. Despite this fact, sometimes data (e.g. network related) differ in its endianness. It is possible to store them in a **Mutar** object, interact with it but keep the given byte order. (Values which are added or got are converted to the according endianness).\n\n## Installation\n\n### GitHub\n```sh\ngit clone https://github.com/UmamiAppearance/MutarJS.git\n```\n\n### npm\n```sh\nnmp install mutar\n```\n\n## Builds\nThe GitHub repository has ready to use builds included. You can find them in [dist](https://github.com/UmamiAppearance/MutarJS/tree/main/dist). The npm package comes without pre build files. \n\nFor building you have to run:\n\n```sh\nnpm run build\n``` \n\nEither way you have two builds available ([esm](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) and [iife](https://developer.mozilla.org/en-US/docs/Glossary/IIFE)), plus a minified version of each. \n* ``Mutar.esm.js``\n* ``Mutar.esm.min.js``\n* ``Mutar.iife.js``\n* ``Mutar.iife.min.js``\n\n\n## Importing\n\n### Node\n```js\n// common js\nconst Mutar = require(\"mutar\");\n\n// ES6 import\nimport Mutar from \"mutar\";\n```\n\n### Browser\n```html\n\u003c!-- iife / classic script tag --\u003e\n\u003cscript src=\"Mutar.iife(.min).js\"\u003e\u003c/script\u003e\n```\n\n```js\n// ES6\nimport Mutar from \"./path/Mutar.esm(.min).js\"\n```\n\n## Usage\n\n### Toolkit\nIf you want to work with an existing TypedArray, you can use **Mutar** to analyse and modify it. Let's for example take a Uint32Array which holds the integer **400**. A little remark: If you plan to do a lot of manipulation to a single array, you should strongly consider to use the [Mutar Object](#Object).\n\n```js\nconst Uint32 = new Uint32Array([400]);\n```\nMutar comes with some functions to analyse the given object. (Let us forget for a moment that we exactly know what kind of object it is).\n\n#### Analysis\n```js\nconst Uint32 = new Uint32Array([400]);\nconst regularArray = [400];\n\n// Test if is a TypedArray //\nMutar.isTypedArray(Uint32);                     // -\u003e true\nMutar.isTypedArray(regularArray);               // -\u003e false\n\n// Get the type //\nMutar.getType(Uint32);                          // -\u003e \"Uint32Array\"\n\n\n// Test if array is of type foo //\n\n// test for Uint32Array\nMutar.isTypeOf(Uint32, \"Uint32Array\");          // -\u003e true\n\n// you can also use the Uint32 constructor as input\nMutar.isTypeOf(Uint32, Uint32Array);            // -\u003e true\n\n// test for Int8Array (this time a shortcut for the type)\nMutar.isTypeOf(Uint32, \"Int8\");                 // -\u003e false\n\n// test the \"wrong\" type\nMutar.isTypeOf(regularArray, Uint32Array);      // -\u003e false\n```\n\nLet's now take a look at the fun part: the _modification_.\n\n#### Modification\nOne major focus of Mutar is to be aware of endianness. For simplicity, there is only a small section at the beginning of this chapter, that is going into this feature. The other here introduced functions are not using the opportunity to manipulate endianness, but that doesn't mean that it is not there. Every function, that manipulates the bytes has the possibility to set the integers in either little or big endian. If nothing is specified, the endianness of the system is getting used. \n\n\n```js\n// Again we are taking the Uint32Array of the Integer 400\n// The individual bytes for this number are:\n// [ 144, 1, 0, 0 ] (little endian byte order)\n\nconst Uint32 = new Uint32Array([400]);                      // -\u003e Uint32Array(1)    [ 400 ]\n\n// Easy start: create a copy (clone) of a TypedArray\n// (changes on the clone will not affect the original)\nconst clone = Mutar.clone(Uint32);                          // -\u003e Uint32Array(1)    [ 400 ]\n\n// Change endianness\nMutar.flipEndianness(clone);                                // -\u003e Uint32Array(1)    [ 2415984640 ]\n\n// Determine the endianness of the System\nconst littleEndian = Mutar.SYS_LITTLE_ENDIAN;               // -\u003e true (most likely)\nconst bigEndian = !littleEndian;                            // -\u003e false \n\n// Get the integer at index 0 (little or big endian is getting passed here,\n// which is only done for universal correctness. Just pass true for little\n// and false for big endian)\nMutar.at(Uint32, 0, littleEndian);                          // -\u003e 400\nMutar.at(clone, 0, littleEndian);                           // -\u003e 2415984640\nMutar.at(clone, 0, bigEndian);                              // -\u003e 400\n\n// Set single integers (Uint32 has the systems endianness, so no need to specify)\nMutar.setAt(Uint32, 0, 500);\nMutar.setAt(clone, 0, 500, bigEndian);\n\n// Now let's set back the endianness of the clone and test if it is equal to\n// the original Uint32\nUint32[0] === clone[0]                                      // -\u003e false (500 === 4093706240)\nMutar.flipEndianness(clone);                                // -\u003e Uint32Array(1)    [ 500 ]\nUint32[0] === clone[0]                                      // -\u003e true\n\n// As already mentioned, you have the ability to specify the endianness\n// for almost every function. From now on it is no longer used, everything\n// is done with the systems default endianness. Before we continue, let us \n// set back clone and original to 400.\nUint32[0] = 400;\nclone[0] = 400;\n\n\n// Concatenation of arrays (let's join the original, the clone and a fresh array)\nconst fresh = new Uint16Array([500, 600]);                  // -\u003e Uint16Array(2)    [500, 600]\nMutar.concat(Uint32, clone, fresh);                         // -\u003e TypeError\n\n// Seems like the objects needs to be converted before they fit together.\nlet freshUint32 = Mutar.convert(fresh, \"Uint32\");           // -\u003e int32Array(1)     [ 39322100 ]\n\n// Two Uint16 values fit in one Uint32 (or to be more clear:\n// the underlying buffer has 4 integers [ 244, 1, 88, 2 ],\n// which converts into one Uint32 integer), but this is not\n// what we want in this case.\n// Luckily we can convert in another mode -\u003e \"intMode\"\nfreshUint32 = Mutar.convert(fresh, \"Uint32\", true);         // -\u003e Uint32Array(2)    [500, 600]\n\n// Now we can concat.\nlet concat = Mutar.concat(Uint32, clone, freshUint32);       // -\u003e Uint32Array(4)    [ 400, 400, 500, 600 ]\n\n// If the conversions are that simple as in this case,\n// we can tell the concat function to force conversion\n// on the fly. By literally passing the string \"force\".\n// Also the \"intMode\" can be activated by passing the\n// string (by using \"intMode\", you don't have to pass\n// \"force\" anymore, as the intention is clear, but it\n// doesn't hurt either).\nconcat = Mutar.concat(concat, new Uint16Array([700]), \"intMode\");\n                                                            // -\u003e Uint32Array(4)    [ 400, 400, 500, 600, 700 ]\n\n// Let's take a closer look at the type conversion.\n// The focus is now on the regular mode\n\n// conversion (e.g. BigInt64) \nconst bigInt = Mutar.convert(concat, \"BigInt\");             // -\u003e BigInt64Array(2)  [ 1717986918800n, 2576980378100n, 700n ]\n\n// The original \"concat\" array has a byte length of 20, BingIntArrays\n// byte lengths must be devisable by 8. Mutar is using zero padding to\n// make this fit (zeros are added to the end of the ArrayBuffer or at\n// the beginning for big endian). So when you convert the BigInt back\n// to Uint32 it looks like this.\nlet Uint32fromBigInt = Mutar.convert(bigInt, \"Uint32\");     // -\u003e Uint32Array(5)    [ 400, 400, 500, 600, 700, 0 ]\n\n// To remove this padded zero we can use the \"trim\" function\nUint32fromBigInt = Mutar.trim(Uint32fromBigInt);            // -\u003e Uint32Array(5)    [ 400, 400, 500, 600, 700 ]\n\n// We can get rid of this zero also during conversion by setting\n// the third param \"trim\" to be true.\nUint32fromBigInt = Mutar.convert(bigInt, \"Uint32\", false, true);\n                                                            // -\u003e Uint32Array(5)    [ 400, 400, 500, 600, 700 ]\n\n// Let us now remove the repetitive 400 and position 1\n// (the function returns an array with the new typed array\n// at position 0 and the detached value an position 1)\nlet cleanedUint32, detached;\n[cleanedUint32, detached] = Mutar.detach(Uint32fromBigInt, 1);\n                                                            // -\u003e Uint32Array(4)    [ 400, 500, 600, 700 ], 400\n\n// We can also insert\nlet insertedUint32, newlen;\n[insertedUint32, newlen] = Mutar.insert(cleanedUint32, 2, 550);\n                                                            // -\u003e Uint32Array(5)    [ 400, 500, 550, 600, 700 ], 5\n\n// Pop\nlet poppedUint32, popped;\n[poppedUint32, popped] = Mutar.pop(insertedUint32);         // -\u003e Uint32Array(4)    [ 400, 500, 550, 600 ], 700\n\n// Push\nlet pushedUint32;\n[pushedUint32, newLen] = Mutar.push(poppedUint32, 700, 800);\n                                                            // -\u003e Uint32Array(6)    [ 400, 500, 550, 600, 700, 800 ], 6\n\n// Shift\nlet shiftedUint32, shifted;\n[shiftedUint32, shifted] = Mutar.shift(pushedUint32);       // -\u003e Uint32Array(5)    [ 500, 550, 600, 700, 800 ], 400\n\n// Unshift\nlet unshiftedUint32, newLen;\n[unshiftedUint32, shifted] = Mutar.unshift(shiftedUint32, 300, 400);       \n                                                            // -\u003e Uint32Array(7)    [ 300, 400, 500, 550, 600, 700, 800 ], 7\n\n// Splice\nlet splicedUint32, slicedArr;\n[splicedUint32, slicedArr] = Mutar.splice(unshiftedUint32, 2, 3, 450, 500, 550, 600, 650);\n                                                            // -\u003e Uint32Array(9)    [ 300, 400, 450,\n                                                            //                        500, 550, 600,\n                                                            //                        650, 700, 800 ],\n                                                            //                      [ 500, 550, 600 ]\n\n// Create a Mutar object from a regular typed array\nconst mutarObj = Mutar.from(splicedUint32);\n\n```\n\n### Object\n\nThere are some opportunities for creating a **Mutar** object. One is, as shown right before, by calling the ``Mutar.from`` function. The default way looks like follows: \n\n#### Creating\n```js\n// Passing a regular array, plus the typed array function Uint32Array or string \"Uint32Array\"``\n// or shortcut string \"Uint32\".\nconst mutarObj = new Mutar([300, 400, 450, 500, 550, 600, 650, 700, 800], Uint32Array);\n\n// Passing a typed array (the type must not be specified)\nconst mutarObjFromTA = new Mutar(new Uint32Array([300, 400]));\n\n// But type can be specified. In this case only the values matter, no longer the type of array\nconst mutarObjTAwithType = new Mutar(new Uint32Array([300, 400]), Uint16Array);\n\n// Input can even be a string (which gets converted to a Uint8Array)\nconst mutarObjFromStr = new Mutar(\"Hello World!\");\n\n// Endianness matters (but is always the systems default if not specified)\n// The third parameter of the constructor sets littleEndian of the object\n// to true/false.\n// Notice, that both objects look the same, but produce completely different\n// results if you use the Mutar methods. The following examples are telling \n// Mutar: treat this is little, treat this as big endian. \nconst mutarObjLE = new Mutar(new Uint32Array([300, 400]), null, true);\nconst mutarObjBE = new Mutar(new Uint32Array([300, 400]), null, false);\n\n// If the values shall flip you can set another parameter, called \"adjustEndianness\"\nconst mutarObjBEadjust = new Mutar(new Uint32Array([300, 400]), null, false, true);\n                                          // [738263040, 2415984640] \n```\n\n#### Structure\nMutar objects have a pretty simple structure. The constructor sets:\n * ``littleEndian``\n * ``array``\n * ``view``\n\n```\nMutar {\n    littleEndian: true,\n    array: Uint32Array(9) [\n        300, 400, 450,\n        500, 550, 600,\n        650, 700, 800\n    ],\n    view: DataView {\n        byteLength: 36,\n        byteOffset: 0,\n        buffer: ArrayBuffer {\n            [Uint8Contents]: \n                \u003c 2c 01 00 00 \n                  90 01 00 00\n                  c2 01 00 00\n                  f4 01 00 00\n                  26 02 00 00\n                  58 02 00 00\n                  8a 02 00 00\n                  bc 02 00 00\n                  20 03 00 00 \u003e,\n            byteLength: 36\n        }\n    }\n}\n\n```\n\n#### Methods\nWe can interact directly with those children, but that is not very handy. There are plenty of methods callable from the root, which include all methods of a typed array and regular arrays (with the exception of flat \u0026 flatMap), plus the custom methods of the toolkit.  \nEven though the object has far more methods than the toolkit provides, there is much less to explain. The difference is, that the object holds the array which gets modified, it is therefore not necessary to always store the output of the method. And also we do not have to hand over a typed array (which will be done for you and will always be ``mutarObj.array``). Let's exemplary take a look at some methods.\n\n```js\n// Let us set again the array, with deliberately setting the endianness to be true.\n// (Which is redundant, as it is set to the systems endianness by default, but let\n// us keep this examples universally correct).\nconst mutarObj = new Mutar([300, 400, 450, 500, 550, 600, 650, 700, 800], Uint32Array, true); \n\n// Pop as method\nmutarObj.pop();                                     // -\u003e 800\n\n// Push as method\nmutarObj.push(750);                                 // -\u003e 9\n\n// Concat also behaves like the regular array method \nmutarObj.concat(new Uint32Array([900, 1000]));      // -\u003e returns a mutar object with the array [... 700, 800, 1000]\n\n// You can btw also concat and set/save the concatenated array at mutarObj.array, which\n// saves the step of storing the concatenated return object.\nmutarObj.conset(new Uint32Array([900, 1000]));\n\n// And this list goes on, just apply your knowledge about regular arrays\n// and typed arrays. If you are not interested in manipulating the endianness,\n// that is all you need to know.\n\n// Let us know look at the particularities.\n// As already displayed, the object has the property littleEndian. That\n// means, that every function call is done with this setting, if it is\n// not overwritten, which totally is possible. See for example:\n\nmutarObj.shift(false);                              // -\u003e 738263040\n\n// The value 300 was converted, as we explicitly asked for it. \n// The last object of almost every function takes this endianness\n// bool. As shift usually takes no arguments, there is only this \n// parameter left. But lets us look at an example that takes many \n// arguments. The littleEndian bool is always at the very end.\n\nmutarObj.splice(-2, 2, 800, 850, false);            // -\u003e Uint32Array(2) [ 900, 1000 ]\n\n// The last argument has set the endianness to false, which we can see\n// if we look at the array\nmutarObj.toString();                                // -\u003e '400,450,500,550,600,650,700,750,537067520,1375928320'\n\n// We can also tell the ``toString`` method to flip the endianness\n\nmutarObj.toString(false);                           // -\u003e '...3154247680,3993108480,800,850'\n\n// At the very end we can now see our two values we added with the\n// splice call as BE flipped back again. But lets us know clean up\n// this mixed mess. First we call the detach method to remove the\n// last but one value and tell ist to use BE.\nmutarObj.detach(-2, false);                         // -\u003e 800\n\n// Now the pop method for the removal of the last value without \n// specification\nmutarObj.pop();                                     // -\u003e 1375928320\n\n// Let us now flip the endianness all together and look what happens\nmutarObj.flipEndianness();                          // -\u003e Uint32Array(8) [ 2415984640, ..., 3993108480 ]\n\n// The values have all flipped as well as the littleEndian value.\n// The result is, that if we don't pass any endian bool manually.\n// the methods now work with be. If we call the \"at\" function, we\n// see the actual value, translated to the endianness of our system.\n\nmutarObj.at(0);                                     // -\u003e 400\nmutarObj.array.at(0);                               // -\u003e 2415984640\n\n// The big advantage is, that we now don't have to bother about this.\n// Algorithms can use the systems endianness Mutar translates it.\n// Lets create a little function and pass it to the map function\n// to demonstrate this.\n\nconst demoFN = (val) =\u003e {\n    const newVal = val*2;\n    console.log(newVal);\n    return newVal;\n} \n\nmutarObj.map(demoFN);\n/* 800\n   900\n  1000\n  1100\n  1200\n  1300\n  1400\n  1500\n  \n  Uint32Array(8) [\n    537067520, 2214789120,\n   3892510720, 1275330560,\n   2953052160,  335872000,\n   2013593600, 3691315200\n ]\n*/\n\n// The console shows the actual values, the array shows the values stored in BE byte order.\n```\n\n##### Available Children\n_getters:_\n* ``obj.BYTES_PER_ELEMENT``\n* ``obj.SYS_LITTLE_ENDIAN``\n* ``obj.buffer``\n* ``obj.byteLength``\n* ``obj.byteOffset``\n* ``obj.length``\n* ``obj.type``\n\n_methods:_\n* ``obj.at``\n* ``obj.clone``\n* ``obj.concat``\n* ``obj.conset``\n* ``obj.convert``\n* ``obj.copyWithin``\n* ``obj.detach``\n* ``obj.entries``\n* ``obj.every``\n* ``obj.extractArrayClone``\n* ``obj.fill``\n* ``obj.filter``\n* ``obj.find``\n* ``obj.findIndex``\n* ``obj.flipEndianness``\n* ``obj.forEach``\n* ``obj.includes``\n* ``obj.indexOf``\n* ``obj.insert``\n* ``obj.join``\n* ``obj.keys``\n* ``obj.lastIndexOf``\n* ``obj.map``\n* ``obj.pop``\n* ``obj.push``\n* ``obj.reduce``\n* ``obj.reduceRight``\n* ``obj.reverse``\n* ``obj.set``\n* ``obj.setAt``\n* ``obj.shift``\n* ``obj.slice``\n* ``obj.some``\n* ``obj.sort``\n* ``obj.splice``\n* ``obj.subarray``\n* ``obj.toLocaleString``\n* ``obj.toString``\n* ``obj.trim``\n* ``obj.unshift``\n* ``obj.updateArray``\n* ``obj.values``\n\n## License\nThis work is licensed under [GPL-3.0](https://opensource.org/licenses/GPL-3.0).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fumamiappearance%2Fmutarjs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fumamiappearance%2Fmutarjs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fumamiappearance%2Fmutarjs/lists"}