Security News
The Push to Ban Ransom Payments Is Gaining Momentum
Ransomware costs victims an estimated $30 billion per year and has gotten so out of control that global support for banning payments is gaining momentum.
binarcular
Advanced tools
Readme
A library that allows you to read the contents of a binary file into a JSON Object, taking care of all data type conversion into JavaScript-friendly values. You can search for data by value, slice blocks into separate, meaningful structures or just read the entire file, all inside your browser.
Practical use cases are:
See API and Example below.
binarcular should work fine on Internet Explorer 10 and up. You can verify support at runtime by querying the result of the isSupported()-method:
import { isSupported } from 'binarcular';
if ( isSupported( optRequire64bitConversion = false ) ) {
...do stuff!
} else {
...do other, less cool stuff! Actually, not cool at all!! >=/
}
NOTE: if you require support for 64-bit types there are additional requirements. Pass boolean true for optional argument optRequire64bitConversion to determine whether the environment supports 64-bit conversion.
You can get it via NPM:
npm install binarcular
The parser is compatible with CommonJS / ES6 modules or can be included in a document using AMD/RequireJS. See the contents of the /dist/ folder and include as your project sees fit.
The module exports the following:
import {
isSupported: fn( optUseBase64Boolean = false ),
types: Object<String>,
parse: async fn( dataSource, structureDefinition, optReadOffset = 0 ),
seek: async fn( uint8Array, searchStringOrByteArray, optReadOffset = 0),
fileToByteArray: async fn( file, optSliceOffset = 0, optSliceSize = file.size )
} from 'binarcular';
We'll look into each of these below:
The parse method is reponsible for this:
async parse( dataSource, structureDefinition, optReadOffset = 0 )
Where:
When the Promise resolves, the result is the following structure:
{
data: Object,
end: Number,
error: Boolean,
byteArray: Uint8Array
}
If all has been read successfully, data is an Object that follows the structure of wavHeader and is populated with the actual file data.
end describes at what offset in given file the structure's definition has ended. This can be used for subsequent read operations where different data types are extracted from the binary data.
If error is true, this indicates that something went wrong during parsing. The data Object will be populated with all data that could've been harvested up until the error occurred. This allow you to harvest what you can from corrupted files.
You can see that another property is defined in the result, namely byteArray. If the dataSource provided to the parse method was a Uint8Array which you intend to reuse inside your project, be sure to reassign your byteArray reference to the returned instance.
The rationale here is that for minimal overhead, the ownership of the ByteArray's binary content is transferred during the read operations. You will not be able to perform any actions on your ByteArray without updating the reference.
If you are working with a file where the content of interest is preceded by some metadata at an arbitrary point, it makes sense to first look for this metadata declaration so you know from where you can retrieve the actual data of interest.
For this purpose you can use seek:
async seek( uint8Array, searchStringOrByteArray, optReadOffset = 0 )
where:
The method returns a numerical index at which the data was found or Infinity if no match were found.
async fileToByteArray( fileReference, optSliceOffset = 0, optSliceSize = fileReference.size )
where:
Let's say we want to read the binary data of a well known proprietary format. First up we will get to...
Defining a structure is nothing more than declaring an Object where the keys define names meaningful to your purpose and the values consist of Strings describing:
An example structure that defines the header of a .WAV file would look like:
const wavHeader = {
type: 'CHAR[4]',
size: 'INT32|LE',
format: 'CHAR[4]',
formatName: 'CHAR[4]',
formatLength: 'INT32|LE',
audioFormat: 'INT16|LE',
channelAmount: 'INT16|LE',
sampleRate: 'INT32|LE',
bytesPerSecond: 'INT32|LE',
blockAlign: 'INT16|LE',
bitsPerSample: 'INT16|LE',
dataChunkId: 'CHAR[4]',
dataChunkSize: 'INT32|LE'
};
Note that the order of the keys (and more importantly: their type definition) should match the order of the values as described the particular file's type!
Note that specifying endianness can be omitted if you're certain that the file's encoding is equal to that of the platform you will be parsing the file on (most likely only Big Endianness will require an explicit definition). And I hope you will never be in the unfortunate situation where you work with a file that uses different endianness for different blocks!
All available data types are listed in the { types } export. Note that definitions for CHAR will return as a String. If you want an 8-bit integer/byte value, use BYTE or INT8 instead.
We can now proceed to read the file:
import { parse } from 'binarcular';
async function readWaveHeader( fileReference ) {
const { data, end, error, byteArray } = await parse( fileReference, wavHeader, 0 );
console.log( data ); // will contain the properties of a WAV file header
console.log( end ); // will describe the end offset of the header
console.log( error ); // when true, a file reading error occurred
}
You can also view the demo provided in this repository's example.html file, which parses .WAV files and provides advanced examples using seeking, slicing and error correction before finally providing you with the instruction on how to extract the meaningful data from the file.
Depending on the size of the files you're working with, memory allocation can become a problem.
The parser will only read the block that is requested (e.g. starting from the requested offset and only for the size of the requested structureDefinition) and should thus be light on resources. Additionally, all read operations happen in a dedicated Web Worker which keeps your main application responsive (you can safely parse several hundred megabytes of data without blocking your UI).
Depending on your use case, it helps to take the following guidelines into consideration:
In case you want to aid in development of the library:
The project dependencies are maintained by NPM, you can resolve them using:
npm install
You can develop (and test against the example app) by running:
npm run dev
To create a production build:
npm run build
After which a folder dist/ is created which contains the prebuilt AMD/RequireJS and CommonJS/ES module libraries (as well as the example application).
The source code is transpiled to ES5 for maximum browser compatibility.
Unit tests are run via jest, you can run the tests by running:
npm run test
Unit tests go in the ./test-folder. The file name for a unit test must be equal to the file it is testing, but contain the suffix ".spec", e.g. functions.js should have a test file functions.spec.js.
FAQs
Parse|write|slice|search binary data/files into|from meaningful JSON structures, inside your web browser
We found that binarcular demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Ransomware costs victims an estimated $30 billion per year and has gotten so out of control that global support for banning payments is gaining momentum.
Application Security
New SEC disclosure rules aim to enforce timely cyber incident reporting, but fear of job loss and inadequate resources lead to significant underreporting.
Security News
The Python Software Foundation has secured a 5-year sponsorship from Fastly that supports PSF's activities and events, most notably the security and reliability of the Python Package Index (PyPI).