bitmark-grammar
Version:
270 lines (227 loc) • 7.52 kB
text/typescript
/*!
* Copyright 2016 The ANTLR Project. All rights reserved.
* Licensed under the BSD-3-Clause license. See LICENSE file in the project root for license information.
*/
import * as assert from "assert";
import * as Character from "./misc/Character";
/**
* Wrapper for `Uint8Array` / `Uint16Array` / `Int32Array`.
*/
export class CodePointBuffer {
private readonly buffer: Uint8Array | Uint16Array | Int32Array;
private _position: number;
private _size: number;
constructor(buffer: Uint8Array | Uint16Array | Int32Array, size: number) {
this.buffer = buffer;
this._position = 0;
this._size = size;
}
public static withArray(buffer: Uint8Array | Uint16Array | Int32Array): CodePointBuffer {
return new CodePointBuffer(buffer, buffer.length);
}
public get position(): number {
return this._position;
}
public set position(newPosition: number) {
if (newPosition < 0 || newPosition > this._size) {
throw new RangeError();
}
this._position = newPosition;
}
public get remaining(): number {
return this._size - this.position;
}
public get(offset: number): number {
return this.buffer[offset];
}
public array(): Uint8Array | Uint16Array | Int32Array {
return this.buffer.slice(0, this._size);
}
public static builder(initialBufferSize: number): CodePointBuffer.Builder {
return new CodePointBuffer.Builder(initialBufferSize);
}
}
export namespace CodePointBuffer {
const enum Type {
BYTE,
CHAR,
INT,
}
export class Builder {
private type: Type;
private buffer: Uint8Array | Uint16Array | Int32Array;
private prevHighSurrogate: number;
private position: number;
constructor(initialBufferSize: number) {
this.type = Type.BYTE;
this.buffer = new Uint8Array(initialBufferSize);
this.prevHighSurrogate = -1;
this.position = 0;
}
public build(): CodePointBuffer {
return new CodePointBuffer(this.buffer, this.position);
}
private static roundUpToNextPowerOfTwo(i: number): number {
let nextPowerOfTwo: number = 32 - Math.clz32(i - 1);
return Math.pow(2, nextPowerOfTwo);
}
public ensureRemaining(remainingNeeded: number): void {
switch (this.type) {
case Type.BYTE:
if (this.buffer.length - this.position < remainingNeeded) {
let newCapacity: number = Builder.roundUpToNextPowerOfTwo(this.buffer.length + remainingNeeded);
let newBuffer: Uint8Array = new Uint8Array(newCapacity);
newBuffer.set(this.buffer.subarray(0, this.position), 0);
this.buffer = newBuffer;
}
break;
case Type.CHAR:
if (this.buffer.length - this.position < remainingNeeded) {
let newCapacity: number = Builder.roundUpToNextPowerOfTwo(this.buffer.length + remainingNeeded);
let newBuffer: Uint16Array = new Uint16Array(newCapacity);
newBuffer.set(this.buffer.subarray(0, this.position), 0);
this.buffer = newBuffer;
}
break;
case Type.INT:
if (this.buffer.length - this.position < remainingNeeded) {
let newCapacity: number = Builder.roundUpToNextPowerOfTwo(this.buffer.length + remainingNeeded);
let newBuffer: Int32Array = new Int32Array(newCapacity);
newBuffer.set(this.buffer.subarray(0, this.position), 0);
this.buffer = newBuffer;
}
break;
}
}
public append(utf16In: Uint16Array): void {
this.ensureRemaining(utf16In.length);
this.appendArray(utf16In);
}
private appendArray(utf16In: Uint16Array): void {
switch (this.type) {
case Type.BYTE:
this.appendArrayByte(utf16In);
break;
case Type.CHAR:
this.appendArrayChar(utf16In);
break;
case Type.INT:
this.appendArrayInt(utf16In);
break;
}
}
private appendArrayByte(utf16In: Uint16Array): void {
assert(this.prevHighSurrogate === -1);
let input: Uint16Array = utf16In;
let inOffset: number = 0;
let inLimit: number = utf16In.length;
let outByte = this.buffer;
let outOffset: number = this.position;
while (inOffset < inLimit) {
let c: number = input[inOffset];
if (c <= 0xFF) {
outByte[outOffset] = c;
} else {
utf16In = utf16In.subarray(inOffset, inLimit);
this.position = outOffset;
if (!Character.isHighSurrogate(c)) {
this.byteToCharBuffer(utf16In.length);
this.appendArrayChar(utf16In);
return;
} else {
this.byteToIntBuffer(utf16In.length);
this.appendArrayInt(utf16In);
return;
}
}
inOffset++;
outOffset++;
}
this.position = outOffset;
}
private appendArrayChar(utf16In: Uint16Array): void {
assert(this.prevHighSurrogate === -1);
let input: Uint16Array = utf16In;
let inOffset: number = 0;
let inLimit: number = utf16In.length;
let outChar = this.buffer;
let outOffset: number = this.position;
while (inOffset < inLimit) {
let c: number = input[inOffset];
if (!Character.isHighSurrogate(c)) {
outChar[outOffset] = c;
} else {
utf16In = utf16In.subarray(inOffset, inLimit);
this.position = outOffset;
this.charToIntBuffer(utf16In.length);
this.appendArrayInt(utf16In);
return;
}
inOffset++;
outOffset++;
}
this.position = outOffset;
}
private appendArrayInt(utf16In: Uint16Array): void {
let input: Uint16Array = utf16In;
let inOffset: number = 0;
let inLimit: number = utf16In.length;
let outInt = this.buffer;
let outOffset = this.position;
while (inOffset < inLimit) {
let c: number = input[inOffset];
inOffset++;
if (this.prevHighSurrogate !== -1) {
if (Character.isLowSurrogate(c)) {
outInt[outOffset] = String.fromCharCode(this.prevHighSurrogate, c).codePointAt(0)!;
outOffset++;
this.prevHighSurrogate = -1;
} else {
// Dangling high surrogate
outInt[outOffset] = this.prevHighSurrogate;
outOffset++;
if (Character.isHighSurrogate(c)) {
this.prevHighSurrogate = c;
} else {
outInt[outOffset] = c;
outOffset++;
this.prevHighSurrogate = -1;
}
}
} else if (Character.isHighSurrogate(c)) {
this.prevHighSurrogate = c;
} else {
outInt[outOffset] = c;
outOffset++;
}
}
if (this.prevHighSurrogate !== -1) {
// Dangling high surrogate
outInt[outOffset] = this.prevHighSurrogate;
outOffset++;
}
this.position = outOffset;
}
private byteToCharBuffer(toAppend: number): void {
// CharBuffers hold twice as much per unit as ByteBuffers, so start with half the capacity.
let newBuffer: Uint16Array = new Uint16Array(Math.max(this.position + toAppend, this.buffer.length >> 1));
newBuffer.set(this.buffer.subarray(0, this.position), 0);
this.type = Type.CHAR;
this.buffer = newBuffer;
}
private byteToIntBuffer(toAppend: number): void {
// IntBuffers hold four times as much per unit as ByteBuffers, so start with one quarter the capacity.
let newBuffer: Int32Array = new Int32Array(Math.max(this.position + toAppend, this.buffer.length >> 2));
newBuffer.set(this.buffer.subarray(0, this.position), 0);
this.type = Type.INT;
this.buffer = newBuffer;
}
private charToIntBuffer(toAppend: number): void {
// IntBuffers hold two times as much per unit as ByteBuffers, so start with one half the capacity.
let newBuffer: Int32Array = new Int32Array(Math.max(this.position + toAppend, this.buffer.length >> 1));
newBuffer.set(this.buffer.subarray(0, this.position), 0);
this.type = Type.INT;
this.buffer = newBuffer;
}
}
}