UNPKG

oracle-nosqldb

Version:

Node.js driver for Oracle NoSQL Database

267 lines (238 loc) 6.74 kB
/*- * Copyright (c) 2018, 2024 Oracle and/or its affiliates. All rights reserved. * * Licensed under the Universal Permissive License v 1.0 as shown at * https://oss.oracle.com/licenses/upl/ */ 'use strict'; const assert = require('assert'); const PackedInteger = require('./packed_integer'); const ResizableBuffer = require('./buffer'); const NoSQLProtocolError = require('../error').NoSQLProtocolError; const stringToUTCDate = require('../utils').stringToUTCDate; class DataReader { constructor(buf) { this._buf = (buf instanceof ResizableBuffer) ? buf : new ResizableBuffer(buf); this._off = 0; } _readByte() { return this._buf.readInt8(this._off++); } _handleEOF(e, ...args) { if (e.name == 'RangeError') { let msg = 'End of stream reached'; if (args.length > 0) { msg += ` while reading ${args[0]}`; if (args.length > 1) { msg += ` of length ${args[1]}`; } } throw new NoSQLProtocolError(msg); } else { throw e; } } _readBuffer(len) { try { const ret = this._buf.readBuffer(this._off, len); this._off += len; return ret; } catch(e) { this._handleEOF(e, 'binary', len); } } get buffer() { return this._buf; } get offset() { return this._off; } set offset(val) { assert(val >= 0); if (val > this._buf.length) { throw new NoSQLProtocolError(`End of stream reached: offset \ ${val} is past length ${this._buf.length}`); } this._off = val; } /** * Reads a packed integer from the buffer and returns it. * * @return the integer that was read * @throws NoSQLError if the input format is invalid or end of input is * reached */ readInt() { try { //We pass ResizableBuffer instead of Buffer so that EOF checking //is performed when reading bytes. let { value, off } = PackedInteger.readSortedInt( this._buf, this._off); this._off = off; return value; } catch(e) { this._handleEOF(e, 'packed int'); } } /** * Reads a packed long from the buffer and returns it. * * @param asBigInt Whether to read the value as bigint * @return the long that was read * @throws NoSQLError if the input format is invalid or end of input is * reached */ readLong(asBigInt = false) { try { let { value, off } = PackedInteger.readSortedLong( this._buf, this._off, asBigInt); this._off = off; return value; } catch(e) { this._handleEOF(e, 'packed long'); } } /** * Reads a packed long from the buffer as bigint and returns it. * * @return bigint value of the long that was read * @throws NoSQLError if the input format is invalid or end of input is * reached */ readLongAsBigInt() { return this._readLong(true); } /** * Reads a string written by {@link #writeString}, using standard UTF-8 * * @return a string or null * @throws NoSQLError if the input format is invalid or end of input is * reached */ readString() { const len = this.readInt(); if (len < -1) { throw new NoSQLProtocolError(`Invalid string length: ${len}`); } if (len == -1) { return null; } if (len == 0) { return ''; } try { const nextOff = this._off + len; const ret = this._buf.slice(this._off, nextOff).toString('utf8'); this._off = nextOff; return ret; } catch(e) { this._handleEOF(e, 'string', len); } } readArray(readItem) { const len = this.readInt(); if (len < -1) { throw new NoSQLProtocolError(`Invalid array length: ${len}`); } if (len == -1) { return null; } const a = new Array(len); for(let i = 0; i < len; i++) { a[i] = readItem(); } return a; } readStringArray() { return this.readArray(this.readString.bind(this)); } /** * Reads a possibly null binary as a {@link #readPackedInt * sequence length} followed by the array contents. * * @return array the array or null * @throws NoSQLError if the input format is invalid or end of input is * reached */ readBinary() { const len = this.readInt(); if (len < -1) { throw new NoSQLProtocolError(`Invalid binary length: ${len}`); } if (len == -1) { return null; } if (len == 0) { return Buffer.allocUnsafe(0); } return this._readBuffer(len); } //Equivalent to readByteArrayWithInt() in BinaryProtocol.java readBinary2() { const len = this.readInt32BE(); if (len <= 0) { return Buffer.allocUnsafe(0); } return this._readBuffer(len); } readIntArray() { return this.readArray(this.readInt.bind(this)); } readByte() { try { return this._readByte(); } catch(e) { this._handleEOF(e, 'byte'); } } readBoolean() { try { return Boolean(this._readByte()); } catch(e) { this._handleEOF(e, 'boolean'); } } readDouble() { try { const ret = this._buf.readDoubleBE(this._off); this._off += 8; return ret; } catch(e) { this._handleEOF(e, 'double'); } } readDate() { const s = this.readString(); if (s === null) { return null; } return stringToUTCDate(s); } readInt16BE() { try { const ret = this._buf.readInt16BE(this._off); this._off += 2; return ret; } catch(e) { this._handleEOF(e, 'short'); } } readInt32BE() { try { const ret = this._buf.readInt32BE(this._off); this._off += 4; return ret; } catch(e) { this._handleEOF(e, 'integer'); } } reset() { this._off = 0; return this; } toString(encoding = 'utf8') { return this._buf.toString(encoding); } } module.exports = DataReader;