2 * Copyright (c) 2019 The xterm.js authors. All rights reserved.
5 import { IParams, ParamsArray } from 'common/parser/Types';
7 // max value supported for a single param/subparam (clamped to positive int32 range)
8 const MAX_VALUE = 0x7FFFFFFF;
9 // max allowed subparams for a single sequence (hardcoded limitation)
10 const MAX_SUBPARAMS = 256;
13 * Params storage class.
14 * This type is used by the parser to accumulate sequence parameters and sub parameters
15 * and transmit them to the input handler actions.
18 * - params object for action handlers is borrowed, use `.toArray` or `.clone` to get a copy
19 * - never read beyond `params.length - 1` (likely to contain arbitrary data)
20 * - `.getSubParams` returns a borrowed typed array, use `.getSubParamsAll` for cloned sub params
21 * - hardcoded limitations:
22 * - max. value for a single (sub) param is 2^31 - 1 (greater values are clamped to that)
23 * - max. 256 sub params possible
24 * - negative values are not allowed beside -1 (placeholder for default value)
26 * About ZDM (Zero Default Mode):
27 * ZDM is not orchestrated by this class. If the parser is in ZDM,
28 * it should add 0 for empty params, otherwise -1. This does not apply
29 * to subparams, empty subparams should always be added with -1.
31 export class Params implements IParams {
32 // params store and length
33 public params: Int32Array;
34 public length: number;
36 // sub params store and length
37 protected _subParams: Int32Array;
38 protected _subParamsLength: number;
40 // sub params offsets from param: param idx --> [start, end] offset
41 private _subParamsIdx: Uint16Array;
42 private _rejectDigits: boolean;
43 private _rejectSubDigits: boolean;
44 private _digitIsSub: boolean;
47 * Create a `Params` type from JS array representation.
49 public static fromArray(values: ParamsArray): Params {
50 const params = new Params();
54 // skip leading sub params
55 for (let i = (values[0] instanceof Array) ? 1 : 0; i < values.length; ++i) {
56 const value = values[i];
57 if (value instanceof Array) {
58 for (let k = 0; k < value.length; ++k) {
59 params.addSubParam(value[k]);
62 params.addParam(value);
69 * @param maxLength max length of storable parameters
70 * @param maxSubParamsLength max length of storable sub parameters
72 constructor(public maxLength: number = 32, public maxSubParamsLength: number = 32) {
73 if (maxSubParamsLength > MAX_SUBPARAMS) {
74 throw new Error('maxSubParamsLength must not be greater than 256');
76 this.params = new Int32Array(maxLength);
78 this._subParams = new Int32Array(maxSubParamsLength);
79 this._subParamsLength = 0;
80 this._subParamsIdx = new Uint16Array(maxLength);
81 this._rejectDigits = false;
82 this._rejectSubDigits = false;
83 this._digitIsSub = false;
89 public clone(): Params {
90 const newParams = new Params(this.maxLength, this.maxSubParamsLength);
91 newParams.params.set(this.params);
92 newParams.length = this.length;
93 newParams._subParams.set(this._subParams);
94 newParams._subParamsLength = this._subParamsLength;
95 newParams._subParamsIdx.set(this._subParamsIdx);
96 newParams._rejectDigits = this._rejectDigits;
97 newParams._rejectSubDigits = this._rejectSubDigits;
98 newParams._digitIsSub = this._digitIsSub;
103 * Get a JS array representation of the current parameters and sub parameters.
104 * The array is structured as follows:
105 * sequence: "1;2:3:4;5::6"
106 * array : [1, 2, [3, 4], 5, [-1, 6]]
108 public toArray(): ParamsArray {
109 const res: ParamsArray = [];
110 for (let i = 0; i < this.length; ++i) {
111 res.push(this.params[i]);
112 const start = this._subParamsIdx[i] >> 8;
113 const end = this._subParamsIdx[i] & 0xFF;
114 if (end - start > 0) {
115 res.push(Array.prototype.slice.call(this._subParams, start, end));
122 * Reset to initial empty state.
124 public reset(): void {
126 this._subParamsLength = 0;
127 this._rejectDigits = false;
128 this._rejectSubDigits = false;
129 this._digitIsSub = false;
133 * Add a parameter value.
134 * `Params` only stores up to `maxLength` parameters, any later
135 * parameter will be ignored.
136 * Note: VT devices only stored up to 16 values, xterm seems to
139 public addParam(value: number): void {
140 this._digitIsSub = false;
141 if (this.length >= this.maxLength) {
142 this._rejectDigits = true;
146 throw new Error('values lesser than -1 are not allowed');
148 this._subParamsIdx[this.length] = this._subParamsLength << 8 | this._subParamsLength;
149 this.params[this.length++] = value > MAX_VALUE ? MAX_VALUE : value;
153 * Add a sub parameter value.
154 * The sub parameter is automatically associated with the last parameter value.
155 * Thus it is not possible to add a subparameter without any parameter added yet.
156 * `Params` only stores up to `subParamsLength` sub parameters, any later
157 * sub parameter will be ignored.
159 public addSubParam(value: number): void {
160 this._digitIsSub = true;
164 if (this._rejectDigits || this._subParamsLength >= this.maxSubParamsLength) {
165 this._rejectSubDigits = true;
169 throw new Error('values lesser than -1 are not allowed');
171 this._subParams[this._subParamsLength++] = value > MAX_VALUE ? MAX_VALUE : value;
172 this._subParamsIdx[this.length - 1]++;
176 * Whether parameter at index `idx` has sub parameters.
178 public hasSubParams(idx: number): boolean {
179 return ((this._subParamsIdx[idx] & 0xFF) - (this._subParamsIdx[idx] >> 8) > 0);
183 * Return sub parameters for parameter at index `idx`.
184 * Note: The values are borrowed, thus you need to copy
185 * the values if you need to hold them in nonlocal scope.
187 public getSubParams(idx: number): Int32Array | null {
188 const start = this._subParamsIdx[idx] >> 8;
189 const end = this._subParamsIdx[idx] & 0xFF;
190 if (end - start > 0) {
191 return this._subParams.subarray(start, end);
197 * Return all sub parameters as {idx: subparams} mapping.
198 * Note: The values are not borrowed.
200 public getSubParamsAll(): {[idx: number]: Int32Array} {
201 const result: {[idx: number]: Int32Array} = {};
202 for (let i = 0; i < this.length; ++i) {
203 const start = this._subParamsIdx[i] >> 8;
204 const end = this._subParamsIdx[i] & 0xFF;
205 if (end - start > 0) {
206 result[i] = this._subParams.slice(start, end);
213 * Add a single digit value to current parameter.
214 * This is used by the parser to account digits on a char by char basis.
216 public addDigit(value: number): void {
218 if (this._rejectDigits
219 || !(length = this._digitIsSub ? this._subParamsLength : this.length)
220 || (this._digitIsSub && this._rejectSubDigits)
225 const store = this._digitIsSub ? this._subParams : this.params;
226 const cur = store[length - 1];
227 store[length - 1] = ~cur ? Math.min(cur * 10 + value, MAX_VALUE) : value;