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

What is the most effective method for obtaining only the "steamid" from an AJAX request (or any other method)?

I have been attempting to extract only the "steamid" from an AJAX link without success. Could someone please provide some assistance? Here is the link to find and retrieve only the "steamid": here This is the code I have tried: var xhttp = new XMLHt ...

Adjust the code to enhance the functionality of web components in Sharepoint

I recently came across some code online that I'm trying to modify in order to add an expanding button next to a web part on my Sharepoint site. However, the problem is that by default, all web parts are already expanded and require a click to collapse ...

Is it possible to modify the contents within the JSP div tag without replacing them through an AJAX call?

In my JSP, I face a scenario where there is a div tag with scriptlet content that pulls data from the database every time a request is received from the server. Previously, I was refreshing the entire page with each update, which not only loaded all the re ...

"Encountered a runtime error while trying to execute the doubleClick() function using Pro

Encountering the following issue: "WebDriverError: Unable to convert: Error 404: Not found" while running a test with protractor: browser.actions().doubleClick(elem).perform(); or browser.actions().click(elem).click(elem).perform(); Uncertain of t ...

Guide to executing a fetch request prior to another fetch in React Native

I am currently working on a project using React Native. One issue I have run into is that all fetch requests are being executed simultaneously. What I actually need is for one fetch to wait until the previous one has completed before using its data. Speci ...

React-native - Dropdownpicker - Error: Unable to retrieve label for selected choice

I'm encountering an issue with DropDownPicker in my react-native project during page load Here is the code snippet where I am using DropDownPicker: <DropDownPicker items={[ { la ...

Struggling with importing aliases in TypeScript for shadcn-ui library

I am facing a challenge with resolving TypeScript path aliases in my project. I have set up the tsconfig.json file to include path aliases using the "baseUrl" and "paths" configurations, but alias imports are not functioning as intended. My goal is to imp ...

Implement necessary validation for the country code selection on the dropdown menu using the intl-tel-input jQuery plugin

Check out the intl-tel-input plugin here Currently, I am utilizing this plugin and attempting to implement required validation on the country code drop-down. However, the plugin seems to be restricting me from achieving this. I have made several attempts ...

What guidelines should be followed for utilizing jQuery's Ajax convenience methods and effectively managing errors?

Imagine a scenario where I am trying to mimic Gmail's interface using jQuery Ajax to incorporate periodic auto-saving and sending functionalities. Error handling is crucial, especially in cases of network errors or other issues. Instead of just being ...

Getting the error message "t is not a function. (In 't(i,c)', 't' is an instance of Object)" while attempting to switch from using createStore to configureStore with React Redux Toolkit

I am attempting to switch from react-redux to its alternative react-redux toolkit but I kept encountering this issue t is not a function. (In 't(i,c)', 't' is an instance of Object) and I am unsure of its meaning. Here is the c ...

Setting the desired configuration for launching an Aurelia application

After creating a new Aurelia Typescript application using the au new command from the Aurelia CLI, I noticed that there is a config directory at the root of the project. Inside this directory, there are two files: environment.json and environment.productio ...

Encountering a problem with Chrome Extension localization upon installation - receiving an error message claiming default locale was not specified, despite having

Error Message: "The package has been deemed invalid due to the following reason: 'Localization was utilized, however default_locale was not specified in the manifest.' Issue: I have developed a customized extension and defined a default locale, ...

Variable missing in the ExpressJs view

Hey there! I'm new to Nodejs and currently experimenting with it. I've been trying to convert some of my basic Python codes to JavaScript. In one of my projects, I am sending a get request to the YouTube API and receiving 50 results in JSON forma ...

React component is being rendered, but it is not mounting properly, so it is unable

In my FillForm functional component, I am calling a list of objects to be rendered sequentially within the FormFiller function. The components are rendering correctly, but I encounter an error when trying to change their internal state. Warning: Can&apos ...

Tips on displaying a spinner only when data is retrieved from an Http service

How can I ensure that a spinner is only shown during an HTTP service call and dismissed when my component receives data? To address this issue, I implemented a cache service to store data fetched from the HTTP service for future use. However, I want to sh ...

Actions cannot be performed on elements generated by ng-repeat

I am facing a challenge where I need to display a list of languages on a page, allowing users to change their selections based on previous choices. To achieve this functionality, I have written the following code snippet: ... <tbody> & ...

Can state values be utilized as content for Meta tags?

I am looking for a way to display image previews and titles when sharing a page link. In order to achieve this, I am using the Nextjs Head Component. The necessary details are fetched on page load and used as content for the meta attributes. let campaign = ...

Transform audio file into a base64 encoding

I am currently developing a Phonegap application for a friend that will allow users to record audio on their phone, save it to the browser's local storage, and then upload it at a later time. As far as I know, local storage does not support storing b ...

What techniques can be used to maintain the value of 'this' when utilizing async.apply?

Employing async.parallel to simultaneously run 2 functions, initiated from a static function within a mongoose model. In this code snippet (where the model contains a static function named verifyParent), I utilize this to access the model and its functions ...

How does the Express server collaborate with Webpack middlewares to facilitate live reloading?

As I delve into node, express, and webpack, I find myself grappling with the concept of middleware. Upon examining the code snippet below, my current understanding is that once the web server is up and running and I navigate to http://localhost:7770/, the ...