@ferrybig/cgeodata-reader 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/binary-format.md +271 -17
- package/dist/chunk.d.ts +3 -2
- package/dist/chunk.js +10 -2
- package/dist/format.d.ts +1 -0
- package/dist/format.js +1 -0
- package/dist/header.d.ts +3 -2
- package/dist/header.js +1 -1
- package/dist/rangeChunk.d.ts +46 -0
- package/dist/rangeChunk.js +401 -0
- package/dist/rangeDecoder.d.ts +17 -0
- package/dist/rangeDecoder.js +129 -0
- package/dist/rangeDecoderWasm.d.ts +2 -0
- package/dist/rangeDecoderWasm.js +4 -0
- package/dist/reader.js +5 -1
- package/package.json +1 -1
package/binary-format.md
CHANGED
|
@@ -184,6 +184,11 @@ self-contained: it can be inflated without any other block. Readers
|
|
|
184
184
|
locate a block by the byte lengths recorded in the file, not by scanning
|
|
185
185
|
for stream boundaries.
|
|
186
186
|
|
|
187
|
+
In a version `2` file ([Section 5](#5-header)) chunks are not compressed
|
|
188
|
+
blocks: a chunk is stored as [Section 8.7](#87-version-2-chunks) lays it
|
|
189
|
+
out, with its own entropy coding. Name blocks are compressed blocks in
|
|
190
|
+
every version.
|
|
191
|
+
|
|
187
192
|
## 4. File Layout
|
|
188
193
|
|
|
189
194
|
A CGEODATA file consists of four consecutive sections, with no padding or
|
|
@@ -223,7 +228,7 @@ and a CGEODATA file never refers to them.
|
|
|
223
228
|
| Field | Type | Description |
|
|
224
229
|
|----------------------|------------|-------------|
|
|
225
230
|
| `signature` | 8 bytes | The ASCII bytes `CGEODATA` (`43 47 45 4f 44 41 54 41`). |
|
|
226
|
-
| `version` | varint | File kind and format version, `kind × 16 + version`. A CGEODATA file is kind `0`; this document specifies version `
|
|
231
|
+
| `version` | varint | File kind and format version, `kind × 16 + version`. A CGEODATA file is kind `0`; this document specifies version `2`, so the field is `2`. See below. |
|
|
227
232
|
| `attribution_length` | varint | Byte length of `attribution`. |
|
|
228
233
|
| `attribution` | bytes | UTF-8 text, MAY contain Markdown, intended for display to humans, for example `[OpenStreetMap](https://openstreetmap.org/copyright)`. MAY be empty. |
|
|
229
234
|
| `width_exp` | u8 | The chunk grid has `W = 2^width_exp` columns. |
|
|
@@ -253,10 +258,12 @@ Version `0` of any kind is a pre-release version: files written under
|
|
|
253
258
|
this document carry no compatibility promise once the version number
|
|
254
259
|
changes.
|
|
255
260
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
derives from the depths ([Section 8.6](#86-version-0-chunks)).
|
|
259
|
-
|
|
261
|
+
Versions `0`, `1` and `2` of kind `0` differ only in how a chunk is
|
|
262
|
+
stored. Version `0` stored every record's Morton code, which version `1`
|
|
263
|
+
derives from the depths ([Section 8.6](#86-version-0-chunks)). Version
|
|
264
|
+
`2` keeps version `1`'s records but stores them range coded instead of
|
|
265
|
+
in a zlib stream ([Section 8.7](#87-version-2-chunks)). A reader MAY read
|
|
266
|
+
all three.
|
|
260
267
|
|
|
261
268
|
Constraints:
|
|
262
269
|
|
|
@@ -641,6 +648,201 @@ million records took 3.77 million index-`0` records (3.5% more records)
|
|
|
641
648
|
to fill, and the chunks went from 62.2 MB to 45.6 MB (-26.7%), with the
|
|
642
649
|
same cell for every place.
|
|
643
650
|
|
|
651
|
+
### 8.7. Version 2 Chunks
|
|
652
|
+
|
|
653
|
+
In a version `2` file ([Section 5](#5-header)), a chunk's records are
|
|
654
|
+
those of a version `1` chunk ([Sections 8.4](#84-records) and
|
|
655
|
+
[8.5](#85-invariants)): they tile the chunk from code `0` in Morton order,
|
|
656
|
+
and MAY end before the chunk does. Only their storage differs. The block
|
|
657
|
+
is not a zlib stream:
|
|
658
|
+
|
|
659
|
+
| Field | Type | Description |
|
|
660
|
+
|-------------------|----------------------------|-------------|
|
|
661
|
+
| `palette_count` | varint | As in Section 8.2, and below `2^26`. |
|
|
662
|
+
| `palette` | `P` varints, delta-coded | As in Section 8.2. |
|
|
663
|
+
| `record_count` | varint | As in Section 8.2. |
|
|
664
|
+
| `stream` | bytes | The rest of the block: the records' depths and palette indices, range coded as described below. |
|
|
665
|
+
|
|
666
|
+
The stream codes the chunk's quadtree as a series of binary decisions:
|
|
667
|
+
whether each node splits, and, for each leaf, which palette index it has.
|
|
668
|
+
Every decision is made with an adaptive probability chosen by a
|
|
669
|
+
**context** from what was already decoded: chiefly the records to the
|
|
670
|
+
west and south of the node, which in Morton order always come first.
|
|
671
|
+
Neighbouring cells usually belong to the same place, so most decisions
|
|
672
|
+
are nearly certain and cost a small fraction of a bit.
|
|
673
|
+
|
|
674
|
+
#### 8.7.1. Range Decoder
|
|
675
|
+
|
|
676
|
+
The decoder keeps two unsigned 32-bit integers, `range` and `code`, and a
|
|
677
|
+
position `pos` in `stream`. Each probability `p` is an integer in
|
|
678
|
+
`[1, 4095]`, the chance out of `4096` that a decision is `0`; every
|
|
679
|
+
probability starts at `2048`.
|
|
680
|
+
|
|
681
|
+
`stream[0]` MUST be `0`. `code` starts as `stream[1..4]` read as a
|
|
682
|
+
big-endian integer, and MUST NOT be `0xFFFFFFFF`; `range` starts as
|
|
683
|
+
`0xFFFFFFFF` and `pos` as `5`. A decision with probability `p` is decoded
|
|
684
|
+
as follows, and `p` is updated in place:
|
|
685
|
+
|
|
686
|
+
```
|
|
687
|
+
bound = (range >> 12) × p
|
|
688
|
+
if code < bound:
|
|
689
|
+
range = bound
|
|
690
|
+
p = p + ((4096 - p) >> 4)
|
|
691
|
+
bit = 0
|
|
692
|
+
else:
|
|
693
|
+
code = code - bound
|
|
694
|
+
range = range - bound
|
|
695
|
+
p = p - (p >> 4)
|
|
696
|
+
bit = 1
|
|
697
|
+
while range < 2^24:
|
|
698
|
+
range = range × 256
|
|
699
|
+
code = code × 256 + stream[pos] # reading past the end is an error
|
|
700
|
+
pos = pos + 1
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
After the last record, `pos` MUST equal the length of `stream`. This is
|
|
704
|
+
LZMA's range coder [LZMA] with 12-bit probabilities that adapt by a
|
|
705
|
+
sixteenth each time. The matching encoder (informative):
|
|
706
|
+
|
|
707
|
+
```
|
|
708
|
+
low = 0 (at least 33 bits), range = 0xFFFFFFFF, cache = 0, pending = 1
|
|
709
|
+
|
|
710
|
+
encode(bit, p):
|
|
711
|
+
bound = (range >> 12) × p
|
|
712
|
+
if bit == 0: range = bound; p = p + ((4096 - p) >> 4)
|
|
713
|
+
else: low = low + bound; range = range - bound; p = p - (p >> 4)
|
|
714
|
+
while range < 2^24: range = range × 256; shift_low()
|
|
715
|
+
|
|
716
|
+
shift_low():
|
|
717
|
+
if low < 0xFF000000 or low >= 2^32:
|
|
718
|
+
carry = low >> 32
|
|
719
|
+
emit (cache + carry) & 0xFF, then pending - 1 bytes of (0xFF + carry) & 0xFF
|
|
720
|
+
pending = 0
|
|
721
|
+
cache = (low >> 24) & 0xFF
|
|
722
|
+
pending = pending + 1
|
|
723
|
+
low = (low & 0x00FFFFFF) × 256
|
|
724
|
+
|
|
725
|
+
finish(): shift_low() five times.
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
Its first byte is always `0`, and the decoder then reads exactly the
|
|
729
|
+
bytes it wrote.
|
|
730
|
+
|
|
731
|
+
#### 8.7.2. Nodes and Neighbours
|
|
732
|
+
|
|
733
|
+
A node is a cell of the chunk's quadtree: its depth `d` and its south-west
|
|
734
|
+
corner `(x, y)`, in full-precision cells ([Section 8.1](#81-cell-grid-and-morton-codes)).
|
|
735
|
+
Nodes are visited in pre-order from the root `(d = 0, x = 0, y = 0)`. A
|
|
736
|
+
node at depth `d < D` first decodes a **split** decision
|
|
737
|
+
([Section 8.7.3](#873-contexts)): `1` visits its four children of depth
|
|
738
|
+
`d + 1` in Morton order, south-west `(x, y)`, south-east `(x + h, y)`,
|
|
739
|
+
north-west `(x, y + h)` and north-east `(x + h, y + h)` with
|
|
740
|
+
`h = 2^(D - d - 1)`; `0` makes it a leaf. A node at depth `D` is a leaf
|
|
741
|
+
without a decision. Each leaf is the next record: its depth is `d`, its
|
|
742
|
+
code is where the record before it ends ([Section 8.4](#84-records)), and
|
|
743
|
+
its palette index is decoded as [Section 8.7.4](#874-palette-indices)
|
|
744
|
+
describes. Decoding stops after `record_count` records, wherever the
|
|
745
|
+
traversal is.
|
|
746
|
+
|
|
747
|
+
A node's **west neighbour** is the record that contains the
|
|
748
|
+
full-precision cell `(x - 1, y)`, and its **south neighbour** the one
|
|
749
|
+
that contains `(x, y - 1)`. There is none when `x = 0` (respectively
|
|
750
|
+
`y = 0`): neighbours never come from another chunk. Both cells have
|
|
751
|
+
lower Morton codes than `(x, y)` and so were decoded before the node. A
|
|
752
|
+
decoder can find them by remembering, for every row and every column of
|
|
753
|
+
full-precision cells, the last record decoded in it: in Morton order the
|
|
754
|
+
last record in row `y` before `(x, y)` is the one at `(x - 1, y)`, and
|
|
755
|
+
likewise for columns.
|
|
756
|
+
|
|
757
|
+
A neighbour's **category** relative to a node at depth `d` is `0` when
|
|
758
|
+
there is no neighbour, `1` when its record's depth is at most `d`, `2`
|
|
759
|
+
when it is `d + 1`, and `3` when it is deeper.
|
|
760
|
+
|
|
761
|
+
#### 8.7.3. Contexts
|
|
762
|
+
|
|
763
|
+
There are five tables of probabilities, each indexed by a context:
|
|
764
|
+
|
|
765
|
+
| Table | Entries | Used for |
|
|
766
|
+
|---------------|---------|----------|
|
|
767
|
+
| `split` | 256 | split decisions, at `16 × d + 4 × cw + cs` |
|
|
768
|
+
| `same_west` | 544 | "the index is the west neighbour's", at `c` |
|
|
769
|
+
| `same_south` | 544 | "the index is the south neighbour's", at `c` |
|
|
770
|
+
| `rank_length` | 128 | the unary part of a rank ([Section 8.7.4](#874-palette-indices)) |
|
|
771
|
+
| `rank_bits` | 1024 | the binary part of a rank |
|
|
772
|
+
|
|
773
|
+
`cw` and `cs` are the west and south neighbours' categories. For a leaf,
|
|
774
|
+
let `w` and `s` be the west and south neighbours' palette indices, or
|
|
775
|
+
`-1` for a missing one, and `prev` the previous record's palette index,
|
|
776
|
+
or `-1` for the first record. Then
|
|
777
|
+
|
|
778
|
+
```
|
|
779
|
+
c = 4 × (8 × d + 4 × [w = s] + 2 × [prev = w] + [prev = s]) + cw
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
where `[...]` is `1` when the comparison holds and `0` otherwise, and
|
|
783
|
+
`-1` equals `-1`.
|
|
784
|
+
|
|
785
|
+
#### 8.7.4. Palette Indices
|
|
786
|
+
|
|
787
|
+
The decoder keeps a **move-to-front list** of the palette indices
|
|
788
|
+
`0` to `P`, initially in that order. A leaf's palette index `v` is
|
|
789
|
+
decoded as follows:
|
|
790
|
+
|
|
791
|
+
1. If there is a west neighbour, a decision in `same_west[c]`: `1` means
|
|
792
|
+
`v = w`.
|
|
793
|
+
2. Otherwise, or if that decision was `0`: if there is a south
|
|
794
|
+
neighbour and `s != w`, a decision in `same_south[c]`: `1` means
|
|
795
|
+
`v = s`.
|
|
796
|
+
3. Otherwise, a **rank** `r`: `v` is the entry of the move-to-front list
|
|
797
|
+
that has `r` entries before it, not counting entries equal to `w` or
|
|
798
|
+
`s`. `r + 1` is coded in Elias gamma: let `a` be `1` if there is a
|
|
799
|
+
west neighbour plus `2` if there is a south neighbour. Decisions in
|
|
800
|
+
`rank_length[32 × a + k]`, for `k = 0, 1, ...`, are `1` until one is
|
|
801
|
+
`0`; their number of `1`s is `n`, and MUST be below `32`. Then `n`
|
|
802
|
+
decisions in `rank_bits[32 × n + k]`, for `k = n - 1` down to `0`,
|
|
803
|
+
are the bits of `r + 1` below its leading `1`, most significant
|
|
804
|
+
first. The list MUST have such an entry.
|
|
805
|
+
|
|
806
|
+
Then `v` is moved to the front of the list.
|
|
807
|
+
|
|
808
|
+
#### 8.7.5. Bounds and Errors
|
|
809
|
+
|
|
810
|
+
A decoder MUST reject a block where `record_count` exceeds `4^D` (more
|
|
811
|
+
records than cells), or exceeds `2048` times the length of `stream`, and
|
|
812
|
+
MUST check both before allocating for the records. No stream reaches the
|
|
813
|
+
second bound: every record takes at least one decision, a decision
|
|
814
|
+
takes at least `log2(4096 / 4081)` bits, as no probability passes
|
|
815
|
+
`4081` or drops below `15`, so a byte codes at most about 1500 records.
|
|
816
|
+
|
|
817
|
+
It MUST also reject a block whose traversal ends (every node visited)
|
|
818
|
+
before `record_count` records, and any of the conditions marked MUST in
|
|
819
|
+
Sections 8.7.1 and 8.7.4. Every record is valid by construction: its
|
|
820
|
+
depth is at most `D`, it is aligned, it ends within the chunk, and its
|
|
821
|
+
palette index is at most `P`.
|
|
822
|
+
|
|
823
|
+
#### 8.7.6. Design Notes
|
|
824
|
+
|
|
825
|
+
This section is informative.
|
|
826
|
+
|
|
827
|
+
The range coder replaced zlib for chunks because the data has structure
|
|
828
|
+
a general-purpose compressor does not see. A version `1` chunk stores
|
|
829
|
+
each depth in a byte, though the depths are just a pre-order listing of
|
|
830
|
+
the quadtree's split flags, which are well predicted by the neighbours'
|
|
831
|
+
depths. Its palette indices are well predicted by the west and south
|
|
832
|
+
neighbours, which in Morton order are not usually the previous record:
|
|
833
|
+
in the worldwide file, 74% of records have their west neighbour's index
|
|
834
|
+
and 19% their south neighbour's, which zlib, looking at one byte column,
|
|
835
|
+
cannot exploit. Measured on the worldwide build (7,608 chunks,
|
|
836
|
+
110 million records), the chunks went from 45.6 MB as version `1` to
|
|
837
|
+
18.5 MB (−59.5%), and the file from 47.6 MB to 20.5 MB.
|
|
838
|
+
|
|
839
|
+
The cost is decoding speed: about 2.5 binary decisions per record, each
|
|
840
|
+
data-dependent, make a chunk about 4 times as slow to decode as to
|
|
841
|
+
inflate and parse in version `1`. Encoding is faster than zlib's highest
|
|
842
|
+
level. Other layouts were measured on the same file: zlib over split
|
|
843
|
+
flags and neighbour-predicted symbols gave 30.2 MB, and Zopfli, a better
|
|
844
|
+
deflate encoder for version `1` unchanged, about 37.4 MB.
|
|
845
|
+
|
|
644
846
|
## 9. Lookup Procedure
|
|
645
847
|
|
|
646
848
|
To resolve a coordinate `(lat, lon)` to a place ID, a reader performs the
|
|
@@ -667,9 +869,10 @@ following steps. `W`, `H`, and `D` come from the header.
|
|
|
667
869
|
avoids inconsistent rounding between the two.
|
|
668
870
|
4. **Fetch the chunk.** If `chunk_lengths[i]` is `0`, the result is
|
|
669
871
|
unknown. Otherwise read `length(i)` bytes at `offset(i)`
|
|
670
|
-
([Section 6.3](#63-locating-a-chunk)) and inflate them
|
|
671
|
-
|
|
672
|
-
|
|
872
|
+
([Section 6.3](#63-locating-a-chunk)) and inflate them (in a version
|
|
873
|
+
`2` file, decode them per [Section 8.7](#87-version-2-chunks)), and
|
|
874
|
+
compute the records' codes ([Section 8.4](#84-records)). Readers
|
|
875
|
+
SHOULD cache decoded chunks with their codes.
|
|
673
876
|
5. **Compute the query code** `q` from `(x, y)` with the procedure of
|
|
674
877
|
[Section 8.1](#81-cell-grid-and-morton-codes).
|
|
675
878
|
6. **Binary search.** Find the largest `j` such that `codes[j] <= q`
|
|
@@ -738,12 +941,15 @@ record at all.
|
|
|
738
941
|
|
|
739
942
|
**Keyframe interval.** See [Section 7.3](#73-name-blocks).
|
|
740
943
|
|
|
741
|
-
**Compression.**
|
|
742
|
-
zero-dependency built-in in Node.js and browsers. The
|
|
743
|
-
delta-coded layout captures most of the gain regardless of
|
|
744
|
-
compressor; on the prototype, zstd and brotli were only 10–20%
|
|
745
|
-
Writers SHOULD use the highest zlib
|
|
746
|
-
one-time cost.
|
|
944
|
+
**Compression.** Versions `0` and `1` chose zlib over zstd or brotli
|
|
945
|
+
because it is a zero-dependency built-in in Node.js and browsers. The
|
|
946
|
+
columnar and delta-coded layout captures most of the gain regardless of
|
|
947
|
+
the compressor; on the prototype, zstd and brotli were only 10–20%
|
|
948
|
+
smaller. Writers of those versions SHOULD use the highest zlib
|
|
949
|
+
compression level; compression is a one-time cost. Version `2`'s range
|
|
950
|
+
coder ([Section 8.7](#87-version-2-chunks)) is a model of the data, not
|
|
951
|
+
a general-purpose compressor, and needs no library; see
|
|
952
|
+
[Section 8.7.6](#876-design-notes) for what it gains and costs.
|
|
747
953
|
|
|
748
954
|
## 11. Robustness and Security Considerations
|
|
749
955
|
|
|
@@ -765,6 +971,10 @@ assume that a file is well formed.
|
|
|
765
971
|
verify the alignment invariant of [Section 8.5](#85-invariants), and
|
|
766
972
|
in a version `0` file the ordering and non-overlap invariants of
|
|
767
973
|
[Section 8.6](#86-version-0-chunks).
|
|
974
|
+
- In a version `2` file, readers MUST check [Section 8.7.5](#875-bounds-and-errors)'s
|
|
975
|
+
bounds on `record_count` before allocating for it, and reject a stream
|
|
976
|
+
that runs past the end of its block or ends before it. The records'
|
|
977
|
+
depths, codes and palette indices are valid by construction.
|
|
768
978
|
- Readers MUST treat the attribution text and place names as untrusted
|
|
769
979
|
text. Where they are rendered as Markdown or HTML, they MUST be
|
|
770
980
|
sanitised.
|
|
@@ -773,7 +983,7 @@ assume that a file is well formed.
|
|
|
773
983
|
|
|
774
984
|
## 12. Open Issues
|
|
775
985
|
|
|
776
|
-
The following are known gaps or unresolved decisions in version `
|
|
986
|
+
The following are known gaps or unresolved decisions in version `2`.
|
|
777
987
|
|
|
778
988
|
1. **Latitude scaling.** Because the grid is equirectangular, cells
|
|
779
989
|
shrink in east-west extent towards the poles. A per-row scaling
|
|
@@ -801,8 +1011,9 @@ The following are known gaps or unresolved decisions in version `1`.
|
|
|
801
1011
|
|
|
802
1012
|
## Appendix A. Worked Example
|
|
803
1013
|
|
|
804
|
-
This appendix gives a complete, valid, synthetic file of
|
|
805
|
-
walks through several lookups
|
|
1014
|
+
This appendix gives a complete, valid, synthetic version `1` file of
|
|
1015
|
+
157 bytes and walks through several lookups; Section A.6 gives its chunk
|
|
1016
|
+
as version `2` stores it. Its geography is fictional. The file uses
|
|
806
1017
|
a `2 × 2` chunk grid, a quadtree depth of `2`, and name blocks of `4`
|
|
807
1018
|
entries.
|
|
808
1019
|
|
|
@@ -963,6 +1174,46 @@ point lies after the last record: **unknown**.
|
|
|
963
1174
|
`c = 1, r = 0`, index `1`. Its length is `0`: **unknown**, with no
|
|
964
1175
|
decompression performed.
|
|
965
1176
|
|
|
1177
|
+
### A.6. Chunk 3 as Version 2 (11 bytes)
|
|
1178
|
+
|
|
1179
|
+
As a version `2` file, the example is 142 bytes: the header's `version`
|
|
1180
|
+
byte is `02`, the directory's last chunk length `0b`, and chunk `3` is
|
|
1181
|
+
stored as follows ([Section 8.7](#87-version-2-chunks)):
|
|
1182
|
+
|
|
1183
|
+
```
|
|
1184
|
+
02 palette_count P = 2
|
|
1185
|
+
02 03 palette deltas 2, 3 -> IDs 2, 5
|
|
1186
|
+
07 record_count M = 7
|
|
1187
|
+
00 a5 11 64 ba 00 00 stream
|
|
1188
|
+
```
|
|
1189
|
+
|
|
1190
|
+
The stream holds these decisions, in order. `W` and `S` are the west
|
|
1191
|
+
and south neighbours' records, `L` the move-to-front list before the
|
|
1192
|
+
leaf; every probability used is still at `2048` unless it was used
|
|
1193
|
+
before.
|
|
1194
|
+
|
|
1195
|
+
| Node `(d; x, y)` | Neighbours | Decisions | Record |
|
|
1196
|
+
|------------------|-----------------------------|----------------------------------------------------|--------|
|
|
1197
|
+
| `(0; 0, 0)` | none | `split[0] = 1` | |
|
|
1198
|
+
| `(1; 0, 0)` | none | `split[16] = 0`; `L = 0 1 2`, `r = 1`: `rank_length[0] = 1`, `rank_length[1] = 0`, `rank_bits[32] = 0` | 0: depth 1, index 1 |
|
|
1199
|
+
| `(1; 2, 0)` | W record 0 | `split[20] = 1` | |
|
|
1200
|
+
| `(2; 2, 0)` | W record 0 | `same_west[73] = 0`; `L = 1 0 2`, skipping `1`, `r = 1`: `rank_length[32] = 1`, `rank_length[33] = 0`, `rank_bits[32] = 0` | 1: depth 2, index 2 |
|
|
1201
|
+
| `(2; 3, 0)` | W record 1 | `same_west[73] = 0`; `L = 2 1 0`, skipping `2`, `r = 1`: `rank_length[32] = 1`, `rank_length[33] = 0`, `rank_bits[32] = 0` | 2: depth 2, index 0 |
|
|
1202
|
+
| `(2; 2, 1)` | W record 0, S record 1 | `same_west[65] = 0`, `same_south[65] = 0`; `L = 0 2 1`, skipping `1` and `2`, `r = 0`: `rank_length[96] = 0` | 3: depth 2, index 0 |
|
|
1203
|
+
| `(2; 3, 1)` | W record 3, S record 2 | `same_west[93] = 1` | 4: depth 2, index 0 |
|
|
1204
|
+
| `(1; 0, 2)` | S record 0 | `split[17] = 0`; `same_south[32] = 1` | 5: depth 1, index 1 |
|
|
1205
|
+
| `(1; 2, 2)` | W record 5, S record 3 | `split[22] = 1` | |
|
|
1206
|
+
| `(2; 2, 2)` | W record 5, S record 3 | `same_west[73] = 0`, `same_south[73] = 0`; `L = 1 0 2`, skipping `1` and `0`, `r = 0`: `rank_length[96] = 0` | 6: depth 2, index 2 |
|
|
1207
|
+
|
|
1208
|
+
For example, at `(2; 3, 1)` both neighbours have index `0`, as had the
|
|
1209
|
+
previous record, so `c = 4 × (8 × 2 + 4 + 2 + 1) + 1 = 93`, and since
|
|
1210
|
+
`s = w` no `same_south` decision follows. At `(1; 2, 2)` the south
|
|
1211
|
+
neighbour, record 3, is one level deeper than the node (category `2`),
|
|
1212
|
+
so `split[16 × 1 + 4 × 1 + 2] = split[22]`. Decoding stops after the
|
|
1213
|
+
seventh record, with three nodes of the traversal still unvisited, and
|
|
1214
|
+
`pos` is then `7`, the length of the stream. The records are those of
|
|
1215
|
+
Section A.4.
|
|
1216
|
+
|
|
966
1217
|
## Appendix B. Text Search Index
|
|
967
1218
|
|
|
968
1219
|
This appendix specifies an OPTIONAL sidecar file that speeds up substring
|
|
@@ -2341,6 +2592,9 @@ A full-precision cell of this file is `45°` wide and `22.5°` high,
|
|
|
2341
2592
|
|
|
2342
2593
|
## Appendix D. References
|
|
2343
2594
|
|
|
2595
|
+
- [LZMA] Pavlov, I., "LZMA SDK", https://www.7-zip.org/sdk.html: the
|
|
2596
|
+
range coder of `LzmaDec.c`, with `kNumBitModelTotalBits` 11 and
|
|
2597
|
+
`kNumMoveBits` 5 where this format uses 12 and 4.
|
|
2344
2598
|
- [RFC1950] Deutsch, P. and J-L. Gailly, "ZLIB Compressed Data Format
|
|
2345
2599
|
Specification version 3.3", May 1996.
|
|
2346
2600
|
- [RFC1951] Deutsch, P., "DEFLATE Compressed Data Format Specification
|
package/dist/chunk.d.ts
CHANGED
|
@@ -27,8 +27,9 @@ export interface ChunkRecords {
|
|
|
27
27
|
* (`width_exp`, `height_exp`) instead (binary-format.md Section 10).
|
|
28
28
|
*/
|
|
29
29
|
export declare const MAX_SUPPORTED_DEPTH = 16;
|
|
30
|
-
/** Parses one already
|
|
31
|
-
* layout for
|
|
30
|
+
/** Parses one chunk block: already inflated for a `version` 0 or 1 file
|
|
31
|
+
* (Section 8.2, or Section 8.6's layout for `version` 0), as stored for
|
|
32
|
+
* `version` 2, whose blocks are not zlib streams (Section 8.7). It checks the cheap Section 8.5
|
|
32
33
|
* invariants a reader can verify without external context (place ids
|
|
33
34
|
* are validated by the caller, which has the place table). */
|
|
34
35
|
export declare function parseChunk(buf: Uint8Array, maxDepth: number, version: number): ChunkRecords;
|
package/dist/chunk.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { decodeChunkCodes, WASM_MIN_RECORDS } from "./chunkDecoder.js";
|
|
2
2
|
import { CGeoDataFormatError } from "./errors.js";
|
|
3
|
+
import { decodeRangeCodedRecords } from "./rangeChunk.js";
|
|
3
4
|
import { readDeltaVarintList, readDeltaVarints32Into, readVarint, } from "./varint.js";
|
|
4
5
|
/**
|
|
5
6
|
* The deepest `max_depth` this reader supports, though the format allows
|
|
@@ -75,8 +76,9 @@ function readLittleEndian(buf, offset, width, n) {
|
|
|
75
76
|
function isMultipleOf(value, span) {
|
|
76
77
|
return value < 0x80000000 ? (value & (span - 1)) === 0 : value % span === 0;
|
|
77
78
|
}
|
|
78
|
-
/** Parses one already
|
|
79
|
-
* layout for
|
|
79
|
+
/** Parses one chunk block: already inflated for a `version` 0 or 1 file
|
|
80
|
+
* (Section 8.2, or Section 8.6's layout for `version` 0), as stored for
|
|
81
|
+
* `version` 2, whose blocks are not zlib streams (Section 8.7). It checks the cheap Section 8.5
|
|
80
82
|
* invariants a reader can verify without external context (place ids
|
|
81
83
|
* are validated by the caller, which has the place table). */
|
|
82
84
|
export function parseChunk(buf, maxDepth, version) {
|
|
@@ -90,6 +92,12 @@ export function parseChunk(buf, maxDepth, version) {
|
|
|
90
92
|
offset += b2;
|
|
91
93
|
const { value: recordCount, bytesRead: b3 } = readVarint(buf, offset);
|
|
92
94
|
offset += b3;
|
|
95
|
+
if (version >= 2) {
|
|
96
|
+
return {
|
|
97
|
+
palette,
|
|
98
|
+
...decodeRangeCodedRecords(buf, offset, paletteCount, recordCount, maxDepth),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
93
101
|
if (version !== 0) {
|
|
94
102
|
return parseRecords(buf, offset, palette, recordCount, maxDepth);
|
|
95
103
|
}
|
package/dist/format.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export { type Inflater, inflateBlock, inflateCeiling, streamInflater, } from "./
|
|
|
15
15
|
export { decodeLocateEntry, encodeLocateEntry, type LocateCell, type LocateGrid, locateEntryWidth, } from "./locateIndex.ts";
|
|
16
16
|
export { lowBits, mortonDecode, mortonEncode, shiftRight } from "./morton.ts";
|
|
17
17
|
export { type PlaceTableInfo, parsePlaceTable, type ResolvedPlace, readNameBlock, readNameEntry, resolveIdentifier, } from "./placeTable.ts";
|
|
18
|
+
export { decodeRangeCodedRecords, encodeRangeCodedRecords, type RangeCodedRecords, } from "./rangeChunk.ts";
|
|
18
19
|
export { containsTokenRun, EXACT_SPELLING_BONUS, foldDiacritics, LEVEL_BONUS, type Needle, nameMatchQuality, placeMatchQuality, prepareNeedle, QUALITY_EXACT, QUALITY_MAX, QUALITY_SUBSTRING, QUALITY_WHOLE_WORD, tokenize, tokenizeFolded, } from "./relevance.ts";
|
|
19
20
|
export { fnv1a32, foldSymbols, trigramListIndex, trigramListIndices, } from "./searchIndex.ts";
|
|
20
21
|
export { toSource } from "./source.ts";
|
package/dist/format.js
CHANGED
|
@@ -15,6 +15,7 @@ export { inflateBlock, inflateCeiling, streamInflater, } from "./inflate.js";
|
|
|
15
15
|
export { decodeLocateEntry, encodeLocateEntry, locateEntryWidth, } from "./locateIndex.js";
|
|
16
16
|
export { lowBits, mortonDecode, mortonEncode, shiftRight } from "./morton.js";
|
|
17
17
|
export { parsePlaceTable, readNameBlock, readNameEntry, resolveIdentifier, } from "./placeTable.js";
|
|
18
|
+
export { decodeRangeCodedRecords, encodeRangeCodedRecords, } from "./rangeChunk.js";
|
|
18
19
|
export { containsTokenRun, EXACT_SPELLING_BONUS, foldDiacritics, LEVEL_BONUS, nameMatchQuality, placeMatchQuality, prepareNeedle, QUALITY_EXACT, QUALITY_MAX, QUALITY_SUBSTRING, QUALITY_WHOLE_WORD, tokenize, tokenizeFolded, } from "./relevance.js";
|
|
19
20
|
export { fnv1a32, foldSymbols, trigramListIndex, trigramListIndices, } from "./searchIndex.js";
|
|
20
21
|
export { toSource } from "./source.js";
|
package/dist/header.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/** A file's header (Section 5). */
|
|
2
2
|
export interface CGeoDataHeader {
|
|
3
|
-
/** Format version:
|
|
4
|
-
*
|
|
3
|
+
/** Format version: 2 (range-coded chunks, Section 8.7), 1 (zlib
|
|
4
|
+
* chunks), or 0 for files written before chunks dropped their codes
|
|
5
|
+
* (Section 8.6). This reader reads all three. */
|
|
5
6
|
version: number;
|
|
6
7
|
/** The data's attribution notice, as given when the file was built.
|
|
7
8
|
* Show it wherever results are shown. */
|
package/dist/header.js
CHANGED
|
@@ -21,7 +21,7 @@ export function parseHeader(buf) {
|
|
|
21
21
|
let offset = SIGNATURE.length;
|
|
22
22
|
const version = readVarint(buf, offset);
|
|
23
23
|
offset += version.bytesRead;
|
|
24
|
-
if (version.value
|
|
24
|
+
if (version.value > 2) {
|
|
25
25
|
throw new CGeoDataVersionError(`unsupported CGEODATA version ${version.value}`, version.value);
|
|
26
26
|
}
|
|
27
27
|
const attrLen = readVarint(buf, offset);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ChunkRecords } from "./chunk.ts";
|
|
2
|
+
/**
|
|
3
|
+
* A version 2 chunk's records (binary-format.md Section 8.7): the
|
|
4
|
+
* quadtree's split flags and each leaf's palette index, coded with an
|
|
5
|
+
* adaptive binary range coder whose contexts come from the west and south
|
|
6
|
+
* neighbours of each node. This is the JavaScript decoder, the one that
|
|
7
|
+
* reports errors; `rangeDecoder.ts`'s WebAssembly one only accepts or
|
|
8
|
+
* declines, and a declined block is decoded again here.
|
|
9
|
+
*/
|
|
10
|
+
/** A chunk's records as `decodeRangeCodedRecords` decodes them: all of
|
|
11
|
+
* `ChunkRecords` but the palette, which precedes the stream. */
|
|
12
|
+
export type RangeCodedRecords = Omit<ChunkRecords, "palette">;
|
|
13
|
+
/** Context table sizes (Section 8.7.3). */
|
|
14
|
+
export declare const SPLIT_CONTEXTS: number;
|
|
15
|
+
export declare const VALUE_CONTEXTS: number;
|
|
16
|
+
export declare const RANK_LENGTH_CONTEXTS: number;
|
|
17
|
+
export declare const RANK_BIT_CONTEXTS: number;
|
|
18
|
+
/** A version 2 chunk's palette_count is below this, so a palette index
|
|
19
|
+
* and a depth fit in one 32-bit integer together. */
|
|
20
|
+
export declare const MAX_PALETTE_COUNT: number;
|
|
21
|
+
/** At most this many records per byte of coded stream: every leaf takes at
|
|
22
|
+
* least one binary decision, which costs at least log2(4096 / 4081) bits,
|
|
23
|
+
* so 2048 records per byte can't be reached by any stream. Checked before
|
|
24
|
+
* allocating for `record_count`. */
|
|
25
|
+
export declare const MAX_RECORDS_PER_BYTE = 2048;
|
|
26
|
+
/** The narrowest typed array for palette indices up to `paletteCount`
|
|
27
|
+
* (Section 8.3's S, with 3 bytes rounded up to 4). */
|
|
28
|
+
export declare function paletteIndexArray(paletteCount: number, n: number): Uint8Array | Uint16Array | Uint32Array;
|
|
29
|
+
/**
|
|
30
|
+
* Decodes the records of a version 2 chunk from `buf[offset..]`, the
|
|
31
|
+
* range-coded stream that follows `record_count` and runs to the end of
|
|
32
|
+
* the block. Throws a `CGeoDataFormatError` for anything Section 8.7
|
|
33
|
+
* rejects.
|
|
34
|
+
*/
|
|
35
|
+
export declare function decodeRangeCodedRecords(buf: Uint8Array, offset: number, paletteCount: number, recordCount: number, maxDepth: number): RangeCodedRecords;
|
|
36
|
+
/** Section 8.7.5's bounds on `record_count`, checked before allocating
|
|
37
|
+
* for it: at most one record per cell, and at most `MAX_RECORDS_PER_BYTE`
|
|
38
|
+
* per byte of stream. */
|
|
39
|
+
export declare function checkRecordCount(recordCount: number, maxDepth: number, streamBytes: number): void;
|
|
40
|
+
/**
|
|
41
|
+
* Encodes records that tile a chunk from code 0 (Section 8.4) as Section
|
|
42
|
+
* 8.7's range-coded stream: `depths[j]` and `paletteIndices[j]` (0..P)
|
|
43
|
+
* per record, in Morton order. The inverse of `decodeRangeCodedRecords`.
|
|
44
|
+
* Throws a RangeError for records that don't tile the chunk.
|
|
45
|
+
*/
|
|
46
|
+
export declare function encodeRangeCodedRecords(depths: ArrayLike<number>, paletteIndices: ArrayLike<number>, paletteCount: number, maxDepth: number): Uint8Array;
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import { CGeoDataFormatError } from "./errors.js";
|
|
2
|
+
import { decodeRangeCodedWasm } from "./rangeDecoder.js";
|
|
3
|
+
/** Probabilities are 12-bit: the chance of a 0, out of 4096. */
|
|
4
|
+
const PROB_BITS = 12;
|
|
5
|
+
const PROB_INIT = 2048;
|
|
6
|
+
/** How fast a probability adapts: it moves 1/16 of the way each time. */
|
|
7
|
+
const ADAPT_SHIFT = 4;
|
|
8
|
+
const TOP = 2 ** 24;
|
|
9
|
+
/** Context table sizes (Section 8.7.3). */
|
|
10
|
+
export const SPLIT_CONTEXTS = 16 * 16;
|
|
11
|
+
export const VALUE_CONTEXTS = 17 * 8 * 4;
|
|
12
|
+
export const RANK_LENGTH_CONTEXTS = 4 * 32;
|
|
13
|
+
export const RANK_BIT_CONTEXTS = 32 * 32;
|
|
14
|
+
/** A version 2 chunk's palette_count is below this, so a palette index
|
|
15
|
+
* and a depth fit in one 32-bit integer together. */
|
|
16
|
+
export const MAX_PALETTE_COUNT = 2 ** 26;
|
|
17
|
+
/** At most this many records per byte of coded stream: every leaf takes at
|
|
18
|
+
* least one binary decision, which costs at least log2(4096 / 4081) bits,
|
|
19
|
+
* so 2048 records per byte can't be reached by any stream. Checked before
|
|
20
|
+
* allocating for `record_count`. */
|
|
21
|
+
export const MAX_RECORDS_PER_BYTE = 2048;
|
|
22
|
+
/** Section 8.7.2's neighbour category: 0 when there is no neighbour, else
|
|
23
|
+
* how much deeper than `d` its record is (1: not deeper, 2: one level, 3:
|
|
24
|
+
* more). `tag` is the neighbour's `index << 5 | depth`, or -1. */
|
|
25
|
+
function category(tag, d) {
|
|
26
|
+
if (tag < 0)
|
|
27
|
+
return 0;
|
|
28
|
+
const e = tag & 31;
|
|
29
|
+
return e <= d ? 1 : e === d + 1 ? 2 : 3;
|
|
30
|
+
}
|
|
31
|
+
class RangeDecoder {
|
|
32
|
+
range = 0xffffffff;
|
|
33
|
+
code = 0;
|
|
34
|
+
pos;
|
|
35
|
+
buf;
|
|
36
|
+
end;
|
|
37
|
+
constructor(buf, start, end) {
|
|
38
|
+
this.buf = buf;
|
|
39
|
+
this.end = end;
|
|
40
|
+
if (end - start < 5) {
|
|
41
|
+
throw new CGeoDataFormatError("chunk range-coded stream is shorter than 5 bytes");
|
|
42
|
+
}
|
|
43
|
+
if (buf[start] !== 0) {
|
|
44
|
+
throw new CGeoDataFormatError("chunk range-coded stream does not start with a 0 byte");
|
|
45
|
+
}
|
|
46
|
+
let code = 0;
|
|
47
|
+
for (let i = 1; i <= 4; i++)
|
|
48
|
+
code = code * 256 + buf[start + i];
|
|
49
|
+
if (code === 0xffffffff) {
|
|
50
|
+
throw new CGeoDataFormatError("chunk range-coded stream starts with code 0xffffffff");
|
|
51
|
+
}
|
|
52
|
+
this.code = code;
|
|
53
|
+
this.pos = start + 5;
|
|
54
|
+
}
|
|
55
|
+
bit(probs, i) {
|
|
56
|
+
const p = probs[i];
|
|
57
|
+
const bound = (this.range >>> PROB_BITS) * p;
|
|
58
|
+
let b;
|
|
59
|
+
if (this.code < bound) {
|
|
60
|
+
this.range = bound;
|
|
61
|
+
probs[i] = p + (((1 << PROB_BITS) - p) >> ADAPT_SHIFT);
|
|
62
|
+
b = 0;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
this.code -= bound;
|
|
66
|
+
this.range -= bound;
|
|
67
|
+
probs[i] = p - (p >> ADAPT_SHIFT);
|
|
68
|
+
b = 1;
|
|
69
|
+
}
|
|
70
|
+
while (this.range < TOP) {
|
|
71
|
+
if (this.pos >= this.end) {
|
|
72
|
+
throw new CGeoDataFormatError("chunk range-coded stream runs past end of block");
|
|
73
|
+
}
|
|
74
|
+
this.range *= 256;
|
|
75
|
+
this.code = this.code * 256 + this.buf[this.pos++];
|
|
76
|
+
}
|
|
77
|
+
return b;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** The narrowest typed array for palette indices up to `paletteCount`
|
|
81
|
+
* (Section 8.3's S, with 3 bytes rounded up to 4). */
|
|
82
|
+
export function paletteIndexArray(paletteCount, n) {
|
|
83
|
+
if (paletteCount <= 255)
|
|
84
|
+
return new Uint8Array(n);
|
|
85
|
+
if (paletteCount <= 65535)
|
|
86
|
+
return new Uint16Array(n);
|
|
87
|
+
return new Uint32Array(n);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Decodes the records of a version 2 chunk from `buf[offset..]`, the
|
|
91
|
+
* range-coded stream that follows `record_count` and runs to the end of
|
|
92
|
+
* the block. Throws a `CGeoDataFormatError` for anything Section 8.7
|
|
93
|
+
* rejects.
|
|
94
|
+
*/
|
|
95
|
+
export function decodeRangeCodedRecords(buf, offset, paletteCount, recordCount, maxDepth) {
|
|
96
|
+
if (paletteCount >= MAX_PALETTE_COUNT) {
|
|
97
|
+
throw new CGeoDataFormatError(`chunk palette_count ${paletteCount} is 2^26 or more, past what a version 2 chunk allows`);
|
|
98
|
+
}
|
|
99
|
+
checkRecordCount(recordCount, maxDepth, buf.length - offset);
|
|
100
|
+
const fast = decodeRangeCodedWasm(buf, offset, paletteCount, recordCount, maxDepth);
|
|
101
|
+
if (fast)
|
|
102
|
+
return fast;
|
|
103
|
+
const rc = new RangeDecoder(buf, offset, buf.length);
|
|
104
|
+
const D = maxDepth;
|
|
105
|
+
const N = 2 ** D;
|
|
106
|
+
const split = new Uint16Array(SPLIT_CONTEXTS).fill(PROB_INIT);
|
|
107
|
+
const eqW = new Uint16Array(VALUE_CONTEXTS).fill(PROB_INIT);
|
|
108
|
+
const eqS = new Uint16Array(VALUE_CONTEXTS).fill(PROB_INIT);
|
|
109
|
+
const rankLength = new Uint16Array(RANK_LENGTH_CONTEXTS).fill(PROB_INIT);
|
|
110
|
+
const rankBits = new Uint16Array(RANK_BIT_CONTEXTS).fill(PROB_INIT);
|
|
111
|
+
// row[y] / col[x]: the last record decoded in row y / column x, as
|
|
112
|
+
// `index << 5 | depth`; in Morton order that is the cell west of /
|
|
113
|
+
// south of the next one to be decoded there.
|
|
114
|
+
const row = new Int32Array(N).fill(-1);
|
|
115
|
+
const col = new Int32Array(N).fill(-1);
|
|
116
|
+
// Move-to-front list of palette indices 0..P.
|
|
117
|
+
const mtf = new Int32Array(paletteCount + 1);
|
|
118
|
+
for (let k = 0; k <= paletteCount; k++)
|
|
119
|
+
mtf[k] = k;
|
|
120
|
+
const codes = new Uint32Array(recordCount);
|
|
121
|
+
const depths = new Uint8Array(recordCount);
|
|
122
|
+
const paletteIndices = paletteIndexArray(paletteCount, recordCount);
|
|
123
|
+
// Pending nodes, popped in pre-order: at most 3 per level plus one.
|
|
124
|
+
const stackD = new Int32Array(4 * (D + 1));
|
|
125
|
+
const stackX = new Int32Array(4 * (D + 1));
|
|
126
|
+
const stackY = new Int32Array(4 * (D + 1));
|
|
127
|
+
let sp = 1;
|
|
128
|
+
let j = 0;
|
|
129
|
+
let prev = -1;
|
|
130
|
+
let code = 0;
|
|
131
|
+
while (j < recordCount) {
|
|
132
|
+
if (sp === 0) {
|
|
133
|
+
throw new CGeoDataFormatError(`chunk record_count ${recordCount} is more than the ${j} records its quadtree has`);
|
|
134
|
+
}
|
|
135
|
+
sp--;
|
|
136
|
+
const d = stackD[sp];
|
|
137
|
+
const x = stackX[sp];
|
|
138
|
+
const y = stackY[sp];
|
|
139
|
+
const w = x > 0 ? row[y] : -1;
|
|
140
|
+
const s = y > 0 ? col[x] : -1;
|
|
141
|
+
const wc = category(w, d);
|
|
142
|
+
if (d < D && rc.bit(split, d * 16 + wc * 4 + category(s, d)) === 1) {
|
|
143
|
+
const h = 2 ** (D - d - 1);
|
|
144
|
+
const d1 = d + 1;
|
|
145
|
+
// Pushed in reverse, so the south-west child pops first.
|
|
146
|
+
stackD[sp] = d1;
|
|
147
|
+
stackX[sp] = x + h;
|
|
148
|
+
stackY[sp] = y + h;
|
|
149
|
+
sp++;
|
|
150
|
+
stackD[sp] = d1;
|
|
151
|
+
stackX[sp] = x;
|
|
152
|
+
stackY[sp] = y + h;
|
|
153
|
+
sp++;
|
|
154
|
+
stackD[sp] = d1;
|
|
155
|
+
stackX[sp] = x + h;
|
|
156
|
+
stackY[sp] = y;
|
|
157
|
+
sp++;
|
|
158
|
+
stackD[sp] = d1;
|
|
159
|
+
stackX[sp] = x;
|
|
160
|
+
stackY[sp] = y;
|
|
161
|
+
sp++;
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
const wv = w < 0 ? -1 : w >>> 5;
|
|
165
|
+
const sv = s < 0 ? -1 : s >>> 5;
|
|
166
|
+
const c = (d * 8 +
|
|
167
|
+
(wv === sv ? 4 : 0) +
|
|
168
|
+
(prev === wv ? 2 : 0) +
|
|
169
|
+
(prev === sv ? 1 : 0)) *
|
|
170
|
+
4 +
|
|
171
|
+
wc;
|
|
172
|
+
let v = -1;
|
|
173
|
+
let p = 0;
|
|
174
|
+
if (wv >= 0 && rc.bit(eqW, c) === 1)
|
|
175
|
+
v = wv;
|
|
176
|
+
else if (sv >= 0 && sv !== wv && rc.bit(eqS, c) === 1)
|
|
177
|
+
v = sv;
|
|
178
|
+
if (v < 0) {
|
|
179
|
+
const cc = (wv >= 0 ? 32 : 0) + (sv >= 0 ? 64 : 0);
|
|
180
|
+
let nb = 0;
|
|
181
|
+
while (rc.bit(rankLength, cc + nb) === 1) {
|
|
182
|
+
if (++nb === 32) {
|
|
183
|
+
throw new CGeoDataFormatError(`chunk record ${j}'s palette rank is 2^32 or more`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
let value = 1;
|
|
187
|
+
for (let k = nb - 1; k >= 0; k--) {
|
|
188
|
+
value = value * 2 + rc.bit(rankBits, nb * 32 + k);
|
|
189
|
+
}
|
|
190
|
+
let rank = value - 1;
|
|
191
|
+
for (;; p++) {
|
|
192
|
+
if (p > paletteCount) {
|
|
193
|
+
throw new CGeoDataFormatError(`chunk record ${j}'s palette rank ${value - 1} is past the palette`);
|
|
194
|
+
}
|
|
195
|
+
const t = mtf[p];
|
|
196
|
+
if (t === wv || t === sv)
|
|
197
|
+
continue;
|
|
198
|
+
if (rank === 0)
|
|
199
|
+
break;
|
|
200
|
+
rank--;
|
|
201
|
+
}
|
|
202
|
+
v = mtf[p];
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
while (mtf[p] !== v)
|
|
206
|
+
p++;
|
|
207
|
+
}
|
|
208
|
+
for (let k = p; k > 0; k--)
|
|
209
|
+
mtf[k] = mtf[k - 1];
|
|
210
|
+
mtf[0] = v;
|
|
211
|
+
const size = 2 ** (D - d);
|
|
212
|
+
const tag = v * 32 + d;
|
|
213
|
+
row.fill(tag, y, y + size);
|
|
214
|
+
col.fill(tag, x, x + size);
|
|
215
|
+
codes[j] = code;
|
|
216
|
+
depths[j] = d;
|
|
217
|
+
paletteIndices[j] = v;
|
|
218
|
+
code += size * size;
|
|
219
|
+
prev = v;
|
|
220
|
+
j++;
|
|
221
|
+
}
|
|
222
|
+
if (rc.pos !== rc.end) {
|
|
223
|
+
throw new CGeoDataFormatError(`chunk block has ${rc.end - rc.pos} trailing bytes after its range-coded stream`);
|
|
224
|
+
}
|
|
225
|
+
return { codes, depths, paletteIndices };
|
|
226
|
+
}
|
|
227
|
+
/** Section 8.7.5's bounds on `record_count`, checked before allocating
|
|
228
|
+
* for it: at most one record per cell, and at most `MAX_RECORDS_PER_BYTE`
|
|
229
|
+
* per byte of stream. */
|
|
230
|
+
export function checkRecordCount(recordCount, maxDepth, streamBytes) {
|
|
231
|
+
if (recordCount > 4 ** maxDepth) {
|
|
232
|
+
throw new CGeoDataFormatError(`chunk record_count ${recordCount} is more than the 4^${maxDepth} cells of a chunk`);
|
|
233
|
+
}
|
|
234
|
+
if (recordCount > MAX_RECORDS_PER_BYTE * Math.max(streamBytes, 0)) {
|
|
235
|
+
throw new CGeoDataFormatError(`chunk record_count ${recordCount} is more than a ${streamBytes}-byte stream can code`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
class RangeEncoder {
|
|
239
|
+
low = 0;
|
|
240
|
+
range = 0xffffffff;
|
|
241
|
+
cache = 0;
|
|
242
|
+
cacheSize = 1;
|
|
243
|
+
out = [];
|
|
244
|
+
bit(probs, i, b) {
|
|
245
|
+
const p = probs[i];
|
|
246
|
+
const bound = (this.range >>> PROB_BITS) * p;
|
|
247
|
+
if (b === 0) {
|
|
248
|
+
this.range = bound;
|
|
249
|
+
probs[i] = p + (((1 << PROB_BITS) - p) >> ADAPT_SHIFT);
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
this.low += bound;
|
|
253
|
+
this.range -= bound;
|
|
254
|
+
probs[i] = p - (p >> ADAPT_SHIFT);
|
|
255
|
+
}
|
|
256
|
+
while (this.range < TOP) {
|
|
257
|
+
this.range *= 256;
|
|
258
|
+
this.shiftLow();
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/** Emits the top byte of `low`, holding back runs of 0xff until a
|
|
262
|
+
* carry out of them is ruled out (LZMA's encoder). */
|
|
263
|
+
shiftLow() {
|
|
264
|
+
if (this.low < 0xff000000 || this.low >= 0x100000000) {
|
|
265
|
+
const carry = this.low >= 0x100000000 ? 1 : 0;
|
|
266
|
+
let c = this.cache;
|
|
267
|
+
do {
|
|
268
|
+
this.out.push((c + carry) & 0xff);
|
|
269
|
+
c = 0xff;
|
|
270
|
+
} while (--this.cacheSize !== 0);
|
|
271
|
+
this.cache = Math.floor(this.low / 0x1000000) & 0xff;
|
|
272
|
+
}
|
|
273
|
+
this.cacheSize++;
|
|
274
|
+
this.low = (this.low % 0x1000000) * 256;
|
|
275
|
+
}
|
|
276
|
+
finish() {
|
|
277
|
+
for (let i = 0; i < 5; i++)
|
|
278
|
+
this.shiftLow();
|
|
279
|
+
return Uint8Array.from(this.out);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Encodes records that tile a chunk from code 0 (Section 8.4) as Section
|
|
284
|
+
* 8.7's range-coded stream: `depths[j]` and `paletteIndices[j]` (0..P)
|
|
285
|
+
* per record, in Morton order. The inverse of `decodeRangeCodedRecords`.
|
|
286
|
+
* Throws a RangeError for records that don't tile the chunk.
|
|
287
|
+
*/
|
|
288
|
+
export function encodeRangeCodedRecords(depths, paletteIndices, paletteCount, maxDepth) {
|
|
289
|
+
if (paletteCount >= MAX_PALETTE_COUNT) {
|
|
290
|
+
throw new RangeError(`palette_count ${paletteCount} is 2^26 or more, past what a version 2 chunk allows`);
|
|
291
|
+
}
|
|
292
|
+
const rc = new RangeEncoder();
|
|
293
|
+
const D = maxDepth;
|
|
294
|
+
const N = 2 ** D;
|
|
295
|
+
const recordCount = depths.length;
|
|
296
|
+
const split = new Uint16Array(SPLIT_CONTEXTS).fill(PROB_INIT);
|
|
297
|
+
const eqW = new Uint16Array(VALUE_CONTEXTS).fill(PROB_INIT);
|
|
298
|
+
const eqS = new Uint16Array(VALUE_CONTEXTS).fill(PROB_INIT);
|
|
299
|
+
const rankLength = new Uint16Array(RANK_LENGTH_CONTEXTS).fill(PROB_INIT);
|
|
300
|
+
const rankBits = new Uint16Array(RANK_BIT_CONTEXTS).fill(PROB_INIT);
|
|
301
|
+
const row = new Int32Array(N).fill(-1);
|
|
302
|
+
const col = new Int32Array(N).fill(-1);
|
|
303
|
+
const mtf = new Int32Array(paletteCount + 1);
|
|
304
|
+
for (let k = 0; k <= paletteCount; k++)
|
|
305
|
+
mtf[k] = k;
|
|
306
|
+
const stackD = new Int32Array(4 * (D + 1));
|
|
307
|
+
const stackX = new Int32Array(4 * (D + 1));
|
|
308
|
+
const stackY = new Int32Array(4 * (D + 1));
|
|
309
|
+
let sp = 1;
|
|
310
|
+
let j = 0;
|
|
311
|
+
let prev = -1;
|
|
312
|
+
while (j < recordCount) {
|
|
313
|
+
if (sp === 0)
|
|
314
|
+
throw new RangeError("records run past the end of the chunk");
|
|
315
|
+
sp--;
|
|
316
|
+
const d = stackD[sp];
|
|
317
|
+
const x = stackX[sp];
|
|
318
|
+
const y = stackY[sp];
|
|
319
|
+
const depth = depths[j];
|
|
320
|
+
if (depth < d || depth > D) {
|
|
321
|
+
throw new RangeError(`record ${j} at depth ${depth} doesn't tile the chunk`);
|
|
322
|
+
}
|
|
323
|
+
const w = x > 0 ? row[y] : -1;
|
|
324
|
+
const s = y > 0 ? col[x] : -1;
|
|
325
|
+
const wc = category(w, d);
|
|
326
|
+
if (d < D) {
|
|
327
|
+
const splits = depth > d ? 1 : 0;
|
|
328
|
+
rc.bit(split, d * 16 + wc * 4 + category(s, d), splits);
|
|
329
|
+
if (splits) {
|
|
330
|
+
const h = 2 ** (D - d - 1);
|
|
331
|
+
const d1 = d + 1;
|
|
332
|
+
stackD[sp] = d1;
|
|
333
|
+
stackX[sp] = x + h;
|
|
334
|
+
stackY[sp] = y + h;
|
|
335
|
+
sp++;
|
|
336
|
+
stackD[sp] = d1;
|
|
337
|
+
stackX[sp] = x;
|
|
338
|
+
stackY[sp] = y + h;
|
|
339
|
+
sp++;
|
|
340
|
+
stackD[sp] = d1;
|
|
341
|
+
stackX[sp] = x + h;
|
|
342
|
+
stackY[sp] = y;
|
|
343
|
+
sp++;
|
|
344
|
+
stackD[sp] = d1;
|
|
345
|
+
stackX[sp] = x;
|
|
346
|
+
stackY[sp] = y;
|
|
347
|
+
sp++;
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
const v = paletteIndices[j];
|
|
352
|
+
if (!(v >= 0 && v <= paletteCount)) {
|
|
353
|
+
throw new RangeError(`record ${j}'s palette index ${v} is past P (${paletteCount})`);
|
|
354
|
+
}
|
|
355
|
+
const wv = w < 0 ? -1 : w >>> 5;
|
|
356
|
+
const sv = s < 0 ? -1 : s >>> 5;
|
|
357
|
+
const c = (d * 8 +
|
|
358
|
+
(wv === sv ? 4 : 0) +
|
|
359
|
+
(prev === wv ? 2 : 0) +
|
|
360
|
+
(prev === sv ? 1 : 0)) *
|
|
361
|
+
4 +
|
|
362
|
+
wc;
|
|
363
|
+
let coded = false;
|
|
364
|
+
if (wv >= 0) {
|
|
365
|
+
rc.bit(eqW, c, v === wv ? 1 : 0);
|
|
366
|
+
coded = v === wv;
|
|
367
|
+
}
|
|
368
|
+
if (!coded && sv >= 0 && sv !== wv) {
|
|
369
|
+
rc.bit(eqS, c, v === sv ? 1 : 0);
|
|
370
|
+
coded = v === sv;
|
|
371
|
+
}
|
|
372
|
+
let p = 0;
|
|
373
|
+
while (mtf[p] !== v)
|
|
374
|
+
p++;
|
|
375
|
+
if (!coded) {
|
|
376
|
+
let rank = 0;
|
|
377
|
+
for (let k = 0; k < p; k++)
|
|
378
|
+
if (mtf[k] !== wv && mtf[k] !== sv)
|
|
379
|
+
rank++;
|
|
380
|
+
const value = rank + 1;
|
|
381
|
+
const nb = 31 - Math.clz32(value);
|
|
382
|
+
const cc = (wv >= 0 ? 32 : 0) + (sv >= 0 ? 64 : 0);
|
|
383
|
+
for (let k = 0; k < nb; k++)
|
|
384
|
+
rc.bit(rankLength, cc + k, 1);
|
|
385
|
+
rc.bit(rankLength, cc + nb, 0);
|
|
386
|
+
for (let k = nb - 1; k >= 0; k--)
|
|
387
|
+
rc.bit(rankBits, nb * 32 + k, (value >>> k) & 1);
|
|
388
|
+
}
|
|
389
|
+
for (let k = p; k > 0; k--)
|
|
390
|
+
mtf[k] = mtf[k - 1];
|
|
391
|
+
mtf[0] = v;
|
|
392
|
+
const size = 2 ** (D - d);
|
|
393
|
+
const tag = v * 32 + d;
|
|
394
|
+
row.fill(tag, y, y + size);
|
|
395
|
+
col.fill(tag, x, x + size);
|
|
396
|
+
prev = v;
|
|
397
|
+
j++;
|
|
398
|
+
}
|
|
399
|
+
return rc.finish();
|
|
400
|
+
}
|
|
401
|
+
//# sourceMappingURL=rangeChunk.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { RangeCodedRecords } from "./rangeChunk.ts";
|
|
2
|
+
/** Turns the fast path off (or back on), for tests and benchmarks that
|
|
3
|
+
* compare it against the JavaScript decoder. */
|
|
4
|
+
export declare function useWasmRangeDecoder(on: boolean): void;
|
|
5
|
+
/** How many bytes of WebAssembly memory the fast path holds right now:
|
|
6
|
+
* 0 while idle. */
|
|
7
|
+
export declare function wasmRangeDecoderBytes(): number;
|
|
8
|
+
/** Whether this platform runs the fast path. */
|
|
9
|
+
export declare function wasmRangeDecoderAvailable(): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* The records of the range-coded stream `buf[offset..]` (Section 8.7), or
|
|
12
|
+
* undefined when the fast path declined it or isn't available, and the
|
|
13
|
+
* caller should decode it in JavaScript. The caller has checked Section
|
|
14
|
+
* 8.7.5's bounds on `recordCount`, that `maxDepth` is at most 16 and
|
|
15
|
+
* `paletteCount` below 2^26.
|
|
16
|
+
*/
|
|
17
|
+
export declare function decodeRangeCodedWasm(buf: Uint8Array, offset: number, paletteCount: number, recordCount: number, maxDepth: number): RangeCodedRecords | undefined;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { RANGE_DECODER_WASM } from "./rangeDecoderWasm.js";
|
|
2
|
+
/**
|
|
3
|
+
* `decodeRangeCodedRecords`'s fast path (wasm/rangeDecoder.wat), about
|
|
4
|
+
* twice as fast as the JavaScript decoder in ./rangeChunk.ts. It only
|
|
5
|
+
* accepts or declines a stream: a declined one -- malformed, or valid
|
|
6
|
+
* but out of its reach -- is decoded again in JavaScript, which throws
|
|
7
|
+
* the error or returns the records. Platforms without WebAssembly (or
|
|
8
|
+
* that forbid compiling it, such as a page whose CSP lacks
|
|
9
|
+
* `'wasm-unsafe-eval'`) always take the JavaScript path.
|
|
10
|
+
*
|
|
11
|
+
* Memory: one instance, grown to the largest chunk decoded so far and
|
|
12
|
+
* dropped once no decode has used it for `IDLE_RELEASE_MS`, as
|
|
13
|
+
* ./chunkDecoder.ts does for version 0 chunks.
|
|
14
|
+
*/
|
|
15
|
+
/** How long the instance outlives its last decode. */
|
|
16
|
+
const IDLE_RELEASE_MS = 1000;
|
|
17
|
+
const PAGE_BYTES = 65536;
|
|
18
|
+
/** Probabilities and node stack (wasm/rangeDecoder.wat's `scratch`). */
|
|
19
|
+
const FIXED_SCRATCH_BYTES = 4992 + 816;
|
|
20
|
+
/** The compiled module; null once compiling failed. */
|
|
21
|
+
let compiled;
|
|
22
|
+
let instance;
|
|
23
|
+
let releaseTimer;
|
|
24
|
+
let enabled = true;
|
|
25
|
+
/** Turns the fast path off (or back on), for tests and benchmarks that
|
|
26
|
+
* compare it against the JavaScript decoder. */
|
|
27
|
+
export function useWasmRangeDecoder(on) {
|
|
28
|
+
enabled = on;
|
|
29
|
+
if (!on)
|
|
30
|
+
release();
|
|
31
|
+
}
|
|
32
|
+
/** How many bytes of WebAssembly memory the fast path holds right now:
|
|
33
|
+
* 0 while idle. */
|
|
34
|
+
export function wasmRangeDecoderBytes() {
|
|
35
|
+
return instance?.memory.buffer.byteLength ?? 0;
|
|
36
|
+
}
|
|
37
|
+
/** Whether this platform runs the fast path. */
|
|
38
|
+
export function wasmRangeDecoderAvailable() {
|
|
39
|
+
return enabled && getInstance() !== undefined;
|
|
40
|
+
}
|
|
41
|
+
function getInstance() {
|
|
42
|
+
if (instance)
|
|
43
|
+
return instance;
|
|
44
|
+
if (compiled === undefined) {
|
|
45
|
+
try {
|
|
46
|
+
// Throws a ReferenceError where there's no WebAssembly at all.
|
|
47
|
+
compiled = new WebAssembly.Module(Uint8Array.from(atob(RANGE_DECODER_WASM), (c) => c.charCodeAt(0)));
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
compiled = null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (compiled === null)
|
|
54
|
+
return undefined;
|
|
55
|
+
const exports = new WebAssembly.Instance(compiled).exports;
|
|
56
|
+
instance = {
|
|
57
|
+
memory: exports.memory,
|
|
58
|
+
decode: exports.decode,
|
|
59
|
+
};
|
|
60
|
+
return instance;
|
|
61
|
+
}
|
|
62
|
+
function release() {
|
|
63
|
+
instance = undefined;
|
|
64
|
+
if (releaseTimer !== undefined)
|
|
65
|
+
clearTimeout(releaseTimer);
|
|
66
|
+
releaseTimer = undefined;
|
|
67
|
+
}
|
|
68
|
+
function releaseWhenIdle() {
|
|
69
|
+
if (releaseTimer !== undefined)
|
|
70
|
+
clearTimeout(releaseTimer);
|
|
71
|
+
releaseTimer = setTimeout(release, IDLE_RELEASE_MS);
|
|
72
|
+
// Node.js: don't keep the process alive just to free this.
|
|
73
|
+
releaseTimer.unref?.();
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The records of the range-coded stream `buf[offset..]` (Section 8.7), or
|
|
77
|
+
* undefined when the fast path declined it or isn't available, and the
|
|
78
|
+
* caller should decode it in JavaScript. The caller has checked Section
|
|
79
|
+
* 8.7.5's bounds on `recordCount`, that `maxDepth` is at most 16 and
|
|
80
|
+
* `paletteCount` below 2^26.
|
|
81
|
+
*/
|
|
82
|
+
export function decodeRangeCodedWasm(buf, offset, paletteCount, recordCount, maxDepth) {
|
|
83
|
+
if (!enabled)
|
|
84
|
+
return undefined;
|
|
85
|
+
const decoder = getInstance();
|
|
86
|
+
if (!decoder)
|
|
87
|
+
return undefined;
|
|
88
|
+
const width = paletteCount <= 255 ? 1 : paletteCount <= 65535 ? 2 : 4;
|
|
89
|
+
const streamBytes = buf.length - offset;
|
|
90
|
+
// Slack past the stream: a stream that runs past its end reads a few
|
|
91
|
+
// bytes there before the fast path declines it.
|
|
92
|
+
const out = Math.ceil((streamBytes + 64) / 16) * 16;
|
|
93
|
+
const indices = out + 4 * recordCount;
|
|
94
|
+
const depthsAt = indices + width * recordCount;
|
|
95
|
+
const scratch = Math.ceil((depthsAt + recordCount) / 16) * 16;
|
|
96
|
+
const needed = scratch + FIXED_SCRATCH_BYTES + 4 * (2 * 2 ** maxDepth + paletteCount + 1);
|
|
97
|
+
const { memory } = decoder;
|
|
98
|
+
if (memory.buffer.byteLength < needed) {
|
|
99
|
+
try {
|
|
100
|
+
memory.grow(Math.ceil((needed - memory.buffer.byteLength) / PAGE_BYTES));
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return undefined; // out of memory: leave it to the JavaScript path
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
new Uint8Array(memory.buffer, 0, streamBytes).set(buf.subarray(offset));
|
|
107
|
+
let ok;
|
|
108
|
+
try {
|
|
109
|
+
ok = decoder.decode(streamBytes, recordCount, paletteCount, maxDepth, width, out, scratch);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// A stream that runs far past its end can read past the memory.
|
|
113
|
+
ok = 0;
|
|
114
|
+
}
|
|
115
|
+
releaseWhenIdle();
|
|
116
|
+
if (!ok)
|
|
117
|
+
return undefined;
|
|
118
|
+
const { buffer } = memory;
|
|
119
|
+
return {
|
|
120
|
+
codes: new Uint32Array(buffer, out, recordCount).slice(),
|
|
121
|
+
depths: new Uint8Array(buffer, depthsAt, recordCount).slice(),
|
|
122
|
+
paletteIndices: width === 1
|
|
123
|
+
? new Uint8Array(buffer, indices, recordCount).slice()
|
|
124
|
+
: width === 2
|
|
125
|
+
? new Uint16Array(buffer, indices, recordCount).slice()
|
|
126
|
+
: new Uint32Array(buffer, indices, recordCount).slice(),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=rangeDecoder.js.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/** rangeDecoder.wat's compiled module (1381 bytes), base64. */
|
|
2
|
+
export declare const RANGE_DECODER_WASM = "AGFzbQEAAAABFwNgAX8Bf2ACf38Bf2AHf39/f39/fwF/AwQDAAECBQMBAAEGFQR/AUEAC38BQQALfwFBAAt/AUEACwcTAgZtZW1vcnkCAAZkZWNvZGUAAgqKCgOBAQEDfyAALwEAIQEjAEEMdiABbCECIwEgAkkEQCACJAAgACABQYAgIAFrQQR2ajsBAAUjASACayQBIwAgAmskACAAIAEgAUEEdms7AQBBASEDCwJAA0AjAEGAgIAITw0BIwFBCHQjAi0AAHIkASMCQQFqJAIjAEEIdCQADAALCyADCzEBAX8gAEEASARAQQAPCyAAQR9xIQIgAiABTQRAQQEPCyACIAFBAWpGBEBBAg8LQQML0ggBJH9BAC0AAARAQQAPC0EBLQAAQRh0QQItAABBEHRyQQMtAABBCHRBBC0AAHJyJAFBfyQAQQUkAiAAJAMgBiEHIAZBgARqIQggBkHADGohCSAGQYAVaiEKIAZBgBdqIQsgBkGAJ2ohDCAMQbAGaiENIA1BBCADdGohDiAOQQQgA3RqIQ8gBSABQQJ0aiERIBEgASAEbGohEEEAIRIDQCAGIBJqQYAQOwEAIBJBAmohEiASQYAnSQ0ACyANQf8BQQggA3T8CwBBACESA0AgDyASQQJ0aiASNgIAIBJBAWohEiASIAJNDQALIAxCADcDACAMQQA2AghBASETQX8hFQJAA0AgFCABTw0BIBNFBEBBAA8LIBNBAWshEyAMIBNBDGxqIRsgGygCACEXIBsoAgQhGCAbKAIIIRkgGAR/IA0gGUECdGooAgAFQX8LIRwgGQR/IA4gGEECdGooAgAFQX8LIR0gHCAXEAEhHiAXIANJBEAgByAXQQR0IB5BAnRqIB0gFxABakEBdGoQAARAQQEgAyAXa0EBa3QhGiAXQQFqIRcgGyAXNgIAIBsgGCAaajYCBCAbIBkgGmo2AgggGyAXNgIMIBsgGDYCECAbIBkgGmo2AhQgGyAXNgIYIBsgGCAaajYCHCAbIBk2AiAgGyAXNgIkIBsgGDYCKCAbIBk2AiwgE0EEaiETDAILC0F/IBxBBXYgHEEASBshH0F/IB1BBXYgHUEASBshICAXQQN0IB8gIEZBAnRqIBUgH0ZBAXRqIBUgIEZqQQJ0IB5qISFBfyEiIB9BAE4EQCAIICFBAXRqEAAEQCAfISILCyAiQQBIICBBAE4gICAfR3FxBEAgCSAhQQF0ahAABEAgICEiCwtBACEjICJBAEgEQEEgQQAgH0EAThtBwABBACAgQQBOG2ohJEEAISUCQANAIAogJCAlakEBdGoQAEUNASAlQQFqISUgJUEgRgRAQQAPCwwACwtBASEmICUhEgJAA0AgEkUNASASQQFrIRIgJkEBdCALICVBBXQgEmpBAXRqEABqISYMAAsLICZBAWshJwJAA0AgIyACSwRAQQAPCyAPICNBAnRqKAIAISggKCAfRyAoICBHcQRAICdFDQIgJ0EBayEnCyAjQQFqISMMAAsLICghIgUDQCAPICNBAnRqKAIAICJHBEAgI0EBaiEjDAELCwsgIwRAIA9BBGogDyAjQQJ0/AoAACAPICI2AgALQQEgAyAXa3QhKSAiQQV0IBdyISpBACESA0AgDSAZIBJqQQJ0aiAqNgIAIA4gGCASakECdGogKjYCACASQQFqIRIgEiApSQ0ACyAFIBRBAnRqIBY2AgAgECAUaiAXOgAAIARBAUYEQCARIBRqICI6AAAFIARBAkYEQCARIBRBAXRqICI7AQAFIBEgFEECdGogIjYCAAsLIBYgKSApbGohFiAiIRUgFEEBaiEUDAALCyMCIwNGCw==";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// Generated by `npm run build:wasm -w reader` from wasm/rangeDecoder.wat; do not edit.
|
|
2
|
+
/** rangeDecoder.wat's compiled module (1381 bytes), base64. */
|
|
3
|
+
export const RANGE_DECODER_WASM = "AGFzbQEAAAABFwNgAX8Bf2ACf38Bf2AHf39/f39/fwF/AwQDAAECBQMBAAEGFQR/AUEAC38BQQALfwFBAAt/AUEACwcTAgZtZW1vcnkCAAZkZWNvZGUAAgqKCgOBAQEDfyAALwEAIQEjAEEMdiABbCECIwEgAkkEQCACJAAgACABQYAgIAFrQQR2ajsBAAUjASACayQBIwAgAmskACAAIAEgAUEEdms7AQBBASEDCwJAA0AjAEGAgIAITw0BIwFBCHQjAi0AAHIkASMCQQFqJAIjAEEIdCQADAALCyADCzEBAX8gAEEASARAQQAPCyAAQR9xIQIgAiABTQRAQQEPCyACIAFBAWpGBEBBAg8LQQML0ggBJH9BAC0AAARAQQAPC0EBLQAAQRh0QQItAABBEHRyQQMtAABBCHRBBC0AAHJyJAFBfyQAQQUkAiAAJAMgBiEHIAZBgARqIQggBkHADGohCSAGQYAVaiEKIAZBgBdqIQsgBkGAJ2ohDCAMQbAGaiENIA1BBCADdGohDiAOQQQgA3RqIQ8gBSABQQJ0aiERIBEgASAEbGohEEEAIRIDQCAGIBJqQYAQOwEAIBJBAmohEiASQYAnSQ0ACyANQf8BQQggA3T8CwBBACESA0AgDyASQQJ0aiASNgIAIBJBAWohEiASIAJNDQALIAxCADcDACAMQQA2AghBASETQX8hFQJAA0AgFCABTw0BIBNFBEBBAA8LIBNBAWshEyAMIBNBDGxqIRsgGygCACEXIBsoAgQhGCAbKAIIIRkgGAR/IA0gGUECdGooAgAFQX8LIRwgGQR/IA4gGEECdGooAgAFQX8LIR0gHCAXEAEhHiAXIANJBEAgByAXQQR0IB5BAnRqIB0gFxABakEBdGoQAARAQQEgAyAXa0EBa3QhGiAXQQFqIRcgGyAXNgIAIBsgGCAaajYCBCAbIBkgGmo2AgggGyAXNgIMIBsgGDYCECAbIBkgGmo2AhQgGyAXNgIYIBsgGCAaajYCHCAbIBk2AiAgGyAXNgIkIBsgGDYCKCAbIBk2AiwgE0EEaiETDAILC0F/IBxBBXYgHEEASBshH0F/IB1BBXYgHUEASBshICAXQQN0IB8gIEZBAnRqIBUgH0ZBAXRqIBUgIEZqQQJ0IB5qISFBfyEiIB9BAE4EQCAIICFBAXRqEAAEQCAfISILCyAiQQBIICBBAE4gICAfR3FxBEAgCSAhQQF0ahAABEAgICEiCwtBACEjICJBAEgEQEEgQQAgH0EAThtBwABBACAgQQBOG2ohJEEAISUCQANAIAogJCAlakEBdGoQAEUNASAlQQFqISUgJUEgRgRAQQAPCwwACwtBASEmICUhEgJAA0AgEkUNASASQQFrIRIgJkEBdCALICVBBXQgEmpBAXRqEABqISYMAAsLICZBAWshJwJAA0AgIyACSwRAQQAPCyAPICNBAnRqKAIAISggKCAfRyAoICBHcQRAICdFDQIgJ0EBayEnCyAjQQFqISMMAAsLICghIgUDQCAPICNBAnRqKAIAICJHBEAgI0EBaiEjDAELCwsgIwRAIA9BBGogDyAjQQJ0/AoAACAPICI2AgALQQEgAyAXa3QhKSAiQQV0IBdyISpBACESA0AgDSAZIBJqQQJ0aiAqNgIAIA4gGCASakECdGogKjYCACASQQFqIRIgEiApSQ0ACyAFIBRBAnRqIBY2AgAgECAUaiAXOgAAIARBAUYEQCARIBRqICI6AAAFIARBAkYEQCARIBRBAXRqICI7AQAFIBEgFEECdGogIjYCAAsLIBYgKSApbGohFiAiIRUgFEEBaiEUDAALCyMCIwNGCw==";
|
|
4
|
+
//# sourceMappingURL=rangeDecoderWasm.js.map
|
package/dist/reader.js
CHANGED
|
@@ -929,7 +929,11 @@ export class CGeoDataReader {
|
|
|
929
929
|
}
|
|
930
930
|
async decodeChunk(index) {
|
|
931
931
|
const compressed = await this.source.read(this.directory.chunkOffsets[index], this.directory.chunkLengths[index]);
|
|
932
|
-
return this.decodes.run(async () => this.checkPalette(index, parseChunk(
|
|
932
|
+
return this.decodes.run(async () => this.checkPalette(index, parseChunk(
|
|
933
|
+
// Version 2 chunks are range coded, not zlib (Section 8.7).
|
|
934
|
+
this.header.version >= 2
|
|
935
|
+
? compressed
|
|
936
|
+
: await inflateBlock(this.inflater, compressed), this.header.maxDepth, this.header.version)));
|
|
933
937
|
}
|
|
934
938
|
checkPalette(index, chunk) {
|
|
935
939
|
// Section 11: "every palette entry [MUST be] less than N".
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ferrybig/cgeodata-reader",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Reverse geocoding without a database: resolves a latitude/longitude to its country, region and city from OpenStreetMap boundaries, reading a compact CGEODATA file. No dependencies; Node.js and browsers.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"reverse-geocoding",
|