Skip to content

Instantly share code, notes, and snippets.

@realsba
Last active July 28, 2023 14:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save realsba/725fd395a26ac195da737dabf0a97194 to your computer and use it in GitHub Desktop.
Save realsba/725fd395a26ac195da737dabf0a97194 to your computer and use it in GitHub Desktop.
JS implementation of BinaryStream
export default class BinaryStream {
_offset = 0;
_dataview = null;
_uint8Array = null;
constructor(buffer) {
this._dataview = new DataView(buffer);
this._uint8Array = new Uint8Array(buffer);
}
get buffer() {
return this._dataview.buffer;
}
readUInt8() {
return this._dataview.getUint8(this._offset++);
};
readUInt16() {
let value = this._dataview.getUint16(this._offset);
this._offset += 2;
return value;
};
readUInt32() {
let value = this._dataview.getUint32(this._offset);
this._offset += 4;
return value;
};
readFloat() {
let value = this._dataview.getFloat32(this._offset);
this._offset += 4;
return value;
};
readString() {
let decoder = new TextDecoder();
let length = this.readUInt16();
let value = decoder.decode(this._dataview.buffer.slice(this._offset, this._offset + length));
this._offset += length;
return value;
};
writeUInt8(value) {
this._dataview.setUint8(this._offset++, value);
};
writeUInt16(value) {
this._dataview.setUint16(this._offset, value);
this._offset += 2;
};
writeUInt32(value) {
this._dataview.setUint32(this._offset, value);
this._offset += 4;
};
writeFloat(value) {
this._dataview.setFloat32(this._offset, value);
this._offset += 4;
};
writeString(value) {
let encoder = new TextEncoder();
value = encoder.encode(value);
this.writeUInt16(value.length);
this._uint8Array.set(value, this._offset);
this._offset += value.length;
};
}
@belichuk
Copy link

Potential Issue:
In the writeString method, there seems to be an error with the line this._uint8Array.set(value, this._offset);. It should be using this._dataview.set instead, as the class doesn't have a _uint8Array property. The correct line should be:

this._dataview.set(value, this._offset);

Error Handling:
The code doesn't have any error handling mechanisms for cases where the _offset goes beyond the buffer's bounds. It might be a good idea to add some validation to ensure that the reading and writing operations don't exceed the buffer size.

@belichuk
Copy link

export default class BinaryStream {
  #offset = 0;
  #dataview = null;

  constructor(buffer) {
    this.#dataview = new DataView(buffer);
  }

  // Read unsigned 8-bit integer
  readUInt8() {
    if (this.#offset + 1 > this.#dataview.byteLength) {
      throw new Error('Attempted to read beyond buffer boundaries.');
    }
    const value = this.#dataview.getUint8(this.#offset);
    this.#offset += 1;
    return value;
  }

  // Read unsigned 16-bit integer
  readUInt16() {
    if (this.#offset + 2 > this.#dataview.byteLength) {
      throw new Error('Attempted to read beyond buffer boundaries.');
    }
    const value = this.#dataview.getUint16(this.#offset);
    this.#offset += 2;
    return value;
  }

  // Read unsigned 32-bit integer
  readUInt32() {
    if (this.#offset + 4 > this.#dataview.byteLength) {
      throw new Error('Attempted to read beyond buffer boundaries.');
    }
    const value = this.#dataview.getUint32(this.#offset);
    this.#offset += 4;
    return value;
  }

  // Read 32-bit floating point number
  readFloat() {
    if (this.#offset + 4 > this.#dataview.byteLength) {
      throw new Error('Attempted to read beyond buffer boundaries.');
    }
    const value = this.#dataview.getFloat32(this.#offset);
    this.#offset += 4;
    return value;
  }

