JavaScript Equivalent to C#'s BinaryReader.ReadString() Function

Currently, I am in the process of translating C# code into JavaScript. While transferring multiple datatypes from this file to matching functionalities found in various JavaScript libraries was relatively smooth, there is one specific function that seems to be missing in JS.

The particular function in question can be accessed via this link.

This brings up a few queries:

  1. My initial confusion lies in the fact that strings are inherently variable-length variables. Therefore, why doesn't this function require a length argument?
  2. If we assume that there is a restriction on the string's length, does JavaScript/TypeScript offer a comparable feature? Is there a specific package that I could utilize to replicate the functionality present in C#?

I appreciate any insights you may have on this matter.

Answer №1

BinaryReader requires strings to be encoded in a specific format that is the same format BinaryWriter uses when writing them. This encoding method prefixes the string with its length, which is then encoded as an integer seven bits at a time.

According to the documentation, this method reads a string from the current stream where the string's length is indicated by an integer value encoded seven bits at a time.

Essentially, the length of the string is stored just before the actual string itself and is encoded using seven bits at a time per integer. Additional information about this process can be found in the BinaryWriter.Write7BitEncodedInt documentation:

This means that the integer value is written out in seven-bit chunks, starting with the least significant bits. Each byte contains a high bit indicating whether more bytes are needed to represent the full integer.

If the value fits within seven bits, it only takes up one byte. If not, the high bit is set on the first byte, and the remaining bits are shifted to the following byte until the entire integer has been represented.

This approach utilizes variable-length encoding unlike the standard 4-byte usage for Int32 values. Shorter strings can require less than 4 bytes (e.g., strings under 128 bytes may only need 1 byte).

In JavaScript, you can replicate this logic by reading one byte at a time. The lower 7 bits convey part of the length information while the highest bit indicates if another byte follows for additional length data or starts the actual string.

To decode the byte array into a string of specified encoding, use the `TextDecoder` function. Below is a TypeScript implementation of this process using a buffer (Uint8Array), buffer offset, and optionally defining the encoding (default being UTF-8):

// Implementation of BinaryReader class in TypeScript
class BinaryReader {
  getString(buffer: Uint8Array, offset: number, encoding: string = "utf-8") {
      let length = 0; // Initialize the length of the subsequent string
      let cursor = 0;
      let nextByte: number;

      do {
          // Retrieve the next byte
          nextByte = buffer[offset + cursor];

          // Extract 7 bits from the current byte and shift them based on their position
          // If it's the first byte, no shifting occurs. For subsequent bytes, shift by multiples of 7
          // Combine the extracted bits with the length using bitwise OR operation
          length = length | ((nextByte & 0x7F) << (cursor * 7));

          cursor++;
      } while (nextByte >= 0x80); // Continue while the most significant bit is 1

      // Fetch a slice of the calculated length
      let sliceWithString = buffer.slice(offset + cursor, offset + cursor + length);
      let decoder = new TextDecoder(encoding);

      return decoder.decode(sliceWithString);
  }
}

It's advisable to include various sanity checks in the above code if it will be used in a production environment to avoid reading unnecessary bytes during length interpretation or ensuring the calculated length falls within buffer boundaries.

A brief test using the binary representation of the string "TEST STRING," as written by BinaryWriter.Write(string) in C#:

// Test example
let buffer = new Uint8Array([12, 84, 69, 83, 84, 32, 83, 84, 82, 73, 78, 71, 33]);
let reader = new BinaryReader();
console.log(reader.getString(buffer, 0, "utf-8"));
// Output should be "TEST STRING"

Update: Your comment mentioned that your data represents string length using 4 bytes (e.g., [0, 0, 0, 29] for a length of 29). In such cases, the data wasn't originally written using BinaryWriter, so using BinaryReader might not be applicable to read it. However, a solution for handling such scenarios is provided below:

