-
Notifications
You must be signed in to change notification settings - Fork 67
Expand file tree
/
Copy pathdecodingUtils.ts
More file actions
196 lines (170 loc) · 6.15 KB
/
Copy pathdecodingUtils.ts
File metadata and controls
196 lines (170 loc) · 6.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
import type IntWrapper from "./intWrapper";
import { VectorType } from "../vector/vectorType";
import type BitVector from "../vector/flat/bitVector";
import { decodeStreamMetadata } from "../metadata/tile/streamMetadataDecoder";
import { unpackNullableBoolean, unpackNullable } from "./unpackNullableUtils";
export function skipColumn(numStreams: number, tile: Uint8Array, offset: IntWrapper) {
//TODO: add size of column in Mlt for fast skipping
for (let i = 0; i < numStreams; i++) {
const streamMetadata = decodeStreamMetadata(tile, offset);
offset.add(streamMetadata.byteLength);
}
}
export function decodeBooleanRle(
buffer: Uint8Array,
numBooleans: number,
byteLength: number,
pos: IntWrapper,
nullabilityBuffer?: BitVector,
): Uint8Array {
const numBytes = Math.ceil(numBooleans / 8.0);
const values = decodeByteRle(buffer, numBytes, byteLength, pos);
if (nullabilityBuffer) {
return unpackNullableBoolean(values, numBooleans, nullabilityBuffer);
}
return values;
}
export function decodeByteRle(buffer: Uint8Array, numBytes: number, byteLength: number, pos: IntWrapper): Uint8Array {
const values = new Uint8Array(numBytes);
let valueOffset = 0;
const streamEndPos = pos.get() + byteLength;
while (valueOffset < numBytes) {
if (pos.get() >= streamEndPos) {
break;
}
const header = buffer[pos.increment()];
/* Runs */
if (header <= 0x7f) {
const numRuns = header + 3;
const value = buffer[pos.increment()];
const endValueOffset = Math.min(valueOffset + numRuns, numBytes);
values.fill(value, valueOffset, endValueOffset);
valueOffset = endValueOffset;
} else {
/* Literals */
const numLiterals = 256 - header;
for (let i = 0; i < numLiterals && valueOffset < numBytes; i++) {
values[valueOffset++] = buffer[pos.increment()];
}
}
}
pos.set(streamEndPos);
return values;
}
export function decodeFloatsLE(
encodedValues: Uint8Array,
pos: IntWrapper,
numValues: number,
nullabilityBuffer?: BitVector,
): Float32Array {
const currentPos = pos.get();
const newOffset = currentPos + numValues * Float32Array.BYTES_PER_ELEMENT;
const newBuf = new Uint8Array(encodedValues.subarray(currentPos, newOffset)).buffer;
const fb = new Float32Array(newBuf);
pos.set(newOffset);
if (nullabilityBuffer) {
return unpackNullable(fb, nullabilityBuffer, 0);
}
return fb;
}
export function decodeDoublesLE(
encodedValues: Uint8Array,
pos: IntWrapper,
numValues: number,
nullabilityBuffer?: BitVector,
): Float64Array {
const currentPos = pos.get();
const newOffset = currentPos + numValues * Float64Array.BYTES_PER_ELEMENT;
const newBuf = new Uint8Array(encodedValues.subarray(currentPos, newOffset)).buffer;
const fb = new Float64Array(newBuf);
pos.set(newOffset);
if (nullabilityBuffer) {
return unpackNullable(fb, nullabilityBuffer, 0);
}
return fb;
}
const TEXT_DECODER_MIN_LENGTH = 12;
const utf8TextDecoder = new TextDecoder();
// Source: https://github.com/mapbox/pbf/issues/106
export function decodeString(buf: Uint8Array, pos: number, end: number): string {
if (end - pos >= TEXT_DECODER_MIN_LENGTH) {
// longer strings are fast with the built-in browser TextDecoder API
return utf8TextDecoder.decode(buf.subarray(pos, end));
}
// short strings are fast with custom implementation
return readUtf8(buf, pos, end);
}
function readUtf8(buf: Uint8Array, pos: number, end: number): string {
let str = "";
let i = pos;
while (i < end) {
const b0 = buf[i];
let c = null; // codepoint
let bytesPerSequence = b0 > 0xef ? 4 : b0 > 0xdf ? 3 : b0 > 0xbf ? 2 : 1;
if (i + bytesPerSequence > end) break;
let b1;
let b2;
let b3;
if (bytesPerSequence === 1) {
if (b0 < 0x80) {
c = b0;
}
} else if (bytesPerSequence === 2) {
b1 = buf[i + 1];
if ((b1 & 0xc0) === 0x80) {
c = ((b0 & 0x1f) << 0x6) | (b1 & 0x3f);
if (c <= 0x7f) {
c = null;
}
}
} else if (bytesPerSequence === 3) {
b1 = buf[i + 1];
b2 = buf[i + 2];
if ((b1 & 0xc0) === 0x80 && (b2 & 0xc0) === 0x80) {
c = ((b0 & 0xf) << 0xc) | ((b1 & 0x3f) << 0x6) | (b2 & 0x3f);
if (c <= 0x7ff || (c >= 0xd800 && c <= 0xdfff)) {
c = null;
}
}
} else if (bytesPerSequence === 4) {
b1 = buf[i + 1];
b2 = buf[i + 2];
b3 = buf[i + 3];
if ((b1 & 0xc0) === 0x80 && (b2 & 0xc0) === 0x80 && (b3 & 0xc0) === 0x80) {
c = ((b0 & 0xf) << 0x12) | ((b1 & 0x3f) << 0xc) | ((b2 & 0x3f) << 0x6) | (b3 & 0x3f);
if (c <= 0xffff || c >= 0x110000) {
c = null;
}
}
}
if (c === null) {
c = 0xfffd;
bytesPerSequence = 1;
} else if (c > 0xffff) {
c -= 0x10000;
str += String.fromCharCode(((c >>> 10) & 0x3ff) | 0xd800);
c = 0xdc00 | (c & 0x3ff);
}
str += String.fromCharCode(c);
i += bytesPerSequence;
}
return str;
}
export function getVectorTypeBooleanStream(
numFeatures: number,
byteLength: number,
data: Uint8Array,
offset: IntWrapper,
): VectorType {
const valuesPerRun = 0x83;
// TODO: use VectorType metadata field for to test which VectorType is used
return Math.ceil(numFeatures / valuesPerRun) * 2 === byteLength &&
/* Test the first value byte if all bits are set to true */
(data[offset.get() + 1] & 0xff) === (bitCount(numFeatures) << 2) - 1
? VectorType.CONST
: VectorType.FLAT;
}
function bitCount(number: number): number {
//TODO: refactor to get rid of special case handling
return number === 0 ? 1 : Math.floor(Math.log2(number) + 1);
}