  // Read string
  readString() {
    const length = this.readUInt16();
    if (this.#offset + length > this.#dataview.byteLength) {
      throw new Error('Attempted to read beyond buffer boundaries.');
    }
    const decoder = new TextDecoder();
    const value = decoder.decode(this.#dataview.buffer.slice(this.#offset, this.#offset + length));
    this.#offset += length;
    return value;
  }

  // Write unsigned 8-bit integer
  writeUInt8(value) {
    this.#dataview.setUint8(this.#offset, value);
    this.#offset += 1;
  }

  // Write unsigned 16-bit integer
  writeUInt16(value) {
    this.#dataview.setUint16(this.#offset, value);
    this.#offset += 2;
  }

  // Write unsigned 32-bit integer
  writeUInt32(value) {
    this.#dataview.setUint32(this.#offset, value);
    this.#offset += 4;
  }

  // Write 32-bit floating point number
  writeFloat(value) {
    this.#dataview.setFloat32(this.#offset, value);
    this.#offset += 4;
  }

  // Write string
  writeString(value) {
    const encoder = new TextEncoder();
    const encodedValue = encoder.encode(value);
    this.writeUInt16(encodedValue.length);
    const remainingSpace = this.#dataview.byteLength - this.#offset;
    if (encodedValue.length > remainingSpace) {
      throw new Error('Not enough space in the buffer to write the string.');
    }
    new Uint8Array(this.#dataview.buffer, this.#offset).set(encodedValue);
    this.#offset += encodedValue.length;
  }
}

@belichuk
Copy link

export default class BinaryStream {
  #offset = 0;
  #dataview = null;

  constructor(buffer) {
    this.#dataview = new DataView(buffer);
  }

  // Read unsigned 8-bit integer
  readUInt8() {
    if (this.#offset + 1 > this.#dataview.byteLength) {
      throw new Error('Attempted to read beyond buffer boundaries.');
    }
    const value = this.#dataview.getUint8(this.#offset);
    this.#offset += 1;
    return value;
  }

  // Read unsigned 16-bit integer
  readUInt16() {
    if (this.#offset + 2 > this.#dataview.byteLength) {
      throw new Error('Attempted to read beyond buffer boundaries.');
    }
    const value = this.#dataview.getUint16(this.#offset);
    this.#offset += 2;
    return value;
  }

  // Read unsigned 32-bit integer
  readUInt32() {
    if (this.#offset + 4 > this.#dataview.byteLength) {
      throw new Error('Attempted to read beyond buffer boundaries.');
    }
    const value = this.#dataview.getUint32(this.#offset);
    this.#offset += 4;
    return value;
  }

  // Read 32-bit floating point number
  readFloat() {
    if (this.#offset + 4 > this.#dataview.byteLength) {
      throw new Error('Attempted to read beyond buffer boundaries.');
    }
    const value = this.#dataview.getFloat32(this.#offset);
    this.#offset += 4;
    return value;
  }

  // Read string
  readString() {
    const length = this.readUInt16();
    if (this.#offset + length > this.#dataview.byteLength) {
      throw new Error('Attempted to read beyond buffer boundaries.');
    }
    const decoder = new TextDecoder();
    const value = decoder.decode(this.#dataview.buffer.slice(this.#offset, this.#offset + length));
    this.#offset += length;
    return value;
  }

  // Write unsigned 8-bit integer
  writeUInt8(value) {
    this.#dataview.setUint8(this.#offset, value);
    this.#offset += 1;
  }

  // Write unsigned 16-bit integer
  writeUInt16(value) {
    this.#dataview.setUint16(this.#offset, value);
    this.#offset += 2;
  }

  // Write unsigned 32-bit integer
  writeUInt32(value) {
    this.#dataview.setUint32(this.#offset, value);
    this.#offset += 4;
  }

  // Write 32-bit floating point number
  writeFloat(value) {
    this.#dataview.setFloat32(this.#offset, value);
    this.#offset += 4;
  }

  // Write string
  writeString(value) {
    const encoder = new TextEncoder();
    const encodedValue = encoder.encode(value);
    this.writeUInt16(encodedValue.length);
    const remainingSpace = this.#dataview.byteLength - this.#offset;
    if (encodedValue.length > remainingSpace) {
      throw new Error('Not enough space in the buffer to write the string.');
    }
    new Uint8Array(this.#dataview.buffer, this.#offset).set(encodedValue);
    this.#offset += encodedValue.length;
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment