readBits(numBits /*int*/) {
			if (numBits < 1 || numBits > 32 || numBits > this.available()) {
				throw new IllegalArgumentException('' + numBits);
			}
			let result = 0;
			let bitOffset = this.bitOffset;
			let byteOffset = this.byteOffset;
			const bytes = this.bytes;
			// First, read remainder from current byte
			if (bitOffset > 0) {
				const bitsLeft = 8 - bitOffset;
				const toRead = numBits < bitsLeft ? numBits : bitsLeft;
				const bitsToNotRead = bitsLeft - toRead;
				const mask = (0xFF >> (8 - toRead)) << bitsToNotRead;
				result = (bytes[byteOffset] & mask) >> bitsToNotRead;
				numBits -= toRead;
				bitOffset += toRead;
				if (bitOffset === 8) {
					bitOffset = 0;
					byteOffset++;
				}
			}
			// Next read whole bytes
			if (numBits > 0) {
				while (numBits >= 8) {
					result = (result << 8) | (bytes[byteOffset] & 0xFF);
					byteOffset++;
					numBits -= 8;
				}
				// Finally read a partial byte
				if (numBits > 0) {
					const bitsToNotRead = 8 - numBits;
					const mask = (0xFF >> bitsToNotRead) << bitsToNotRead;
					result = (result << numBits) | ((bytes[byteOffset] & mask) >> bitsToNotRead);
					bitOffset += numBits;
				}
			}
			this.bitOffset = bitOffset;
			this.byteOffset = byteOffset;
			return result;
		}
@param numBits number of bits to read
@return int representing the bits read. The bits will appear as the least-significant
        bits of the int
@throws IllegalArgumentException if numBits isn't in [1,32] or more than is available