// Updated implementation for handling 4-byte length representation
class BinaryReader {
  getString(buffer: Uint8Array, offset: number, encoding: string = "utf-8") {
      // Create a view over the first 4 bytes starting at the given offset
      let view = new DataView(buffer.buffer, offset, 4);
      
      // Read these 4 bytes as a signed int32 (big-endian format)
      let length = view.getInt32(0);
      
      // Get a slice of the obtained length
      let sliceWithString = buffer.slice(offset + 4, offset + 4 + length);
      
      let decoder = new TextDecoder(encoding);

      return decoder.decode(sliceWithString);
  }
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Resolving ES6 type conflicts while compiling TypeScript to Node.js

I seem to be facing some challenges with the TypeScript 2 type system when working with Node.js. Let me explain the situation: I am compiling a small Node.js Express server written in TypeScript to plain ES5 for running under Node 6.10.0 (target: ES5 in ...

The function initFoodModel is missing and causing issues as it tries to read properties of undefined, specifically attempting to read 'findAll'

myWishlist.js const { setupFoodModel } = require("../../model/FoodModel"); const { setupWishlistModel } = require("../../model/myWishlistModel"); const setupMyWishlistModel = async () =\> { try { const sequelizeConnection = awai ...

Unveiling the Evasive Final Element in a JavaScript Array

Having a Javascript array named full_range: const range1 = _.range(1, 10, 0.5); const range2 = _.range(10, 100, 5); const range3 = _.range(100, 1000, 50); const range4 = _.range(1000, 10000, 500); const range5 = _.range(10000, 105000, 5000); const full_ran ...

What is the default state of ng-switch in AngularJS?

I am completely new to using AngularJS and I have a question about setting a default ng-switch state in the following Plunkr. Currently, I can only get it to work when clicking, but ideally the menu should be displayed automatically if the user button is t ...

Leverage the Node Short ID library in conjunction with Angular 6 using TypeScript

I attempted to utilize the node module within an Angular 6 typescript environment. Step one: npm i shortid Within my TypeScript class: import { shortid } from 'shortid'; let Uid = shortid.generate(); However, I encountered an error stating ...

The Axios.get function is mistakenly returning raw HTML instead of the expected JSON data

I'm new to using react js and am currently attempting to retrieve data from the database by utilizing axios.get within the componentDidMount lifecycle method. The code snippet below shows how I am trying to fetch products. My setup involves using reac ...

Uncovering design elements from Material UI components

The AppBar component applies certain styles to children of specific types, but only works on direct children. <AppBar title="first" iconElementRight={ <FlatButton label="first" /> }/> <AppBar title="second" iconElementRight={ <di ...

Leveraging @types from custom directories in TypeScript

In our monorepo utilizing Lerna, we have two packages - package a and package b - both containing @types/react. Package A is dependent on Package B, resulting in the following structure: Package A: node_modules/PackageB/node_modules/@types This setup le ...

What is the best way to retrieve AWS secret values using JavaScript?

Having recently started using AWS, I have been able to manually obtain the secret I need. However, when attempting to utilize the code snippet provided by AWS to retrieve the secret value, all my attempts result in undefined. Can someone please point out ...

Issue with Bootstrap Carousel: all elements displayed at once

I'm in the process of building a carousel. I have set up the structure, but I only want five blocks to be visible initially, with the sixth block appearing after clicking an arrow. How can I achieve this? My strategy: (adopted from here) img{ b ...

Is there a potential impact on performance when utilizing local variables instead of repeatedly accessing properties?

Examining JavaScript code optimized for a performance-sensitive environment, specifically a game engine in mobile settings. Oftentimes, this code avoids using local variables and instead relies on explicit chains, such as: if (this.x.y[z].i) { this.x ...

Rendering HTML is not supported by AngularJS on Android 19 with version 4.4.4 and Safari 8.0.5

I have been working on an AngularJS app that is being displayed in a Webview on Android. Everything was functioning properly until yesterday when I started encountering issues with Angular not rendering the DOM correctly. I have tested the app on various v ...

"Enhancing event handling: Using addEventListener natively with selectors similar to .on()

Has anyone figured out how to replicate jQuery's .on() method in vanilla JavaScript? The native addEventListener function doesn't seem to have the capability to filter based on child/selector elements, and delving into event bubbling and capturin ...

The React class component is throwing an unexpected error with the keyword 'this'

I encountered an error stating "Unexpected keyword 'this'" while attempting to update the React state using Redux saga. Could someone shed light on what's wrong with the code below and how I can fix it? class Welcome extends React.Component ...

What is the best way to retrieve router parameters within a JSX component?

How can I pass the post ID as a prop to the EditPost component in order to edit it? render() { return ( <Router> <Switch > <Route path="/edit/:id"> <EditPost index={/*what do I do here?*/} /> ...

Observe the task while returning - Firebase Functions

I am working on a Firebase Cloud Functions project where I need to run a scraping task within one of the functions. While the scraping is in progress, I also need to provide progress updates to the consumer. For example, informing them when the task is at ...

What is the best way to save the various canvas images within a division as a single png file?

Hey there, I currently have a setup with multiple canvas elements within a main division structured like this: <div id="main"> <canvas id="one"> <canvas id="two"> <div id="main_2"> <canvas id="three"> </div ...

JavaScript Age confirmation Overlay

I designed an age verification popup with the help of some online tutorials and a friend due to my limited knowledge of JavaScript. Check it out live here- My issue is that I want this popup to load/appear after the page content loads, but I'm not s ...

Utilize an AJAX call to fetch an array and incorporate it within your JavaScript code

Currently, I am in the process of building a live update chart feature. To access the necessary data, I created a separate file which contains the required information. Through AJAX, I sent a request from my main page to retrieve an Array and incorporate i ...

Enhanced coding experience with JavaScript completion and ArangoDB module management

Exploring New Horizons After more than a decade of using Eclipse for Java development, I have decided to delve into the realms of javascript and arangodb due to high demand. My current task involves developing multiple microservices to run within arangodb ...